diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2009-07-13 10:54:26 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-07-14 17:39:46 -0400 |
commit | 7702ce40bc84a02e88aa20f95333df8cff5f9d37 (patch) | |
tree | 72e9e0fb07175486cf2b6996cf8d94b1d0d7c466 /net/sunrpc/svcsock.c | |
parent | 9208faf297dddfa97a86d7224b6bf94f2e346dd9 (diff) |
SUNRPC: handle IPv6 PKTINFO when extracting destination address
PKTINFO is needed to scrape the caller's IP address off the socket so
RPC datagram replies are routed correctly. Fill in missing pieces in
the kernel RPC server's UDP receive path to request IPv6 PKTINFO and
correctly parse the IPv6 cmsg header.
Without this patch, kernel RPC services drop all incoming requests on
UDP on IPv6.
Related commit: 7a37f5787e76bf1765c1add3a9a7163f841a28bb
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Neil Brown <neilb@suse.de>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'net/sunrpc/svcsock.c')
-rw-r--r-- | net/sunrpc/svcsock.c | 84 |
1 files changed, 56 insertions, 28 deletions
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 23128ee191ae..99a826dcc32e 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -432,29 +432,49 @@ static void svc_tcp_write_space(struct sock *sk) | |||
432 | } | 432 | } |
433 | 433 | ||
434 | /* | 434 | /* |
435 | * See net/ipv6/ip_sockglue.c : ip_cmsg_recv_pktinfo | ||
436 | */ | ||
437 | static int svc_udp_get_dest_address4(struct svc_rqst *rqstp, | ||
438 | struct cmsghdr *cmh) | ||
439 | { | ||
440 | struct in_pktinfo *pki = CMSG_DATA(cmh); | ||
441 | if (cmh->cmsg_type != IP_PKTINFO) | ||
442 | return 0; | ||
443 | rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr; | ||
444 | return 1; | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * See net/ipv6/datagram.c : datagram_recv_ctl | ||
449 | */ | ||
450 | static int svc_udp_get_dest_address6(struct svc_rqst *rqstp, | ||
451 | struct cmsghdr *cmh) | ||
452 | { | ||
453 | struct in6_pktinfo *pki = CMSG_DATA(cmh); | ||
454 | if (cmh->cmsg_type != IPV6_PKTINFO) | ||
455 | return 0; | ||
456 | ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr); | ||
457 | return 1; | ||
458 | } | ||
459 | |||
460 | /* | ||
435 | * Copy the UDP datagram's destination address to the rqstp structure. | 461 | * Copy the UDP datagram's destination address to the rqstp structure. |
436 | * The 'destination' address in this case is the address to which the | 462 | * The 'destination' address in this case is the address to which the |
437 | * peer sent the datagram, i.e. our local address. For multihomed | 463 | * peer sent the datagram, i.e. our local address. For multihomed |
438 | * hosts, this can change from msg to msg. Note that only the IP | 464 | * hosts, this can change from msg to msg. Note that only the IP |
439 | * address changes, the port number should remain the same. | 465 | * address changes, the port number should remain the same. |
440 | */ | 466 | */ |
441 | static void svc_udp_get_dest_address(struct svc_rqst *rqstp, | 467 | static int svc_udp_get_dest_address(struct svc_rqst *rqstp, |
442 | struct cmsghdr *cmh) | 468 | struct cmsghdr *cmh) |
443 | { | 469 | { |
444 | struct svc_sock *svsk = | 470 | switch (cmh->cmsg_level) { |
445 | container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); | 471 | case SOL_IP: |
446 | switch (svsk->sk_sk->sk_family) { | 472 | return svc_udp_get_dest_address4(rqstp, cmh); |
447 | case AF_INET: { | 473 | case SOL_IPV6: |
448 | struct in_pktinfo *pki = CMSG_DATA(cmh); | 474 | return svc_udp_get_dest_address6(rqstp, cmh); |
449 | rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr; | ||
450 | break; | ||
451 | } | ||
452 | case AF_INET6: { | ||
453 | struct in6_pktinfo *pki = CMSG_DATA(cmh); | ||
454 | ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr); | ||
455 | break; | ||
456 | } | ||
457 | } | 475 | } |
476 | |||
477 | return 0; | ||
458 | } | 478 | } |
459 | 479 | ||
460 | /* | 480 | /* |
@@ -531,16 +551,15 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
531 | 551 | ||
532 | rqstp->rq_prot = IPPROTO_UDP; | 552 | rqstp->rq_prot = IPPROTO_UDP; |
533 | 553 | ||
534 | if (cmh->cmsg_level != IPPROTO_IP || | 554 | if (!svc_udp_get_dest_address(rqstp, cmh)) { |
535 | cmh->cmsg_type != IP_PKTINFO) { | ||
536 | if (net_ratelimit()) | 555 | if (net_ratelimit()) |
537 | printk("rpcsvc: received unknown control message:" | 556 | printk(KERN_WARNING |
538 | "%d/%d\n", | 557 | "svc: received unknown control message %d/%d; " |
539 | cmh->cmsg_level, cmh->cmsg_type); | 558 | "dropping RPC reply datagram\n", |
559 | cmh->cmsg_level, cmh->cmsg_type); | ||
540 | skb_free_datagram(svsk->sk_sk, skb); | 560 | skb_free_datagram(svsk->sk_sk, skb); |
541 | return 0; | 561 | return 0; |
542 | } | 562 | } |
543 | svc_udp_get_dest_address(rqstp, cmh); | ||
544 | 563 | ||
545 | if (skb_is_nonlinear(skb)) { | 564 | if (skb_is_nonlinear(skb)) { |
546 | /* we have to copy */ | 565 | /* we have to copy */ |
@@ -651,8 +670,7 @@ static struct svc_xprt_class svc_udp_class = { | |||
651 | 670 | ||
652 | static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) | 671 | static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) |
653 | { | 672 | { |
654 | int one = 1; | 673 | int err, level, optname, one = 1; |
655 | mm_segment_t oldfs; | ||
656 | 674 | ||
657 | svc_xprt_init(&svc_udp_class, &svsk->sk_xprt, serv); | 675 | svc_xprt_init(&svc_udp_class, &svsk->sk_xprt, serv); |
658 | clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); | 676 | clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); |
@@ -671,12 +689,22 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) | |||
671 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | 689 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); |
672 | set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags); | 690 | set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags); |
673 | 691 | ||
674 | oldfs = get_fs(); | ||
675 | set_fs(KERNEL_DS); | ||
676 | /* make sure we get destination address info */ | 692 | /* make sure we get destination address info */ |
677 | svsk->sk_sock->ops->setsockopt(svsk->sk_sock, IPPROTO_IP, IP_PKTINFO, | 693 | switch (svsk->sk_sk->sk_family) { |
678 | (char __user *)&one, sizeof(one)); | 694 | case AF_INET: |
679 | set_fs(oldfs); | 695 | level = SOL_IP; |
696 | optname = IP_PKTINFO; | ||
697 | break; | ||
698 | case AF_INET6: | ||
699 | level = SOL_IPV6; | ||
700 | optname = IPV6_RECVPKTINFO; | ||
701 | break; | ||
702 | default: | ||
703 | BUG(); | ||
704 | } | ||
705 | err = kernel_setsockopt(svsk->sk_sock, level, optname, | ||
706 | (char *)&one, sizeof(one)); | ||
707 | dprintk("svc: kernel_setsockopt returned %d\n", err); | ||
680 | } | 708 | } |
681 | 709 | ||
682 | /* | 710 | /* |