Skip to content

Commit

Permalink
SUNRPC: handle IPv6 PKTINFO when extracting destination address
Browse files Browse the repository at this point in the history
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: 7a37f57

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>
  • Loading branch information
Chuck Lever authored and J. Bruce Fields committed Jul 14, 2009
1 parent 9208faf commit 7702ce4
Showing 1 changed file with 56 additions and 28 deletions.
84 changes: 56 additions & 28 deletions net/sunrpc/svcsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,30 +431,50 @@ static void svc_tcp_write_space(struct sock *sk)
svc_write_space(sk);
}

/*
* See net/ipv6/ip_sockglue.c : ip_cmsg_recv_pktinfo
*/
static int svc_udp_get_dest_address4(struct svc_rqst *rqstp,
struct cmsghdr *cmh)
{
struct in_pktinfo *pki = CMSG_DATA(cmh);
if (cmh->cmsg_type != IP_PKTINFO)
return 0;
rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr;
return 1;
}

/*
* See net/ipv6/datagram.c : datagram_recv_ctl
*/
static int svc_udp_get_dest_address6(struct svc_rqst *rqstp,
struct cmsghdr *cmh)
{
struct in6_pktinfo *pki = CMSG_DATA(cmh);
if (cmh->cmsg_type != IPV6_PKTINFO)
return 0;
ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr);
return 1;
}

/*
* Copy the UDP datagram's destination address to the rqstp structure.
* The 'destination' address in this case is the address to which the
* peer sent the datagram, i.e. our local address. For multihomed
* hosts, this can change from msg to msg. Note that only the IP
* address changes, the port number should remain the same.
*/
static void svc_udp_get_dest_address(struct svc_rqst *rqstp,
struct cmsghdr *cmh)
static int svc_udp_get_dest_address(struct svc_rqst *rqstp,
struct cmsghdr *cmh)
{
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
switch (svsk->sk_sk->sk_family) {
case AF_INET: {
struct in_pktinfo *pki = CMSG_DATA(cmh);
rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr;
break;
}
case AF_INET6: {
struct in6_pktinfo *pki = CMSG_DATA(cmh);
ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr);
break;
}
switch (cmh->cmsg_level) {
case SOL_IP:
return svc_udp_get_dest_address4(rqstp, cmh);
case SOL_IPV6:
return svc_udp_get_dest_address6(rqstp, cmh);
}

return 0;
}

/*
Expand Down Expand Up @@ -531,16 +551,15 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)

rqstp->rq_prot = IPPROTO_UDP;

if (cmh->cmsg_level != IPPROTO_IP ||
cmh->cmsg_type != IP_PKTINFO) {
if (!svc_udp_get_dest_address(rqstp, cmh)) {
if (net_ratelimit())
printk("rpcsvc: received unknown control message:"
"%d/%d\n",
cmh->cmsg_level, cmh->cmsg_type);
printk(KERN_WARNING
"svc: received unknown control message %d/%d; "
"dropping RPC reply datagram\n",
cmh->cmsg_level, cmh->cmsg_type);
skb_free_datagram(svsk->sk_sk, skb);
return 0;
}
svc_udp_get_dest_address(rqstp, cmh);

if (skb_is_nonlinear(skb)) {
/* we have to copy */
Expand Down Expand Up @@ -651,8 +670,7 @@ static struct svc_xprt_class svc_udp_class = {

static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)
{
int one = 1;
mm_segment_t oldfs;
int err, level, optname, one = 1;

svc_xprt_init(&svc_udp_class, &svsk->sk_xprt, serv);
clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags);
Expand All @@ -671,12 +689,22 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);

oldfs = get_fs();
set_fs(KERNEL_DS);
/* make sure we get destination address info */
svsk->sk_sock->ops->setsockopt(svsk->sk_sock, IPPROTO_IP, IP_PKTINFO,
(char __user *)&one, sizeof(one));
set_fs(oldfs);
switch (svsk->sk_sk->sk_family) {
case AF_INET:
level = SOL_IP;
optname = IP_PKTINFO;
break;
case AF_INET6:
level = SOL_IPV6;
optname = IPV6_RECVPKTINFO;
break;
default:
BUG();
}
err = kernel_setsockopt(svsk->sk_sock, level, optname,
(char *)&one, sizeof(one));
dprintk("svc: kernel_setsockopt returned %d\n", err);
}

/*
Expand Down

0 comments on commit 7702ce4

Please sign in to comment.