File libtirpc-use-correct-source-addr-on-replies.patch of Package libtirpc

Patch by Olaf Kirch and Leonardo Chiquitto as attached with id 348693
at https://bugzilla.novell.com/show_bug.cgi?id=587934

diff --git a/src/svc_dg.c b/src/svc_dg.c
index 7df470e..edf7d17 100644
--- a/src/svc_dg.c
+++ b/src/svc_dg.c
@@ -76,6 +76,8 @@ static bool_t svc_dg_control(SVCXPRT *, const u_int, void *);
 static int cache_get(SVCXPRT *, struct rpc_msg *, char **, size_t *);
 static void cache_set(SVCXPRT *, size_t);
 int svc_dg_enablecache(SVCXPRT *, u_int);
+static void svc_dg_enable_pktinfo(int, const struct __rpc_sockinfo *);
+static int svc_dg_valid_pktinfo(struct msghdr *);
 
 /*
  * Usage:
@@ -142,6 +144,9 @@ svc_dg_create(fd, sendsize, recvsize)
 		goto freedata;
 	__rpc_set_netbuf(&xprt->xp_ltaddr, &ss, slen);
 
+	/* Enable reception of IP*_PKTINFO control msgs */
+	svc_dg_enable_pktinfo(fd, &si);
+
 	xprt_register(xprt);
 	return (xprt);
 freedata:
@@ -171,19 +176,37 @@ svc_dg_recv(xprt, msg)
 	XDR *xdrs = &(su->su_xdrs);
 	char *reply;
 	struct sockaddr_storage ss;
+	struct msghdr *mesgp;
+	struct iovec iov;
 	socklen_t alen;
 	size_t replylen;
 	ssize_t rlen;
 
 again:
-	alen = sizeof (struct sockaddr_storage);
-	rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0,
-	    (struct sockaddr *)(void *)&ss, &alen);
+	iov.iov_base = rpc_buffer(xprt);
+	iov.iov_len = su->su_iosz;
+	mesgp = &su->su_msghdr;
+	memset(mesgp, 0, sizeof(*mesgp));
+	mesgp->msg_iov = &iov;
+	mesgp->msg_iovlen = 1;
+	mesgp->msg_name = (struct sockaddr *)(void *) &ss;
+	mesgp->msg_namelen = sizeof (struct sockaddr_storage);
+	mesgp->msg_control = su->su_cmsg;
+	mesgp->msg_controllen = sizeof(su->su_cmsg);
+
+	rlen = recvmsg(xprt->xp_fd, mesgp, 0);
 	if (rlen == -1 && errno == EINTR)
 		goto again;
 	if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t))))
 		return (FALSE);
-	__rpc_set_netbuf(&xprt->xp_rtaddr, &ss, alen);
+	__rpc_set_netbuf(&xprt->xp_rtaddr, &ss, mesgp->msg_namelen);
+
+	/* Check whether there's an IP_PKTINFO or IP6_PKTINFO control message.
+	 * If yes, preserve it for svc_dg_reply; otherwise just zap an cmsgs */
+	if (!svc_dg_valid_pktinfo(mesgp)) {
+		mesgp->msg_control = NULL;
+		mesgp->msg_controllen = 0;
+	}
 
 	__xprt_set_raddr(xprt, &ss);
 	xdrs->x_op = XDR_DECODE;
@@ -194,8 +217,9 @@ again:
 	su->su_xid = msg->rm_xid;
 	if (su->su_cache != NULL) {
 		if (cache_get(xprt, msg, &reply, &replylen)) {
-			(void)sendto(xprt->xp_fd, reply, replylen, 0,
-			    (struct sockaddr *)(void *)&ss, alen);
+			iov.iov_base = reply;
+			iov.iov_len = replylen;
+			(void) sendmsg(xprt->xp_fd, mesgp, 0);
 			return (FALSE);
 		}
 	}
@@ -216,10 +240,18 @@ svc_dg_reply(xprt, msg)
 	XDR_SETPOS(xdrs, 0);
 	msg->rm_xid = su->su_xid;
 	if (xdr_replymsg(xdrs, msg)) {
-		slen = XDR_GETPOS(xdrs);
-		if (sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0,
-		    (struct sockaddr *)xprt->xp_rtaddr.buf,
-		    (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) {
+		struct msghdr *msg = &su->su_msghdr;
+		struct iovec iov;
+
+		iov.iov_base = rpc_buffer(xprt);
+		iov.iov_len = slen = XDR_GETPOS(xdrs);
+		msg->msg_iov = &iov;
+		msg->msg_iovlen = 1;
+		msg->msg_name = (struct sockaddr *)(void *) xprt->xp_rtaddr.buf;
+		msg->msg_namelen = xprt->xp_rtaddr.len;
+		/* cmsg already set in svc_dg_recv */
+
+		if (sendmsg(xprt->xp_fd, msg, 0) == (ssize_t) slen) {
 			stat = TRUE;
 			if (su->su_cache)
 				cache_set(xprt, slen);
@@ -583,3 +615,76 @@ cache_get(xprt, msg, replyp, replylenp)
 	mutex_unlock(&dupreq_lock);
 	return (0);
 }
+
+/*
+ * Enable reception of PKTINFO control messages
+ */
+void
+svc_dg_enable_pktinfo(int fd, const struct __rpc_sockinfo *si)
+{
+	int val = 1;
+
+	switch (si->si_af) {
+	case AF_INET:
+		(void) setsockopt(fd, SOL_IP, IP_PKTINFO, &val, sizeof(val));
+		break;
+
+	case AF_INET6:
+		(void) setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &val, sizeof(val));
+		break;
+	}
+}
+
+/*
+ * When given a control message received from the socket
+ * layer, check whether it contains valid PKTINFO data matching
+ * the address family of the peer address.
+ */
+int
+svc_dg_valid_pktinfo(struct msghdr *msg)
+{
+	struct cmsghdr *cmsg;
+
+	if (!msg->msg_name)
+		return 0;
+
+	if (msg->msg_flags & MSG_CTRUNC)
+		return 0;
+
+	cmsg = CMSG_FIRSTHDR(msg);
+	if (cmsg == NULL || CMSG_NXTHDR(msg, cmsg) != NULL)
+		return 0;
+
+	switch (((struct sockaddr *) msg->msg_name)->sa_family) {
+	case AF_INET:
+		if (cmsg->cmsg_level != SOL_IP
+		 || cmsg->cmsg_type != IP_PKTINFO
+		 || cmsg->cmsg_len < CMSG_LEN(sizeof (struct in_pktinfo))) {
+			return 0;
+		} else {
+			struct in_pktinfo *pkti;
+			
+			pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+			pkti->ipi_ifindex = 0;
+		}
+		break;
+
+	case AF_INET6:
+		if (cmsg->cmsg_level != SOL_IPV6
+		 || cmsg->cmsg_type != IPV6_PKTINFO
+		 || cmsg->cmsg_len < CMSG_LEN(sizeof (struct in6_pktinfo))) {
+			return 0;
+		} else {
+			struct in6_pktinfo *pkti;
+			
+			pkti = (struct in6_pktinfo *) CMSG_DATA (cmsg);
+			pkti->ipi6_ifindex = 0;
+		}
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/tirpc/rpc/svc_dg.h b/tirpc/rpc/svc_dg.h
index 67d2564..88e7df7 100644
--- a/tirpc/rpc/svc_dg.h
+++ b/tirpc/rpc/svc_dg.h
@@ -46,6 +46,9 @@ struct svc_dg_data {
 	XDR		su_xdrs;			/* XDR handle */
 	char		su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
 	void		*su_cache;		/* cached data, NULL if none */
+
+	struct msghdr	su_msghdr;		/* msghdr received from clnt */
+	unsigned char	su_cmsg[64];		/* cmsghdr received from clnt */
 };
 
 #define __rpcb_get_dg_xidp(x)	(&((struct svc_dg_data *)(x)->xp_p2)->su_xid)
openSUSE Build Service is sponsored by