diff options
Diffstat (limited to 'net/sunrpc')
-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 | /* |