SHA256
1
0
forked from pool/libtirpc
libtirpc/libtirpc-use-correct-source-addr-on-replies.patch
2011-02-22 05:08:58 +00:00

196 lines
5.4 KiB
Diff

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)