diff options
author | NeilBrown <neilb@suse.de> | 2007-03-06 04:42:21 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-06 12:30:26 -0500 |
commit | 7a37f5787e76bf1765c1add3a9a7163f841a28bb (patch) | |
tree | f67e69f082bf6a6083e3b0cff089d1bba1d1da0c | |
parent | 0e8cd28a084691587549630dce728661401d343b (diff) |
[PATCH] knfsd: use recv_msg to get peer address for NFSD instead of code-copying
The sunrpc server code needs to know the source and destination address for
UDP packets so it can reply properly. It currently copies code out of the
network stack to pick the pieces out of the skb. This is ugly and causes
compile problems with the IPv6 stuff.
So, rip that out and use recv_msg instead. This is a much cleaner interface,
but has a slight cost in that the checksum is now checked before the copy, so
we don't benefit from doing both at the same time. This can probably be
fixed.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | net/sunrpc/svcsock.c | 72 |
1 files changed, 40 insertions, 32 deletions
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 63ae94771b8e..32b94cf19f89 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -721,45 +721,23 @@ svc_write_space(struct sock *sk) | |||
721 | } | 721 | } |
722 | } | 722 | } |
723 | 723 | ||
724 | static void svc_udp_get_sender_address(struct svc_rqst *rqstp, | 724 | static inline void svc_udp_get_dest_address(struct svc_rqst *rqstp, |
725 | struct sk_buff *skb) | 725 | struct cmsghdr *cmh) |
726 | { | 726 | { |
727 | switch (rqstp->rq_sock->sk_sk->sk_family) { | 727 | switch (rqstp->rq_sock->sk_sk->sk_family) { |
728 | case AF_INET: { | 728 | case AF_INET: { |
729 | /* this seems to come from net/ipv4/udp.c:udp_recvmsg */ | 729 | struct in_pktinfo *pki = CMSG_DATA(cmh); |
730 | struct sockaddr_in *sin = svc_addr_in(rqstp); | 730 | rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr; |
731 | |||
732 | sin->sin_family = AF_INET; | ||
733 | sin->sin_port = skb->h.uh->source; | ||
734 | sin->sin_addr.s_addr = skb->nh.iph->saddr; | ||
735 | rqstp->rq_addrlen = sizeof(struct sockaddr_in); | ||
736 | /* Remember which interface received this request */ | ||
737 | rqstp->rq_daddr.addr.s_addr = skb->nh.iph->daddr; | ||
738 | } | ||
739 | break; | 731 | break; |
732 | } | ||
740 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 733 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
741 | case AF_INET6: { | 734 | case AF_INET6: { |
742 | /* this is derived from net/ipv6/udp.c:udpv6_recvmesg */ | 735 | struct in6_pktinfo *pki = CMSG_DATA(cmh); |
743 | struct sockaddr_in6 *sin6 = svc_addr_in6(rqstp); | 736 | ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr); |
744 | |||
745 | sin6->sin6_family = AF_INET6; | ||
746 | sin6->sin6_port = skb->h.uh->source; | ||
747 | sin6->sin6_flowinfo = 0; | ||
748 | sin6->sin6_scope_id = 0; | ||
749 | if (ipv6_addr_type(&sin6->sin6_addr) & | ||
750 | IPV6_ADDR_LINKLOCAL) | ||
751 | sin6->sin6_scope_id = IP6CB(skb)->iif; | ||
752 | ipv6_addr_copy(&sin6->sin6_addr, | ||
753 | &skb->nh.ipv6h->saddr); | ||
754 | rqstp->rq_addrlen = sizeof(struct sockaddr_in); | ||
755 | /* Remember which interface received this request */ | ||
756 | ipv6_addr_copy(&rqstp->rq_daddr.addr6, | ||
757 | &skb->nh.ipv6h->saddr); | ||
758 | } | ||
759 | break; | 737 | break; |
738 | } | ||
760 | #endif | 739 | #endif |
761 | } | 740 | } |
762 | return; | ||
763 | } | 741 | } |
764 | 742 | ||
765 | /* | 743 | /* |
@@ -771,7 +749,15 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
771 | struct svc_sock *svsk = rqstp->rq_sock; | 749 | struct svc_sock *svsk = rqstp->rq_sock; |
772 | struct svc_serv *serv = svsk->sk_server; | 750 | struct svc_serv *serv = svsk->sk_server; |
773 | struct sk_buff *skb; | 751 | struct sk_buff *skb; |
752 | char buffer[CMSG_SPACE(sizeof(union svc_pktinfo_u))]; | ||
753 | struct cmsghdr *cmh = (struct cmsghdr *)buffer; | ||
774 | int err, len; | 754 | int err, len; |
755 | struct msghdr msg = { | ||
756 | .msg_name = svc_addr(rqstp), | ||
757 | .msg_control = cmh, | ||
758 | .msg_controllen = sizeof(buffer), | ||
759 | .msg_flags = MSG_DONTWAIT, | ||
760 | }; | ||
775 | 761 | ||
776 | if (test_and_clear_bit(SK_CHNGBUF, &svsk->sk_flags)) | 762 | if (test_and_clear_bit(SK_CHNGBUF, &svsk->sk_flags)) |
777 | /* udp sockets need large rcvbuf as all pending | 763 | /* udp sockets need large rcvbuf as all pending |
@@ -797,7 +783,9 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
797 | } | 783 | } |
798 | 784 | ||
799 | clear_bit(SK_DATA, &svsk->sk_flags); | 785 | clear_bit(SK_DATA, &svsk->sk_flags); |
800 | while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { | 786 | while ((err == kernel_recvmsg(svsk->sk_sock, &msg, NULL, |
787 | 0, 0, MSG_PEEK | MSG_DONTWAIT)) < 0 || | ||
788 | (skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { | ||
801 | if (err == -EAGAIN) { | 789 | if (err == -EAGAIN) { |
802 | svc_sock_received(svsk); | 790 | svc_sock_received(svsk); |
803 | return err; | 791 | return err; |
@@ -805,6 +793,7 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
805 | /* possibly an icmp error */ | 793 | /* possibly an icmp error */ |
806 | dprintk("svc: recvfrom returned error %d\n", -err); | 794 | dprintk("svc: recvfrom returned error %d\n", -err); |
807 | } | 795 | } |
796 | rqstp->rq_addrlen = sizeof(rqstp->rq_addr); | ||
808 | if (skb->tstamp.off_sec == 0) { | 797 | if (skb->tstamp.off_sec == 0) { |
809 | struct timeval tv; | 798 | struct timeval tv; |
810 | 799 | ||
@@ -827,7 +816,16 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
827 | 816 | ||
828 | rqstp->rq_prot = IPPROTO_UDP; | 817 | rqstp->rq_prot = IPPROTO_UDP; |
829 | 818 | ||
830 | svc_udp_get_sender_address(rqstp, skb); | 819 | if (cmh->cmsg_level != IPPROTO_IP || |
820 | cmh->cmsg_type != IP_PKTINFO) { | ||
821 | if (net_ratelimit()) | ||
822 | printk("rpcsvc: received unknown control message:" | ||
823 | "%d/%d\n", | ||
824 | cmh->cmsg_level, cmh->cmsg_type); | ||
825 | skb_free_datagram(svsk->sk_sk, skb); | ||
826 | return 0; | ||
827 | } | ||
828 | svc_udp_get_dest_address(rqstp, cmh); | ||
831 | 829 | ||
832 | if (skb_is_nonlinear(skb)) { | 830 | if (skb_is_nonlinear(skb)) { |
833 | /* we have to copy */ | 831 | /* we have to copy */ |
@@ -884,6 +882,9 @@ svc_udp_sendto(struct svc_rqst *rqstp) | |||
884 | static void | 882 | static void |
885 | svc_udp_init(struct svc_sock *svsk) | 883 | svc_udp_init(struct svc_sock *svsk) |
886 | { | 884 | { |
885 | int one = 1; | ||
886 | mm_segment_t oldfs; | ||
887 | |||
887 | svsk->sk_sk->sk_data_ready = svc_udp_data_ready; | 888 | svsk->sk_sk->sk_data_ready = svc_udp_data_ready; |
888 | svsk->sk_sk->sk_write_space = svc_write_space; | 889 | svsk->sk_sk->sk_write_space = svc_write_space; |
889 | svsk->sk_recvfrom = svc_udp_recvfrom; | 890 | svsk->sk_recvfrom = svc_udp_recvfrom; |
@@ -899,6 +900,13 @@ svc_udp_init(struct svc_sock *svsk) | |||
899 | 900 | ||
900 | set_bit(SK_DATA, &svsk->sk_flags); /* might have come in before data_ready set up */ | 901 | set_bit(SK_DATA, &svsk->sk_flags); /* might have come in before data_ready set up */ |
901 | set_bit(SK_CHNGBUF, &svsk->sk_flags); | 902 | set_bit(SK_CHNGBUF, &svsk->sk_flags); |
903 | |||
904 | oldfs = get_fs(); | ||
905 | set_fs(KERNEL_DS); | ||
906 | /* make sure we get destination address info */ | ||
907 | svsk->sk_sock->ops->setsockopt(svsk->sk_sock, IPPROTO_IP, IP_PKTINFO, | ||
908 | (char __user *)&one, sizeof(one)); | ||
909 | set_fs(oldfs); | ||
902 | } | 910 | } |
903 | 911 | ||
904 | /* | 912 | /* |