Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 3120

kernel-2.6.18-238.el5.src.rpm

From: Jeff Layton <jlayton@redhat.com>
Date: Fri, 31 Jul 2009 08:38:46 -0400
Subject: [net] sunrpc client: IF for binding to a local address
Message-id: 1249043927-10558-3-git-send-email-jlayton@redhat.com
O-Subject: [RHEL5.5 PATCH 2/3] BZ#500653: SUNRPC client: add interface for binding to a local address
Bugzilla: 500653
RH-Acked-by: Peter Staubach <staubach@redhat.com>

(Loosely backported from commit d3bc9a1deb8964d774af8535814cb91bf8f6def0)

In order to do callbacks from the correct address, the sending socket
needs to be bound to that address. Add a srcaddr field to the rpc_xprt,
and when it's populated have the RPC engine bind the socket to it.

To minimize behavior changes, I've tried to take the conservative
approach here and only bind the socket if a reserved port is needed, or
if the srcaddr is non-zero.

Signed-off-by: Jeff Layton <jlayton@redhat.com>

diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index af49932..71b073c 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -207,6 +207,9 @@ struct rpc_xprt {
 	void			(*old_data_ready)(struct sock *, int);
 	void			(*old_state_change)(struct sock *);
 	void			(*old_write_space)(struct sock *);
+#ifndef __GENKSYMS__
+	struct sockaddr_in	srcaddr;		/* local (source) address */
+#endif
 };
 
 #define XPRT_LAST_FRAG		(1 << 0)
@@ -214,6 +217,9 @@ struct rpc_xprt {
 #define XPRT_COPY_XID		(1 << 2)
 #define XPRT_COPY_DATA		(1 << 3)
 
+/* private RHEL flag to indicate presence of rpc_xprt.srcaddr field */
+#define XPRT_SRCADDR_PRESENT	(1 << 16)
+
 #ifdef __KERNEL__
 
 /*
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 40dbb96..919b2b5 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -891,6 +891,7 @@ static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc
 		return ERR_PTR(-ENOMEM);
 
 	xprt->addr = *ap;
+	xprt->tcp_flags |= XPRT_SRCADDR_PRESENT;
 
 	switch (proto) {
 	case IPPROTO_UDP:
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 8a2c717..2b694e1 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -832,7 +832,15 @@ static void xs_tcp_state_change(struct sock *sk)
 			xprt->tcp_offset = 0;
 			xprt->tcp_reclen = 0;
 			xprt->tcp_copied = 0;
-			xprt->tcp_flags = XPRT_COPY_RECM | XPRT_COPY_XID;
+
+			if (xprt->tcp_flags & XPRT_SRCADDR_PRESENT)
+				xprt->tcp_flags = XPRT_SRCADDR_PRESENT |
+						  XPRT_COPY_RECM |
+						  XPRT_COPY_XID;
+			else
+				xprt->tcp_flags = XPRT_COPY_RECM |
+						  XPRT_COPY_XID;
+
 			xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
 			xprt_wake_pending_tasks(xprt, 0);
 		}
@@ -984,7 +992,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
 	xprt->addr.sin_port = htons(port);
 }
 
-static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
+static int xs_bind(struct rpc_xprt *xprt, struct socket *sock)
 {
 	struct sockaddr_in myaddr = {
 		.sin_family = AF_INET,
@@ -992,15 +1000,28 @@ static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
 	int err;
 	unsigned short port = xprt->port;
 
+	/* only bind for reserved port, or if the the srcaddr is set */
+	if (xprt->tcp_flags & XPRT_SRCADDR_PRESENT) {
+		if (!xprt->resvport) {
+			if (xprt->srcaddr.sin_addr.s_addr == INADDR_ANY)
+				goto out_skip;
+			port = 0;
+		}
+		myaddr.sin_addr = xprt->srcaddr.sin_addr;
+	} else {
+		if (!xprt->resvport)
+			goto out_skip;
+	}
+
 	do {
 		myaddr.sin_port = htons(port);
 		err = kernel_bind(sock, (struct sockaddr *) &myaddr,
 						sizeof(myaddr));
+		if (!xprt->resvport)
+			break;
 		if (err == 0) {
 			xprt->port = port;
-			dprintk("RPC:      xs_bindresvport bound to port %u\n",
-					port);
-			return 0;
+			break;
 		}
 		if (port <= xprt_min_resvport)
 			port = xprt_max_resvport;
@@ -1008,8 +1029,14 @@ static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
 			port--;
 	} while (err == -EADDRINUSE && port != xprt->port);
 
-	dprintk("RPC:      can't bind to reserved port (%d).\n", -err);
+	dprintk("RPC:       xs_bind "NIPQUAD_FMT":%u: %s (%d)\n",
+		NIPQUAD(myaddr.sin_addr), port, err ? "failed" : "ok", err);
 	return err;
+out_skip:
+	dprintk("RPC:       xs_bind not binding socket (srcaddr %s)\n",
+		(xprt->tcp_flags & XPRT_SRCADDR_PRESENT) ? "present" :
+		"absent");
+	return 0;
 }
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -1069,7 +1096,7 @@ static void xs_udp_connect_worker(void *args)
 	}
 	xs_reclassify_socket(sock);
 
-	if (xprt->resvport && xs_bindresvport(xprt, sock) < 0) {
+	if (xs_bind(xprt, sock)) {
 		sock_release(sock);
 		goto out;
 	}
@@ -1152,7 +1179,7 @@ static void xs_tcp_connect_worker(void *args)
 		}
 		xs_reclassify_socket(sock);
 
-		if (xprt->resvport && xs_bindresvport(xprt, sock) < 0) {
+		if (xs_bind(xprt, sock)) {
 			sock_release(sock);
 			goto out;
 		}