Bug#59800: RPC in glibc doesn't work with IP aliases
Package: libc6
Version: 2.1.3-4
The RPC code doesn't work right with IP aliases. The problem is that you can
get a UDP packet back from a different IP than the IP you sent the request
to.
For instance, a showmount -e server yields:
16:54:24.538854 va-su-137.valinux.com.814 > ftp.valinux.com.sunrpc: udp 56
16:54:24.539814 kernel.valinux.com.sunrpc > va-su-137.valinux.com.814: udp 2
Because the packet doesn't come back from the same IP address, it breaks
when you go through IP masquerading.
Here's a patch from HJ Lu, which we've been using successfully (although you
also need a similar fix in the kernel for everything to work, but HJ is
going to feed that through the proper channels)
Index: sunrpc/svc_udp.c
===================================================================
RCS file: /work/cvs/gnu/glibc-2.1/sunrpc/svc_udp.c,v
retrieving revision 1.1.1.5
diff -u -p -r1.1.1.5 svc_udp.c
--- sunrpc/svc_udp.c 1998/12/14 00:55:26 1.1.1.5
+++ sunrpc/svc_udp.c 2000/02/17 18:33:34
@@ -46,6 +46,10 @@ static char sccsid[] = "@(#)svc_udp.c 1.
#include <sys/socket.h>
#include <errno.h>
+#ifdef IP_PKTINFO
+#include <sys/uio.h>
+#endif
+
#ifdef USE_IN_LIBIO
# include <libio/iolibio.h>
# define fputs(s, f) _IO_fputs (s, f)
@@ -113,6 +117,7 @@ svcudp_bufcreate (sock, sendsz, recvsz)
struct svcudp_data *su;
struct sockaddr_in addr;
socklen_t len = sizeof (struct sockaddr_in);
+ int pad;
if (sock == RPC_ANYSOCK)
{
@@ -162,6 +167,27 @@ svcudp_bufcreate (sock, sendsz, recvsz)
xprt->xp_ops = &svcudp_op;
xprt->xp_port = ntohs (addr.sin_port);
xprt->xp_sock = sock;
+
+#ifdef IP_PKTINFO
+ if ((sizeof (struct iovec) + sizeof (struct msghdr)
+ + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
+ > sizeof (xprt->xp_pad))
+ {
+ (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
+ stderr);
+ return NULL;
+ }
+ pad = 1;
+ if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
+ sizeof (pad)) == 0)
+ /* Set the padding to all 1s. */
+ pad = 0xff;
+ else
+#endif
+ /* Clear the padding. */
+ pad = 0;
+ memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
+
xprt_register (xprt);
return xprt;
}
@@ -194,11 +220,41 @@ svcudp_recv (xprt, msg)
u_long replylen;
socklen_t len;
+ /* It is very tricky when you have IP aliases. We want to make sure
+ that we are sending the packet from the IP address where the
+ incoming packet is addressed to. H.J. */
+#ifdef IP_PKTINFO
+ struct iovec *iovp;
+ struct msghdr *mesgp;
+#endif
+
again:
/* FIXME -- should xp_addrlen be a size_t? */
len = (socklen_t) sizeof(struct sockaddr_in);
- rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt), (int) su->su_iosz, 0,
- (struct sockaddr *) &(xprt->xp_raddr), &len);
+#ifdef IP_PKTINFO
+ iovp = (struct iovec *) &xprt->xp_pad [0];
+ mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
+ if (mesgp->msg_iovlen)
+ {
+ iovp->iov_base = rpc_buffer (xprt);
+ iovp->iov_len = su->su_iosz;
+ mesgp->msg_iov = iovp;
+ mesgp->msg_iovlen = 1;
+ mesgp->msg_name = &(xprt->xp_raddr);
+ mesgp->msg_namelen = len;
+ mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
+ + sizeof (struct msghdr)];
+ mesgp->msg_controllen = sizeof(struct cmsghdr)
+ + sizeof (struct in_pktinfo);
+ rlen = recvmsg (xprt->xp_sock, mesgp, 0);
+ if (rlen >= 0)
+ len = mesgp->msg_namelen;
+ }
+ else
+#endif
+ rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
+ (int) su->su_iosz, 0,
+ (struct sockaddr *) &(xprt->xp_raddr), &len);
xprt->xp_addrlen = len;
if (rlen == -1 && errno == EINTR)
goto again;
@@ -213,8 +269,17 @@ again:
{
if (cache_get (xprt, msg, &reply, &replylen))
{
- (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
- (struct sockaddr *) &xprt->xp_raddr, len);
+#ifdef IP_PKTINFO
+ if (mesgp->msg_iovlen)
+ {
+ iovp->iov_base = reply;
+ iovp->iov_len = replylen;
+ (void) sendmsg (xprt->xp_sock, mesgp, 0);
+ }
+ else
+#endif
+ (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
+ (struct sockaddr *) &xprt->xp_raddr, len);
return TRUE;
}
}
@@ -228,8 +293,12 @@ svcudp_reply (xprt, msg)
{
struct svcudp_data *su = su_data (xprt);
XDR *xdrs = &(su->su_xdrs);
- int slen;
+ int slen, sent;
bool_t stat = FALSE;
+#ifdef IP_PKTINFO
+ struct iovec *iovp;
+ struct msghdr *mesgp;
+#endif
xdrs->x_op = XDR_ENCODE;
XDR_SETPOS (xdrs, 0);
@@ -237,9 +306,21 @@ svcudp_reply (xprt, msg)
if (xdr_replymsg (xdrs, msg))
{
slen = (int) XDR_GETPOS (xdrs);
- if (sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
- (struct sockaddr *) &(xprt->xp_raddr), xprt->xp_addrlen)
- == slen)
+#ifdef IP_PKTINFO
+ mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
+ if (mesgp->msg_iovlen)
+ {
+ iovp = (struct iovec *) &xprt->xp_pad [0];
+ iovp->iov_base = rpc_buffer (xprt);
+ iovp->iov_len = slen;
+ sent = sendmsg (xprt->xp_sock, mesgp, 0);
+ }
+ else
+#endif
+ sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
+ (struct sockaddr *) &(xprt->xp_raddr),
+ xprt->xp_addrlen);
+ if (sent == slen)
{
stat = TRUE;
if (su->su_cache && slen >= 0)
Index: sunrpc/rpc/svc.h
===================================================================
RCS file: /work/cvs/gnu/glibc-2.1/sunrpc/rpc/svc.h,v
retrieving revision 1.1.1.7
diff -u -p -r1.1.1.7 svc.h
--- sunrpc/rpc/svc.h 1998/07/29 23:55:59 1.1.1.7
+++ sunrpc/rpc/svc.h 2000/02/17 18:36:54
@@ -97,6 +97,7 @@ struct SVCXPRT {
struct opaque_auth xp_verf; /* raw response verifier */
caddr_t xp_p1; /* private */
caddr_t xp_p2; /* private */
+ char xp_pad [256]; /* padding, internal use */
};
/*
--
Microsoft is to software what McDonalds is to gourmet cooking
Home page: http://marc.merlins.org/ (friendly to non IE browsers)
Finger marc_f@merlins.org for PGP key and other contact information
Reply to: