All of lore.kernel.org
 help / color / mirror / Atom feed
* PATCH:  Support binding to a local IPv4 address when mounting a server.
@ 2009-01-22  1:01 Ben Greear
  2009-01-22  2:38 ` Chuck Lever
  0 siblings, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-01-22  1:01 UTC (permalink / raw)
  To: linux-nfs, Patrick McHardy

[-- Attachment #1: Type: text/plain, Size: 814 bytes --]


The attached patch allows one to mount NFSv3 (should work with v4 as well,
but not tested as of today) servers and specify the local IP address.

This facilitates mounting on a particular interface if you have a
multi-homed box.

I have a matching patch to the kernel that I will be porting forward to
the latest 2.6.29-rcX branch next.

Please note that Patrick gets a lot of credit for helping me with
this patch in the past, though I have somewhat butchered his previous
work so blame can fall directly to me :)

Please let me know if this patch or something like it would be welcome
in the nfs-utils tree.  Suggestions for improvement are welcome.

Signed-Off-By: Ben Greear<greearb@candelatech.com>

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


[-- Attachment #2: nfs_local_ip_14.patch --]
[-- Type: text/x-patch, Size: 39295 bytes --]

diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h
index fc26f4e..03dea21 100644
--- a/support/include/nfs/nfs.h
+++ b/support/include/nfs/nfs.h
@@ -71,7 +71,7 @@ struct nfsctl_client {
  */
 #include <linux/version.h>
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,70)
-# define __nfsd_dev_t  __kernel_old_dev_t
+# define __nfsd_dev_t  unsigned short /*__kernel_old_dev_t -BEN*/
 #else
 # define __nfsd_dev_t  __kernel_dev_t
 #endif
diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
index 1529d44..265bbbe 100644
--- a/support/include/nfsrpc.h
+++ b/support/include/nfsrpc.h
@@ -62,7 +62,8 @@ extern unsigned short	nfs_getportbynumber(const rpcprog_t program,
 /*
  * Acquire an RPC CLIENT *
  */
-extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
+extern CLIENT *nfs_get_rpcclient(struct sockaddr* caddr, const socklen_t clen,
+				 const struct sockaddr *,
 				const socklen_t, const unsigned short,
 				const rpcprog_t, const rpcvers_t,
 				struct timeval *);
@@ -83,7 +84,8 @@ extern int		nfs_universal2port(const char *);
  * number of the service on a remote post, and sends a NULL
  * request to determine if the service is responding to requests
  */
-extern int		nfs_getport_ping(struct sockaddr *sap,
+extern int		nfs_getport_ping(struct sockaddr* caddr, const socklen_t clen,
+				struct sockaddr *sap,
 				const socklen_t salen,
 				const rpcprog_t program,
 				const rpcvers_t version,
@@ -93,7 +95,8 @@ extern int		nfs_getport_ping(struct sockaddr *sap,
  * Generic function that maps an RPC service tuple to an IP port
  * number of the service on a remote host
  */
-extern unsigned short	nfs_getport(const struct sockaddr *,
+extern unsigned short	nfs_getport(struct sockaddr* caddr, const socklen_t clen,
+				const struct sockaddr *,
 				const socklen_t, const rpcprog_t,
 				const rpcvers_t, const unsigned short);
 
@@ -120,7 +123,8 @@ extern unsigned short	nfs_rpcb_getaddr(const struct sockaddr *,
 /*
  * Function to invoke a portmap GETPORT request
  */
-extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
+extern unsigned long	nfs_pmap_getport(struct sockaddr* caddr, const socklen_t clen,
+				const struct sockaddr_in *,
 				const unsigned short,
 				const unsigned long,
 				const unsigned long,
@@ -131,7 +135,8 @@ extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
  * Contact a remote RPC service to discover whether it is responding
  * to requests.
  */
-extern int		nfs_rpc_ping(const struct sockaddr *sap,
+extern int		nfs_rpc_ping(struct sockaddr* caddr, const socklen_t clen,
+				const struct sockaddr *sap,
 				const socklen_t salen,
 				const rpcprog_t program,
 				const rpcvers_t version,
diff --git a/support/nfs/getport.c b/support/nfs/getport.c
index 47824a2..ec099ba 100644
--- a/support/nfs/getport.c
+++ b/support/nfs/getport.c
@@ -190,7 +190,9 @@ static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port)
  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
  * reflect the error.
  */
-static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
+static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr* caddr,
+				     const socklen_t clen,
+				     const struct sockaddr *sap,
 				     const socklen_t salen,
 				     const unsigned short transport,
 				     const rpcvers_t version,
@@ -203,7 +205,7 @@ static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
 	memcpy(saddr, sap, (size_t)salen);
 	nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport));
 
-	return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog,
+	return nfs_get_rpcclient(caddr, clen, saddr, salen, transport, rpcb_prog,
 					version, timeout);
 }
 
@@ -660,7 +662,8 @@ static unsigned short nfs_gp_getport(CLIENT *client,
  * Returns 1 if the remote service responded without an error; otherwise
  * zero.
  */
-int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
+int nfs_rpc_ping(struct sockaddr* caddr, const socklen_t clen,
+		 const struct sockaddr *sap, const socklen_t salen,
 		 const rpcprog_t program, const rpcvers_t version,
 		 const unsigned short protocol, const struct timeval *timeout)
 {
@@ -671,7 +674,7 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
 	if (timeout != NULL)
 		tout = *timeout;
 
-	client = nfs_get_rpcclient(sap, salen, protocol, program, version, &tout);
+	client = nfs_get_rpcclient(caddr, clen, sap, salen, protocol, program, version, &tout);
 	if (client != NULL) {
 		result = nfs_gp_ping(client, tout);
 		CLNT_DESTROY(client);
@@ -720,7 +723,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
  * AF_INET6 at all.  The rpcbind socket is re-used in an attempt to keep the
  * overall number of consumed ephemeral ports low.
  */
-unsigned short nfs_getport(const struct sockaddr *sap,
+unsigned short nfs_getport(struct sockaddr* caddr, const socklen_t clen,
+			   const struct sockaddr *sap,
 			   const socklen_t salen,
 			   const rpcprog_t program,
 			   const rpcvers_t version,
@@ -730,7 +734,7 @@ unsigned short nfs_getport(const struct sockaddr *sap,
 	unsigned short port = 0;
 	CLIENT *client;
 
-	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
+	client = nfs_gp_get_rpcbclient(caddr, clen, sap, salen, protocol,
 						default_rpcb_version, &timeout);
 	if (client != NULL) {
 		port = nfs_gp_getport(client, sap, salen, program,
@@ -759,7 +763,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
  * if both the query and the ping were successful; otherwise zero.
  * rpccreateerr is set to reflect the underlying cause of the error.
  */
-int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
+int nfs_getport_ping(struct sockaddr* caddr, const socklen_t clen,
+		     struct sockaddr *sap, const socklen_t salen,
 		     const rpcprog_t program, const rpcvers_t version,
 		     const unsigned short protocol)
 {
@@ -768,7 +773,7 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 	CLIENT *client;
 	int result = 0;
 	
-	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
+	client = nfs_gp_get_rpcbclient(caddr, clen, sap, salen, protocol,
 						default_rpcb_version, &timeout);
 	if (client != NULL) {
 		port = nfs_gp_getport(client, sap, salen, program,
@@ -784,7 +789,7 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 		memcpy(saddr, sap, (size_t)salen);
 		nfs_gp_set_port(saddr, htons(port));
 
-		client = nfs_get_rpcclient(saddr, salen, protocol,
+		client = nfs_get_rpcclient(caddr, clen, saddr, salen, protocol,
 						program, version, &timeout);
 		if (client != NULL) {
 			result = nfs_gp_ping(client, timeout);
@@ -836,7 +841,7 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
 {
 	struct sockaddr_storage address;
 	struct sockaddr *lb_addr = (struct sockaddr *)&address;
-	socklen_t lb_len = sizeof(lb_addr);
+	socklen_t lb_len = sizeof(*lb_addr);
 	unsigned short port = 0;
 
 #ifdef NFS_GP_LOCAL
@@ -864,7 +869,23 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
 
 	if (port == 0) {
 		if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
-			port = nfs_getport(lb_addr, lb_len,
+			struct sockaddr_in sin = {
+				.sin_family		= AF_INET,
+				.sin_addr.s_addr	= htonl(INADDR_ANY),
+			};
+			struct sockaddr_in6 sin6 = {
+				.sin6_family		= AF_INET6,
+				.sin6_addr		= IN6ADDR_ANY_INIT,
+			};
+			struct sockaddr* caddr = (struct sockaddr*)(&sin);
+			int ln = sizeof(sin);
+
+			if (lb_addr->sa_family == AF_INET6) {
+				caddr = (struct sockaddr*)(&sin6);
+				ln = sizeof(sin6);
+			}
+
+			port = nfs_getport(caddr, ln, lb_addr, lb_len,
 						program, version, protocol);
 		} else
 			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
@@ -983,7 +1004,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
  *  3.	This version shares code with the rpcbindv3 and rpcbindv4 query
  *	functions.  It can use a TI-RPC generated CLIENT.
  */
-unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
+unsigned long nfs_pmap_getport(struct sockaddr* caddr, const socklen_t clen,
+			       const struct sockaddr_in *sin,
 			       const unsigned short transport,
 			       const unsigned long program,
 			       const unsigned long version,
@@ -1002,7 +1024,7 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
 	if (timeout != NULL)
 		tout = *timeout;
 
-	client = nfs_gp_get_rpcbclient((struct sockaddr *)sin,
+	client = nfs_gp_get_rpcbclient(caddr, clen, (struct sockaddr *)sin,
 					(socklen_t)sizeof(*sin),
 					transport, PMAPVERS, &tout);
 	if (client != NULL) {
diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
index 82ba818..57c96c8 100644
--- a/support/nfs/rpc_socket.c
+++ b/support/nfs/rpc_socket.c
@@ -149,28 +149,10 @@ static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
  * Returns zero on success, or returns -1 on error.  errno is
  * set to reflect the nature of the error.
  */
-static int nfs_bind(const int sock, const sa_family_t family)
+static int nfs_bind(struct sockaddr* caddr, const socklen_t clen,
+		    const int sock, const sa_family_t family)
 {
-	struct sockaddr_in sin = {
-		.sin_family		= AF_INET,
-		.sin_addr.s_addr	= htonl(INADDR_ANY),
-	};
-	struct sockaddr_in6 sin6 = {
-		.sin6_family		= AF_INET6,
-		.sin6_addr		= IN6ADDR_ANY_INIT,
-	};
-
-	switch (family) {
-	case AF_INET:
-		return bind(sock, (struct sockaddr *)&sin,
-					(socklen_t)sizeof(sin));
-	case AF_INET6:
-		return bind(sock, (struct sockaddr *)&sin6,
-					(socklen_t)sizeof(sin6));
-	}
-
-	errno = EAFNOSUPPORT;
-	return -1;
+	return bind(sock, caddr, clen);
 }
 
 /*
@@ -255,7 +237,8 @@ done:
  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
  * rpc_createerr.cf_stat is set to reflect the error.
  */
-static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
+static CLIENT *nfs_get_udpclient(struct sockaddr* caddr, const socklen_t clen,
+				 const struct sockaddr *sap,
 				 const socklen_t salen,
 				 const rpcprog_t program,
 				 const rpcvers_t version,
@@ -286,7 +269,7 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
 		return NULL;
 	}
 
-	ret = nfs_bind(sock, sap->sa_family);
+	ret = nfs_bind(caddr, clen, sock, sap->sa_family);
 	if (ret < 0) {
 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 		rpc_createerr.cf_error.re_errno = errno;
@@ -331,7 +314,8 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
  * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
  * error.
  */
-static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
+static CLIENT *nfs_get_tcpclient(struct sockaddr* caddr, const socklen_t clen,
+				 const struct sockaddr *sap,
 				 const socklen_t salen,
 				 const rpcprog_t program,
 				 const rpcvers_t version,
@@ -362,7 +346,7 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
 		return NULL;
 	}
 
-	ret = nfs_bind(sock, sap->sa_family);
+	ret = nfs_bind(caddr, clen, sock, sap->sa_family);
 	if (ret < 0) {
 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 		rpc_createerr.cf_error.re_errno = errno;
@@ -413,7 +397,9 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
  * reflect the error.
  */
-CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
+CLIENT *nfs_get_rpcclient(struct sockaddr *caddr,
+			  const socklen_t clen,
+			  const struct sockaddr *sap,
 			  const socklen_t salen,
 			  const unsigned short transport,
 			  const rpcprog_t program,
@@ -446,10 +432,10 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
 
 	switch (transport) {
 	case IPPROTO_TCP:
-		return nfs_get_tcpclient(sap, salen, program, version, timeout);
+		return nfs_get_tcpclient(caddr, clen, sap, salen, program, version, timeout);
 	case 0:
 	case IPPROTO_UDP:
-		return nfs_get_udpclient(sap, salen, program, version, timeout);
+		return nfs_get_udpclient(caddr, clen, sap, salen, program, version, timeout);
 	}
 
 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
diff --git a/utils/mount/network.c b/utils/mount/network.c
index d262e94..5fb52f6 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -428,30 +428,27 @@ out:
  *
  * The caller should check rpc_createerr to determine the cause of any error.
  */
-static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
-			unsigned int timeout, int resvp, int conn)
+static int get_socket(struct sockaddr *caddr, const socklen_t clen,
+		      struct sockaddr_in *saddr,
+		      unsigned int p_prot, unsigned int timeout, int resvp,
+		      int conn)
 {
 	int so, cc, type;
-	struct sockaddr_in laddr;
-	socklen_t namelen = sizeof(laddr);
 
 	type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
 	if ((so = socket (AF_INET, type, p_prot)) < 0)
 		goto err_socket;
 
-	laddr.sin_family = AF_INET;
-	laddr.sin_port = 0;
-	laddr.sin_addr.s_addr = htonl(INADDR_ANY);
 	if (resvp) {
-		if (bindresvport(so, &laddr) < 0)
+		if (bindresvport(so, (struct sockaddr_in*)caddr) < 0)
 			goto err_bindresvport;
 	} else {
-		cc = bind(so, (struct sockaddr *)&laddr, namelen);
+		cc = bind(so, caddr, clen);
 		if (cc < 0)
 			goto err_bind;
 	}
 	if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
-		cc = connect_to(so, (struct sockaddr *)saddr, namelen,
+		cc = connect_to(so, (struct sockaddr *)saddr, sizeof(*saddr),
 				timeout);
 		if (cc < 0)
 			goto err_connect;
@@ -473,9 +470,9 @@ err_bindresvport:
 	rpc_createerr.cf_error.re_errno = errno;
 	if (verbose) {
 		nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
-				" (%s)\n"),
+				" (%s)  caddr->family: %i\n"),
 			progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
-			errno, strerror(errno));
+			  errno, strerror(errno), (int)(caddr->sa_family));
 	}
 	close(so);
 	return RPC_ANYSOCK;
@@ -519,11 +516,12 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
 	}
 
 	fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d\n"),
-			progname, buf, program, version,
-			(protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
-			port);
+		progname, buf, program, version,
+		(protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
+		port);
 }
 
+
 /*
  * Use the portmapper to discover whether or not the service we want is
  * available. The lists 'versions' and 'protos' define ordered sequences
@@ -538,7 +536,8 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
  * If an error occurs or the requested service isn't available, zero is
  * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
  */
-static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
+static int nfs_probe_port(struct sockaddr *caddr, const socklen_t clen,
+			  const struct sockaddr *sap, const socklen_t salen,
 			  struct pmap *pmap, const unsigned long *versions,
 			  const unsigned int *protos)
 {
@@ -556,13 +555,12 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
 	rpc_createerr.cf_stat = 0;
 
 	for (;;) {
-		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
+		p_port = nfs_getport(caddr, clen, saddr, salen, prog, *p_vers, *p_prot);
 		if (p_port) {
 			if (!port || port == p_port) {
 				nfs_set_port(saddr, p_port);
-				nfs_pp_debug(saddr, salen, prog, *p_vers,
-						*p_prot, p_port);
-				if (nfs_rpc_ping(saddr, salen, prog,
+				nfs_pp_debug(saddr, salen, prog, *p_vers, *p_prot, p_port);
+				if (nfs_rpc_ping(caddr, clen, saddr, salen, prog,
 							*p_vers, *p_prot, NULL))
 					goto out_ok;
 			}
@@ -613,17 +611,18 @@ out_ok:
  * If an error occurs or the requested service isn't available, zero is
  * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
  */
-static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
-				struct pmap *pmap)
+static int nfs_probe_nfsport(struct sockaddr *caddr, const socklen_t clen,
+                             const struct sockaddr *sap, const socklen_t salen,
+                             struct pmap *pmap)
 {
 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
 		return 1;
 
 	if (nfs_mount_data_version >= 4)
-		return nfs_probe_port(sap, salen, pmap,
+		return nfs_probe_port(caddr, clen, sap, salen, pmap,
 					probe_nfs3_first, probe_tcp_first);
 	else
-		return nfs_probe_port(sap, salen, pmap,
+		return nfs_probe_port(caddr, clen, sap, salen, pmap,
 					probe_nfs2_only, probe_udp_only);
 }
 
@@ -640,17 +639,18 @@ static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
  * If an error occurs or the requested service isn't available, zero is
  * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
  */
-static int nfs_probe_mntport(const struct sockaddr *sap, const socklen_t salen,
+static int nfs_probe_mntport(struct sockaddr *caddr, const socklen_t clen,
+                             const struct sockaddr *sap, const socklen_t salen,
 				struct pmap *pmap)
 {
 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
 		return 1;
 
 	if (nfs_mount_data_version >= 4)
-		return nfs_probe_port(sap, salen, pmap,
+		return nfs_probe_port(caddr, clen, sap, salen, pmap,
 					probe_mnt3_first, probe_udp_first);
 	else
-		return nfs_probe_port(sap, salen, pmap,
+		return nfs_probe_port(caddr, clen, sap, salen, pmap,
 					probe_mnt1_first, probe_udp_only);
 }
 
@@ -666,13 +666,15 @@ static int nfs_probe_mntport(const struct sockaddr *sap, const socklen_t salen,
 static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
 			const socklen_t mnt_salen,
 			struct pmap *mnt_pmap,
+			struct sockaddr *nfs_caddr,
+			const socklen_t nfs_clen,
 			const struct sockaddr *nfs_saddr,
 			const socklen_t nfs_salen,
 			struct pmap *nfs_pmap)
 {
-	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
+	if (!nfs_probe_nfsport(nfs_caddr, nfs_clen, nfs_saddr, nfs_salen, nfs_pmap))
 		return 0;
-	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+	return nfs_probe_mntport(nfs_caddr, nfs_clen, mnt_saddr, mnt_salen, mnt_pmap);
 }
 
 /**
@@ -693,6 +695,8 @@ static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
 int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
 			const socklen_t mnt_salen,
 			struct pmap *mnt_pmap,
+                        struct sockaddr *nfs_caddr,
+			const socklen_t nfs_clen,
 			const struct sockaddr *nfs_saddr,
 			const socklen_t nfs_salen,
 			struct pmap *nfs_pmap)
@@ -707,6 +711,7 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
 
 	if (nfs_pmap->pm_vers)
 		return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap,
+					       nfs_caddr, nfs_clen,
 					       nfs_saddr, nfs_salen, nfs_pmap);
 
 	memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
@@ -716,9 +721,9 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
 
 	for (; *probe_vers; probe_vers++) {
 		nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
-		if (nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap) != 0) {
+		if (nfs_probe_nfsport(nfs_caddr, nfs_clen, nfs_saddr, nfs_salen, nfs_pmap) != 0) {
 			mnt_pmap->pm_vers = *probe_vers;
-			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
+			if (nfs_probe_mntport(nfs_caddr, nfs_clen, mnt_saddr, mnt_salen, mnt_pmap) != 0)
 				return 1;
 			memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
 		}
@@ -748,11 +753,12 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
  * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect
  * the nature of the error.
  */
-int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
+int probe_bothports(struct sockaddr* caddr, const socklen_t clen, clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
 {
 	return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr,
 					sizeof(mnt_server->saddr),
 					&mnt_server->pmap,
+				 	caddr, clen,
 					(struct sockaddr *)&nfs_server->saddr,
 					sizeof(nfs_server->saddr),
 					&nfs_server->pmap);
@@ -764,9 +770,14 @@ static int nfs_probe_statd(void)
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
 	};
+	struct sockaddr_in laddr = {
+		.sin_family		= AF_INET,
+		.sin_addr.s_addr	= htonl(INADDR_ANY),
+	};
 	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
 
-	return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
+	return nfs_getport_ping((struct sockaddr*)&laddr, sizeof(laddr),
+				(struct sockaddr *)&addr, sizeof(addr),
 				program, (rpcvers_t)1, IPPROTO_UDP);
 }
 
@@ -820,7 +831,7 @@ int start_statd(void)
  * Note that a side effect of calling this function is that rpccreateerr
  * is set.
  */
-int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
+int nfs_call_umount(struct sockaddr *caddr, const socklen_t clen, clnt_addr_t *mnt_server, dirpath *argp)
 {
 	struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
 	socklen_t salen = sizeof(mnt_server->saddr);
@@ -829,9 +840,9 @@ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
 	enum clnt_stat res = 0;
 	int msock;
 
-	if (!nfs_probe_mntport(sap, salen, pmap))
+	if (!nfs_probe_mntport(caddr, clen, sap, salen, pmap))
 		return 0;
-	clnt = mnt_openclnt(mnt_server, &msock);
+	clnt = mnt_openclnt(caddr, clen, mnt_server, &msock);
 	if (!clnt)
 		return 0;
 	res = clnt_call(clnt, MOUNTPROC_UMNT,
@@ -852,14 +863,14 @@ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
  *
  * Returns an active handle for the remote's mountd service
  */
-CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
+CLIENT *mnt_openclnt(struct sockaddr *caddr, const socklen_t clen, clnt_addr_t *mnt_server, int *msock)
 {
 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
 	struct pmap *mnt_pmap = &mnt_server->pmap;
 	CLIENT *clnt = NULL;
 
 	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
-	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
+	*msock = get_socket(caddr, clen, mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
 				TRUE, FALSE);
 	if (*msock == RPC_ANYSOCK) {
 		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
@@ -925,9 +936,9 @@ void mnt_closeclnt(CLIENT *clnt, int msock)
  *
  * Returns one if successful, otherwise zero.
  */
-int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
-		const unsigned long vers, const unsigned int prot,
-		struct sockaddr_in *caddr)
+int clnt_ping(struct sockaddr* caddr, socklen_t clen,
+	      struct sockaddr_in *saddr, const unsigned long prog,
+	      const unsigned long vers, const unsigned int prot)
 {
 	CLIENT *clnt = NULL;
 	int sock, stat;
@@ -935,7 +946,7 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 	struct sockaddr dissolve;
 
 	rpc_createerr.cf_stat = stat = 0;
-	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
+	sock = get_socket(caddr, clen, saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
 	if (sock == RPC_ANYSOCK) {
 		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
 			/*
@@ -949,9 +960,8 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 
 	if (caddr) {
 		/* Get the address of our end of this connection */
-		socklen_t len = sizeof(*caddr);
-		if (getsockname(sock, caddr, &len) != 0)
-			caddr->sin_family = 0;
+		if (getsockname(sock, caddr, &clen) != 0)
+			((struct sockaddr_in*)(caddr))->sin_family = 0;
 	}
 
 	switch(prot) {
@@ -1003,40 +1013,20 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
  * This conserves the ephemeral port number space, helping reduce failed
  * socket binds during mount storms.
  */
-static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen,
+static int nfs_ca_sockname(const struct sockaddr* caddr, const socklen_t clen,
+                           const struct sockaddr *sap, const socklen_t salen,
 			   struct sockaddr *buf, socklen_t *buflen)
 {
-	struct sockaddr_in sin = {
-		.sin_family		= AF_INET,
-		.sin_addr.s_addr	= htonl(INADDR_ANY),
-	};
-	struct sockaddr_in6 sin6 = {
-		.sin6_family		= AF_INET6,
-		.sin6_addr		= IN6ADDR_ANY_INIT,
-	};
 	int sock;
 
 	sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
 	if (sock < 0)
 		return 0;
 
-	switch (sap->sa_family) {
-	case AF_INET:
-		if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
-			close(sock);
-			return 0;
-		}
-		break;
-	case AF_INET6:
-		if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
-			close(sock);
-			return 0;
-		}
-		break;
-	default:
-		errno = EAFNOSUPPORT;
-		return 0;
-	}
+        if (bind(sock, (struct sockaddr *)&caddr, clen) < 0) {
+                close(sock);
+                return 0;
+        }
 
 	if (connect(sock, sap, salen) < 0) {
 		close(sock);
@@ -1086,12 +1076,13 @@ static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t salen,
  * available; returns 1 and fills in an appropriate ANYADDR address
  * if a local address isn't available; otherwise, returns zero.
  */
-int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen,
+int nfs_callback_address(struct sockaddr* caddr, const socklen_t clen,
+			 const struct sockaddr *sap, const socklen_t salen,
 			 struct sockaddr *buf, socklen_t *buflen)
 {
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
 
-	if (nfs_ca_sockname(sap, salen, buf, buflen) == 0)
+	if (nfs_ca_sockname(caddr, clen, sap, salen, buf, buflen) == 0)
 		if (nfs_ca_gai(sap, salen, buf, buflen) == 0)
 			goto out_failed;
 
diff --git a/utils/mount/network.h b/utils/mount/network.h
index 075093d..5413686 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -39,29 +39,32 @@ typedef struct {
 static const struct timeval TIMEOUT = { 20, 0 };
 static const struct timeval RETRY_TIMEOUT = { 3, 0 };
 
-int probe_bothports(clnt_addr_t *, clnt_addr_t *);
+int probe_bothports(struct sockaddr* caddr, const socklen_t clen, clnt_addr_t *, clnt_addr_t *);
 int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
-			struct pmap *, const struct sockaddr *,
+			struct pmap *,
+                        struct sockaddr *nfs_caddr,
+			const socklen_t nfs_clen, const struct sockaddr *,
 			const socklen_t, struct pmap *);
 int nfs_gethostbyname(const char *, struct sockaddr_in *);
+int nfs_call_umount(struct sockaddr *caddr, const socklen_t clen, clnt_addr_t *, dirpath *);
 int nfs_name_to_address(const char *, const sa_family_t,
 		struct sockaddr *, socklen_t *);
 int nfs_string_to_sockaddr(const char *, const size_t,
 			   struct sockaddr *, socklen_t *);
 int nfs_present_sockaddr(const struct sockaddr *,
 			 const socklen_t, char *, const size_t);
-int nfs_callback_address(const struct sockaddr *, const socklen_t,
+int nfs_callback_address(struct sockaddr* caddr, const socklen_t clen,
+		const struct sockaddr *, const socklen_t,
 		struct sockaddr *, socklen_t *);
-int nfs_call_umount(clnt_addr_t *, dirpath *);
-int clnt_ping(struct sockaddr_in *, const unsigned long,
-		const unsigned long, const unsigned int,
-		struct sockaddr_in *);
+int clnt_ping(struct sockaddr* caddr, socklen_t clen,
+		struct sockaddr_in *, const unsigned long,
+		const unsigned long, const unsigned int);
 
 int start_statd(void);
 
 unsigned long nfsvers_to_mnt(const unsigned long);
 
-CLIENT *mnt_openclnt(clnt_addr_t *, int *);
+CLIENT *mnt_openclnt(struct sockaddr *caddr, const socklen_t clen, clnt_addr_t *, int *);
 void mnt_closeclnt(CLIENT *, int);
 
 #endif	/* _NFS_UTILS_MOUNT_NETWORK_H */
diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
index a2f318f..e06025d 100644
--- a/utils/mount/nfs4mount.c
+++ b/utils/mount/nfs4mount.c
@@ -420,9 +420,12 @@ int nfs4mount(const char *spec, const char *node, int flags,
 				data.proto == IPPROTO_UDP ? "udp" : "tcp",
 				ntohs(server_addr.sin_port));
 		}
-		client_addr.sin_family = 0;
-		client_addr.sin_addr.s_addr = 0;
-		clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
+		memset(&client_addr, 0, sizeof(client_addr));
+		if (ip_addr_in_opts) {
+			client_addr.sin_family = AF_INET;
+			client_addr.sin_addr.s_addr = inet_addr(ip_addr);
+		}
+		clnt_ping((struct sockaddr*)&client_addr, sizeof(client_addr), &server_addr, NFS_PROGRAM, 4, data.proto);
 		if (rpc_createerr.cf_stat == RPC_SUCCESS) {
 			if (!ip_addr_in_opts &&
 			    client_addr.sin_family != 0 &&
diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h
index 2becfb1..9fe1251 100644
--- a/utils/mount/nfs_mount.h
+++ b/utils/mount/nfs_mount.h
@@ -14,7 +14,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#define NFS_MOUNT_VERSION	6
+#define NFS_MOUNT_VERSION	7
 #define NFS_MAX_CONTEXT_LEN	256
 
 struct nfs2_fh {
@@ -45,6 +45,7 @@ struct nfs_mount_data {
 	struct nfs3_fh	root;			/* 4 */
 	int		pseudoflavor;		/* 5 */
 	char    context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
+	struct sockaddr_storage clientaddr;		/* 7 */
 
 };
 
diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
index 6355681..8c01d58 100644
--- a/utils/mount/nfsmount.c
+++ b/utils/mount/nfsmount.c
@@ -122,17 +122,17 @@ nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg, mnt2res_t *mnt2res)
 }
 
 static int
-nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
-	       mntarg_t *mntarg, mntres_t *mntres)
+nfs_call_mount(clnt_addr_t *mnt_server, struct sockaddr *clientaddr, socklen_t clen,
+	       clnt_addr_t *nfs_server, mntarg_t *mntarg, mntres_t *mntres)
 {
 	CLIENT *clnt;
 	enum clnt_stat stat;
 	int msock;
 
-	if (!probe_bothports(mnt_server, nfs_server))
+	if (!probe_bothports(clientaddr, clen, mnt_server, nfs_server))
 		goto out_bad;
 
-	clnt = mnt_openclnt(mnt_server, &msock);
+	clnt = mnt_openclnt(clientaddr, clen, mnt_server, &msock);
 	if (!clnt)
 		goto out_bad;
 	/* make pointers in xdr_mountres3 NULL so
@@ -167,6 +167,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
 	      clnt_addr_t *nfs_server, char *new_opts, const int opt_size)
 {
 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+	struct sockaddr_in *clientaddr;
 	struct pmap *mnt_pmap = &mnt_server->pmap;
 	struct pmap *nfs_pmap = &nfs_server->pmap;
 	int len;
@@ -178,6 +179,10 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
 	data->flags = 0;
 	*bg = 0;
 
+	/* TODO:  Support IPv6 binding */
+	clientaddr = (struct sockaddr_in *)&data->clientaddr;
+	clientaddr->sin_family = AF_INET;
+
 	len = strlen(new_opts);
 	for (p=old_opts, opt_b=NULL; p && *p; p++) {
 		if (!opt_b)
@@ -252,6 +257,19 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
 			} else if (!strcmp(opt, "addr")) {
 				/* ignore */;
 				continue;
+#if NFS_MOUNT_VERSION >= 7
+			} else if (!strcmp(opt, "clientaddr")) {
+				char ip_addr[sizeof("255.255.255.255")];
+				if (strlen(opteq+1) >= sizeof(ip_addr)) {
+					printf(_("Invalid client address %s"),
+						 opteq+1);
+					goto bad_parameter;
+				}
+				strncpy(ip_addr,opteq+1, sizeof(ip_addr));
+				ip_addr[sizeof(ip_addr)-1] = '\0';
+
+				clientaddr->sin_addr.s_addr = inet_addr(ip_addr);
+#endif
  			} else if (sloppy)
 				continue;
 			else
@@ -673,8 +691,10 @@ nfsmount(const char *spec, const char *node, int flags,
 			if (t - prevt < 30)
 				sleep(30);
 
-			stat = nfs_call_mount(&mnt_server, &nfs_server,
-					      &dirname, &mntres);
+			stat = nfs_call_mount(&mnt_server,
+					      (struct sockaddr *)&data.clientaddr,
+					      sizeof(data.clientaddr),
+					      &nfs_server, &dirname, &mntres);
 			if (stat)
 				break;
 			memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
@@ -772,7 +792,8 @@ nfsmount(const char *spec, const char *node, int flags,
 					"not supported"),
 					progname, hostname, dirname);
 			/* server has registered us in rmtab, send umount */
-			nfs_call_umount(&mnt_server, &dirname);
+			nfs_call_umount((struct sockaddr *)&data.clientaddr, sizeof(data.clientaddr),
+					&mnt_server, &dirname);
 			goto fail;
 		}
 noauth_flavors:
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
index b2327e0..cb9edfd 100644
--- a/utils/mount/nfsumount.c
+++ b/utils/mount/nfsumount.c
@@ -25,6 +25,9 @@
 #include <sys/mount.h>
 #include <ctype.h>
 #include <pwd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "xcommon.h"
 #include "fstab.h"
@@ -147,6 +150,7 @@ static int do_nfs_umount23(const char *spec, char *opts)
 {
 	char *hostname;
 	char *dirname;
+	struct sockaddr_in caddr;
 	clnt_addr_t mnt_server = { &hostname, };
 	struct mntent mnt = { .mnt_opts = opts };
 	struct pmap *pmap = &mnt_server.pmap;
@@ -160,14 +164,37 @@ static int do_nfs_umount23(const char *spec, char *opts)
 	printf(_("host: %s, directory: %s\n"), hostname, dirname);
 #endif
 
-	if (opts && (p = strstr(opts, "addr="))) {
+	for (p = opts; (p = strstr(p, "addr=")); ) {
 		char *q;
 
+		/* This is a bit of a hack, but necessary to avoid using the
+		 * client address as server address. */
+		if (p >= opts + strlen("client") &&
+		    strncmp(p - strlen("client"), "client",
+		    	    strlen("client")) == 0) {
+			p += strlen("addr=");
+			continue;
+		}
+
 		free(hostname);
 		p += 5;
 		q = p;
 		while (*q && *q != ',') q++;
 		hostname = xstrndup(p,q-p);
+		break;
+	}
+
+	memset(&caddr, 0, sizeof(caddr));
+	if (opts && (p = strstr(opts, "clientaddr="))) {
+		char *q, *tmp;
+
+		p += 11;
+		q = p;
+		while (*q && *q != ',') q++;
+		tmp = xstrndup(p,q-p);
+		caddr.sin_family = AF_INET;
+		caddr.sin_addr.s_addr = inet_addr(tmp);
+		free(tmp);
 	}
 
 	if (opts && (p = strstr(opts, "mounthost="))) {
@@ -211,7 +238,7 @@ static int do_nfs_umount23(const char *spec, char *opts)
 		goto out;
 	}
 
-	if (!nfs_call_umount(&mnt_server, &dirname)) {
+	if (!nfs_call_umount((struct sockaddr*)&caddr, sizeof(caddr), &mnt_server, &dirname)) {
 		nfs_error(_("%s: Server failed to unmount '%s'"),
 				progname, spec);
 		result = EX_FAIL;
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 43791e6..54b5c94 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -184,11 +184,17 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
 	struct sockaddr_storage dummy;
 	struct sockaddr *my_addr = (struct sockaddr *)&dummy;
 	socklen_t my_len = sizeof(dummy);
+	struct sockaddr_storage dclient;
+	struct sockaddr *my_cl = (struct sockaddr *)&dclient;
+	socklen_t my_lenc = sizeof(dclient);
 
 	if (po_contains(options, "clientaddr") == PO_FOUND)
 		return 1;
 
-	nfs_callback_address(sap, salen, my_addr, &my_len);
+	memset(&dclient, 0, sizeof(dclient));
+	my_cl->sa_family = sap->sa_family;
+	
+	nfs_callback_address(my_cl, my_lenc, sap, salen, my_addr, &my_len);
 
 	return nfs_append_generic_address_option(my_addr, my_len,
 							"clientaddr", options);
@@ -322,8 +328,11 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
 	char *option, new_option[64];
 	clnt_addr_t mnt_server = { };
 	clnt_addr_t nfs_server = { };
+        struct sockaddr_in clientaddr;
 	int p;
 
+        memset(&clientaddr, 0, sizeof(clientaddr));
+        
 	options = po_split(str);
 	if (!options) {
 		errno = EFAULT;
@@ -339,6 +348,12 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
 	} else
 		goto err;
 
+        option = po_get(options, "clientaddr");
+        if (option) {
+           clientaddr.sin_family = AF_INET;
+           clientaddr.sin_addr.s_addr = inet_addr(option);
+        }
+        
 	option = po_get(options, "mountaddr");
 	if (option) {
 		mnt_server.saddr.sin_family = AF_INET;
@@ -407,7 +422,7 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
 	po_remove_all(options, "tcp");
 	po_remove_all(options, "udp");
 
-	if (!probe_bothports(&mnt_server, &nfs_server)) {
+	if (!probe_bothports((struct sockaddr*)&clientaddr, sizeof(clientaddr), &mnt_server, &nfs_server)) {
 		errno = ESPIPE;
 		goto err;
 	}
diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c
index 2695c51..2545ed6 100644
--- a/utils/showmount/showmount.c
+++ b/utils/showmount/showmount.c
@@ -55,6 +55,7 @@ static struct option longopts[] =
 	{ "all", 0, 0, 'a' },
 	{ "directories", 0, 0, 'd' },
 	{ "exports", 0, 0, 'e' },
+	{ "local_ip", 1, 0, 'i' },
 	{ "no-headers", 0, &headers, 0 },
 	{ "version", 0, 0, 'v' },
 	{ "help", 0, 0, 'h' },
@@ -74,7 +75,7 @@ static void usage(FILE *fp, int n)
 {
 	fprintf(fp, "Usage: %s [-adehv]\n", program_name);
 	fprintf(fp, "       [--all] [--directories] [--exports]\n");
-	fprintf(fp, "       [--no-headers] [--help] [--version] [host]\n");
+	fprintf(fp, "       [--no-headers] [--help] [--version] [--local_ip a.b.c.d] [host]\n");
 	exit(n);
 }
 
@@ -190,14 +191,35 @@ done:
  *
  * Supports only AF_INET server addresses.
  */
-static CLIENT *nfs_get_mount_client(const char *hostname)
+static CLIENT *nfs_get_mount_client(const char *hostname, const char* local_ip)
 {
 	struct hostent *hp;
 	struct sockaddr_in server_addr;
+	struct sockaddr_in local_addr;
 	struct timeval pertry_timeout;
 	CLIENT *mclient = NULL;
 	int ret, msock;
 
+	if (local_ip) {
+		if (inet_aton(local_ip, &local_addr.sin_addr)) {
+			server_addr.sin_family = AF_INET;
+		}
+		else {
+			if ((hp = gethostbyname(hostname)) == NULL) {
+				fprintf(stderr, "%s: can't get address for %s\n",
+					program_name, hostname);
+				exit(1);
+			}
+			server_addr.sin_family = AF_INET;
+			memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
+		}
+	}
+	else {
+		local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+		local_addr.sin_port = 0;
+		local_addr.sin_family = AF_INET;
+	}
+	
 	if (inet_aton(hostname, &server_addr.sin_addr)) {
 		server_addr.sin_family = AF_INET;
 	}
@@ -213,7 +235,8 @@ static CLIENT *nfs_get_mount_client(const char *hostname)
 
 	msock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 	if (msock != -1) {
-		if (nfs_getport_ping((struct sockaddr *)&server_addr,
+		if (nfs_getport_ping((struct sockaddr*)(&local_addr), sizeof(local_addr),
+				     (struct sockaddr *)&server_addr,
 					sizeof(server_addr), MOUNTPROG,
 					MOUNTVERS, IPPROTO_TCP)) {
 			ret = connect_nb(msock, &server_addr, 0);
@@ -228,7 +251,8 @@ static CLIENT *nfs_get_mount_client(const char *hostname)
 	}
 
 	if (!mclient) {
-		if (nfs_getport_ping((struct sockaddr *)&server_addr,
+		if (nfs_getport_ping((struct sockaddr*)&local_addr, sizeof(local_addr),
+				     (struct sockaddr *)&server_addr,
 					sizeof(server_addr), MOUNTPROG,
 					MOUNTVERS, IPPROTO_UDP)) {
 			clnt_pcreateerror("showmount");
@@ -253,6 +277,7 @@ int main(int argc, char **argv)
 {
 	char hostname_buf[MAXHOSTLEN];
 	char *hostname;
+	char* local_ip = NULL;
 	enum clnt_stat clnt_stat;
 	struct timeval total_timeout;
 	int c;
@@ -267,7 +292,7 @@ int main(int argc, char **argv)
 	char **dumpv;
 
 	program_name = argv[0];
-	while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
+	while ((c = getopt_long(argc, argv, "adehvi:", longopts, NULL)) != EOF) {
 		switch (c) {
 		case 'a':
 			aflag = 1;
@@ -281,6 +306,9 @@ int main(int argc, char **argv)
 		case 'h':
 			usage(stdout, 0);
 			break;
+		case 'i':
+			local_ip = strdup(optarg);
+			break;
 		case 'v':
 			printf("%s\n", version);
 			exit(0);
@@ -326,7 +354,7 @@ int main(int argc, char **argv)
 		break;
 	}
 
-	mclient = nfs_get_mount_client(hostname);
+	mclient = nfs_get_mount_client(hostname, local_ip);
 	mclient->cl_auth = authunix_create_default();
 	total_timeout.tv_sec = TOTAL_TIMEOUT;
 	total_timeout.tv_usec = 0;

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-22  1:01 PATCH: Support binding to a local IPv4 address when mounting a server Ben Greear
@ 2009-01-22  2:38 ` Chuck Lever
  2009-01-22  5:35   ` Ben Greear
  0 siblings, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2009-01-22  2:38 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-nfs, Patrick McHardy

A handful of generic comments.

1.  This needs to be broken into smaller patches before submission;  
preferably before you submit another version for review.  Take a look  
at the linux-nfs@vger.kernel.org archives to see how we handle large  
changes like this.

2.  You should support local addresses only in the text-based path  
(utils/mount/stropts.c) and not in the legacy paths (utils/mount/ 
nfs[4]mount.c).  I don't think we're ever going to allow a version 7  
of the mount data structure.

3.  There are some umount-related changes coming up for IPv6 that will  
touch the umount paths here; that may require some changes in your  
modifications.

4.  This needs to have support for a new mount option in the kernel  
(not a command-line option to the mount.nfs command, as you have  
implemented) to handle passing the address to the kernel.


On Jan 21, 2009, at 8:01 PM, Ben Greear wrote:

>
> The attached patch allows one to mount NFSv3 (should work with v4 as  
> well,
> but not tested as of today) servers and specify the local IP address.
>
> This facilitates mounting on a particular interface if you have a
> multi-homed box.
>
> I have a matching patch to the kernel that I will be porting forward  
> to
> the latest 2.6.29-rcX branch next.
>
> Please note that Patrick gets a lot of credit for helping me with
> this patch in the past, though I have somewhat butchered his previous
> work so blame can fall directly to me :)
>
> Please let me know if this patch or something like it would be welcome
> in the nfs-utils tree.  Suggestions for improvement are welcome.
>
> Signed-Off-By: Ben Greear<greearb@candelatech.com>
>
> Thanks,
> Ben
>
> -- 
> Ben Greear <greearb@candelatech.com>
> Candela Technologies Inc  http://www.candelatech.com
>
> diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h
> index fc26f4e..03dea21 100644
> --- a/support/include/nfs/nfs.h
> +++ b/support/include/nfs/nfs.h
> @@ -71,7 +71,7 @@ struct nfsctl_client {
>  */
> #include <linux/version.h>
> #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,70)
> -# define __nfsd_dev_t  __kernel_old_dev_t
> +# define __nfsd_dev_t  unsigned short /*__kernel_old_dev_t -BEN*/
> #else
> # define __nfsd_dev_t  __kernel_dev_t
> #endif
> diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
> index 1529d44..265bbbe 100644
> --- a/support/include/nfsrpc.h
> +++ b/support/include/nfsrpc.h
> @@ -62,7 +62,8 @@ extern unsigned short	nfs_getportbynumber(const  
> rpcprog_t program,
> /*
>  * Acquire an RPC CLIENT *
>  */
> -extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
> +extern CLIENT *nfs_get_rpcclient(struct sockaddr* caddr, const  
> socklen_t clen,
> +				 const struct sockaddr *,
> 				const socklen_t, const unsigned short,
> 				const rpcprog_t, const rpcvers_t,
> 				struct timeval *);
> @@ -83,7 +84,8 @@ extern int		nfs_universal2port(const char *);
>  * number of the service on a remote post, and sends a NULL
>  * request to determine if the service is responding to requests
>  */
> -extern int		nfs_getport_ping(struct sockaddr *sap,
> +extern int		nfs_getport_ping(struct sockaddr* caddr, const  
> socklen_t clen,
> +				struct sockaddr *sap,
> 				const socklen_t salen,
> 				const rpcprog_t program,
> 				const rpcvers_t version,
> @@ -93,7 +95,8 @@ extern int		nfs_getport_ping(struct sockaddr *sap,
>  * Generic function that maps an RPC service tuple to an IP port
>  * number of the service on a remote host
>  */
> -extern unsigned short	nfs_getport(const struct sockaddr *,
> +extern unsigned short	nfs_getport(struct sockaddr* caddr, const  
> socklen_t clen,
> +				const struct sockaddr *,
> 				const socklen_t, const rpcprog_t,
> 				const rpcvers_t, const unsigned short);
>
> @@ -120,7 +123,8 @@ extern unsigned short	nfs_rpcb_getaddr(const  
> struct sockaddr *,
> /*
>  * Function to invoke a portmap GETPORT request
>  */
> -extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
> +extern unsigned long	nfs_pmap_getport(struct sockaddr* caddr, const  
> socklen_t clen,
> +				const struct sockaddr_in *,
> 				const unsigned short,
> 				const unsigned long,
> 				const unsigned long,
> @@ -131,7 +135,8 @@ extern unsigned long	nfs_pmap_getport(const  
> struct sockaddr_in *,
>  * Contact a remote RPC service to discover whether it is responding
>  * to requests.
>  */
> -extern int		nfs_rpc_ping(const struct sockaddr *sap,
> +extern int		nfs_rpc_ping(struct sockaddr* caddr, const socklen_t  
> clen,
> +				const struct sockaddr *sap,
> 				const socklen_t salen,
> 				const rpcprog_t program,
> 				const rpcvers_t version,
> diff --git a/support/nfs/getport.c b/support/nfs/getport.c
> index 47824a2..ec099ba 100644
> --- a/support/nfs/getport.c
> +++ b/support/nfs/getport.c
> @@ -190,7 +190,9 @@ static void nfs_gp_set_port(struct sockaddr  
> *sap, const in_port_t port)
>  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set  
> to
>  * reflect the error.
>  */
> -static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
> +static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr* caddr,
> +				     const socklen_t clen,
> +				     const struct sockaddr *sap,
> 				     const socklen_t salen,
> 				     const unsigned short transport,
> 				     const rpcvers_t version,
> @@ -203,7 +205,7 @@ static CLIENT *nfs_gp_get_rpcbclient(const  
> struct sockaddr *sap,
> 	memcpy(saddr, sap, (size_t)salen);
> 	nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport));
>
> -	return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog,
> +	return nfs_get_rpcclient(caddr, clen, saddr, salen, transport,  
> rpcb_prog,
> 					version, timeout);
> }
>
> @@ -660,7 +662,8 @@ static unsigned short nfs_gp_getport(CLIENT  
> *client,
>  * Returns 1 if the remote service responded without an error;  
> otherwise
>  * zero.
>  */
> -int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
> +int nfs_rpc_ping(struct sockaddr* caddr, const socklen_t clen,
> +		 const struct sockaddr *sap, const socklen_t salen,
> 		 const rpcprog_t program, const rpcvers_t version,
> 		 const unsigned short protocol, const struct timeval *timeout)
> {
> @@ -671,7 +674,7 @@ int nfs_rpc_ping(const struct sockaddr *sap,  
> const socklen_t salen,
> 	if (timeout != NULL)
> 		tout = *timeout;
>
> -	client = nfs_get_rpcclient(sap, salen, protocol, program, version,  
> &tout);
> +	client = nfs_get_rpcclient(caddr, clen, sap, salen, protocol,  
> program, version, &tout);
> 	if (client != NULL) {
> 		result = nfs_gp_ping(client, tout);
> 		CLNT_DESTROY(client);
> @@ -720,7 +723,8 @@ int nfs_rpc_ping(const struct sockaddr *sap,  
> const socklen_t salen,
>  * AF_INET6 at all.  The rpcbind socket is re-used in an attempt to  
> keep the
>  * overall number of consumed ephemeral ports low.
>  */
> -unsigned short nfs_getport(const struct sockaddr *sap,
> +unsigned short nfs_getport(struct sockaddr* caddr, const socklen_t  
> clen,
> +			   const struct sockaddr *sap,
> 			   const socklen_t salen,
> 			   const rpcprog_t program,
> 			   const rpcvers_t version,
> @@ -730,7 +734,7 @@ unsigned short nfs_getport(const struct sockaddr  
> *sap,
> 	unsigned short port = 0;
> 	CLIENT *client;
>
> -	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
> +	client = nfs_gp_get_rpcbclient(caddr, clen, sap, salen, protocol,
> 						default_rpcb_version, &timeout);
> 	if (client != NULL) {
> 		port = nfs_gp_getport(client, sap, salen, program,
> @@ -759,7 +763,8 @@ unsigned short nfs_getport(const struct sockaddr  
> *sap,
>  * if both the query and the ping were successful; otherwise zero.
>  * rpccreateerr is set to reflect the underlying cause of the error.
>  */
> -int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
> +int nfs_getport_ping(struct sockaddr* caddr, const socklen_t clen,
> +		     struct sockaddr *sap, const socklen_t salen,
> 		     const rpcprog_t program, const rpcvers_t version,
> 		     const unsigned short protocol)
> {
> @@ -768,7 +773,7 @@ int nfs_getport_ping(struct sockaddr *sap, const  
> socklen_t salen,
> 	CLIENT *client;
> 	int result = 0;
> 	
> -	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
> +	client = nfs_gp_get_rpcbclient(caddr, clen, sap, salen, protocol,
> 						default_rpcb_version, &timeout);
> 	if (client != NULL) {
> 		port = nfs_gp_getport(client, sap, salen, program,
> @@ -784,7 +789,7 @@ int nfs_getport_ping(struct sockaddr *sap, const  
> socklen_t salen,
> 		memcpy(saddr, sap, (size_t)salen);
> 		nfs_gp_set_port(saddr, htons(port));
>
> -		client = nfs_get_rpcclient(saddr, salen, protocol,
> +		client = nfs_get_rpcclient(caddr, clen, saddr, salen, protocol,
> 						program, version, &timeout);
> 		if (client != NULL) {
> 			result = nfs_gp_ping(client, timeout);
> @@ -836,7 +841,7 @@ unsigned short nfs_getlocalport(const rpcprot_t  
> program,
> {
> 	struct sockaddr_storage address;
> 	struct sockaddr *lb_addr = (struct sockaddr *)&address;
> -	socklen_t lb_len = sizeof(lb_addr);
> +	socklen_t lb_len = sizeof(*lb_addr);
> 	unsigned short port = 0;
>
> #ifdef NFS_GP_LOCAL
> @@ -864,7 +869,23 @@ unsigned short nfs_getlocalport(const rpcprot_t  
> program,
>
> 	if (port == 0) {
> 		if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
> -			port = nfs_getport(lb_addr, lb_len,
> +			struct sockaddr_in sin = {
> +				.sin_family		= AF_INET,
> +				.sin_addr.s_addr	= htonl(INADDR_ANY),
> +			};
> +			struct sockaddr_in6 sin6 = {
> +				.sin6_family		= AF_INET6,
> +				.sin6_addr		= IN6ADDR_ANY_INIT,
> +			};
> +			struct sockaddr* caddr = (struct sockaddr*)(&sin);
> +			int ln = sizeof(sin);
> +
> +			if (lb_addr->sa_family == AF_INET6) {
> +				caddr = (struct sockaddr*)(&sin6);
> +				ln = sizeof(sin6);
> +			}
> +
> +			port = nfs_getport(caddr, ln, lb_addr, lb_len,
> 						program, version, protocol);
> 		} else
> 			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
> @@ -983,7 +1004,8 @@ unsigned short nfs_rpcb_getaddr(const struct  
> sockaddr *sap,
>  *  3.	This version shares code with the rpcbindv3 and rpcbindv4 query
>  *	functions.  It can use a TI-RPC generated CLIENT.
>  */
> -unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
> +unsigned long nfs_pmap_getport(struct sockaddr* caddr, const  
> socklen_t clen,
> +			       const struct sockaddr_in *sin,
> 			       const unsigned short transport,
> 			       const unsigned long program,
> 			       const unsigned long version,
> @@ -1002,7 +1024,7 @@ unsigned long nfs_pmap_getport(const struct  
> sockaddr_in *sin,
> 	if (timeout != NULL)
> 		tout = *timeout;
>
> -	client = nfs_gp_get_rpcbclient((struct sockaddr *)sin,
> +	client = nfs_gp_get_rpcbclient(caddr, clen, (struct sockaddr *)sin,
> 					(socklen_t)sizeof(*sin),
> 					transport, PMAPVERS, &tout);
> 	if (client != NULL) {
> diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
> index 82ba818..57c96c8 100644
> --- a/support/nfs/rpc_socket.c
> +++ b/support/nfs/rpc_socket.c
> @@ -149,28 +149,10 @@ static CLIENT *nfs_get_localclient(const  
> struct sockaddr *sap,
>  * Returns zero on success, or returns -1 on error.  errno is
>  * set to reflect the nature of the error.
>  */
> -static int nfs_bind(const int sock, const sa_family_t family)
> +static int nfs_bind(struct sockaddr* caddr, const socklen_t clen,
> +		    const int sock, const sa_family_t family)
> {
> -	struct sockaddr_in sin = {
> -		.sin_family		= AF_INET,
> -		.sin_addr.s_addr	= htonl(INADDR_ANY),
> -	};
> -	struct sockaddr_in6 sin6 = {
> -		.sin6_family		= AF_INET6,
> -		.sin6_addr		= IN6ADDR_ANY_INIT,
> -	};
> -
> -	switch (family) {
> -	case AF_INET:
> -		return bind(sock, (struct sockaddr *)&sin,
> -					(socklen_t)sizeof(sin));
> -	case AF_INET6:
> -		return bind(sock, (struct sockaddr *)&sin6,
> -					(socklen_t)sizeof(sin6));
> -	}
> -
> -	errno = EAFNOSUPPORT;
> -	return -1;
> +	return bind(sock, caddr, clen);
> }
>
> /*
> @@ -255,7 +237,8 @@ done:
>  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
>  * rpc_createerr.cf_stat is set to reflect the error.
>  */
> -static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
> +static CLIENT *nfs_get_udpclient(struct sockaddr* caddr, const  
> socklen_t clen,
> +				 const struct sockaddr *sap,
> 				 const socklen_t salen,
> 				 const rpcprog_t program,
> 				 const rpcvers_t version,
> @@ -286,7 +269,7 @@ static CLIENT *nfs_get_udpclient(const struct  
> sockaddr *sap,
> 		return NULL;
> 	}
>
> -	ret = nfs_bind(sock, sap->sa_family);
> +	ret = nfs_bind(caddr, clen, sock, sap->sa_family);
> 	if (ret < 0) {
> 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
> 		rpc_createerr.cf_error.re_errno = errno;
> @@ -331,7 +314,8 @@ static CLIENT *nfs_get_udpclient(const struct  
> sockaddr *sap,
>  * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
>  * error.
>  */
> -static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
> +static CLIENT *nfs_get_tcpclient(struct sockaddr* caddr, const  
> socklen_t clen,
> +				 const struct sockaddr *sap,
> 				 const socklen_t salen,
> 				 const rpcprog_t program,
> 				 const rpcvers_t version,
> @@ -362,7 +346,7 @@ static CLIENT *nfs_get_tcpclient(const struct  
> sockaddr *sap,
> 		return NULL;
> 	}
>
> -	ret = nfs_bind(sock, sap->sa_family);
> +	ret = nfs_bind(caddr, clen, sock, sap->sa_family);
> 	if (ret < 0) {
> 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
> 		rpc_createerr.cf_error.re_errno = errno;
> @@ -413,7 +397,9 @@ static CLIENT *nfs_get_tcpclient(const struct  
> sockaddr *sap,
>  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set  
> to
>  * reflect the error.
>  */
> -CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
> +CLIENT *nfs_get_rpcclient(struct sockaddr *caddr,
> +			  const socklen_t clen,
> +			  const struct sockaddr *sap,
> 			  const socklen_t salen,
> 			  const unsigned short transport,
> 			  const rpcprog_t program,
> @@ -446,10 +432,10 @@ CLIENT *nfs_get_rpcclient(const struct  
> sockaddr *sap,
>
> 	switch (transport) {
> 	case IPPROTO_TCP:
> -		return nfs_get_tcpclient(sap, salen, program, version, timeout);
> +		return nfs_get_tcpclient(caddr, clen, sap, salen, program,  
> version, timeout);
> 	case 0:
> 	case IPPROTO_UDP:
> -		return nfs_get_udpclient(sap, salen, program, version, timeout);
> +		return nfs_get_udpclient(caddr, clen, sap, salen, program,  
> version, timeout);
> 	}
>
> 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
> diff --git a/utils/mount/network.c b/utils/mount/network.c
> index d262e94..5fb52f6 100644
> --- a/utils/mount/network.c
> +++ b/utils/mount/network.c
> @@ -428,30 +428,27 @@ out:
>  *
>  * The caller should check rpc_createerr to determine the cause of  
> any error.
>  */
> -static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
> -			unsigned int timeout, int resvp, int conn)
> +static int get_socket(struct sockaddr *caddr, const socklen_t clen,
> +		      struct sockaddr_in *saddr,
> +		      unsigned int p_prot, unsigned int timeout, int resvp,
> +		      int conn)
> {
> 	int so, cc, type;
> -	struct sockaddr_in laddr;
> -	socklen_t namelen = sizeof(laddr);
>
> 	type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
> 	if ((so = socket (AF_INET, type, p_prot)) < 0)
> 		goto err_socket;
>
> -	laddr.sin_family = AF_INET;
> -	laddr.sin_port = 0;
> -	laddr.sin_addr.s_addr = htonl(INADDR_ANY);
> 	if (resvp) {
> -		if (bindresvport(so, &laddr) < 0)
> +		if (bindresvport(so, (struct sockaddr_in*)caddr) < 0)
> 			goto err_bindresvport;
> 	} else {
> -		cc = bind(so, (struct sockaddr *)&laddr, namelen);
> +		cc = bind(so, caddr, clen);
> 		if (cc < 0)
> 			goto err_bind;
> 	}
> 	if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
> -		cc = connect_to(so, (struct sockaddr *)saddr, namelen,
> +		cc = connect_to(so, (struct sockaddr *)saddr, sizeof(*saddr),
> 				timeout);
> 		if (cc < 0)
> 			goto err_connect;
> @@ -473,9 +470,9 @@ err_bindresvport:
> 	rpc_createerr.cf_error.re_errno = errno;
> 	if (verbose) {
> 		nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
> -				" (%s)\n"),
> +				" (%s)  caddr->family: %i\n"),
> 			progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
> -			errno, strerror(errno));
> +			  errno, strerror(errno), (int)(caddr->sa_family));
> 	}
> 	close(so);
> 	return RPC_ANYSOCK;
> @@ -519,11 +516,12 @@ static void nfs_pp_debug(const struct sockaddr  
> *sap, const socklen_t salen,
> 	}
>
> 	fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d 
> \n"),
> -			progname, buf, program, version,
> -			(protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
> -			port);
> +		progname, buf, program, version,
> +		(protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
> +		port);
> }
>
> +
> /*
>  * Use the portmapper to discover whether or not the service we want  
> is
>  * available. The lists 'versions' and 'protos' define ordered  
> sequences
> @@ -538,7 +536,8 @@ static void nfs_pp_debug(const struct sockaddr  
> *sap, const socklen_t salen,
>  * If an error occurs or the requested service isn't available, zero  
> is
>  * returned; rpccreateerr.cf_stat is set to reflect the nature of  
> the error.
>  */
> -static int nfs_probe_port(const struct sockaddr *sap, const  
> socklen_t salen,
> +static int nfs_probe_port(struct sockaddr *caddr, const socklen_t  
> clen,
> +			  const struct sockaddr *sap, const socklen_t salen,
> 			  struct pmap *pmap, const unsigned long *versions,
> 			  const unsigned int *protos)
> {
> @@ -556,13 +555,12 @@ static int nfs_probe_port(const struct  
> sockaddr *sap, const socklen_t salen,
> 	rpc_createerr.cf_stat = 0;
>
> 	for (;;) {
> -		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
> +		p_port = nfs_getport(caddr, clen, saddr, salen, prog, *p_vers,  
> *p_prot);
> 		if (p_port) {
> 			if (!port || port == p_port) {
> 				nfs_set_port(saddr, p_port);
> -				nfs_pp_debug(saddr, salen, prog, *p_vers,
> -						*p_prot, p_port);
> -				if (nfs_rpc_ping(saddr, salen, prog,
> +				nfs_pp_debug(saddr, salen, prog, *p_vers, *p_prot, p_port);
> +				if (nfs_rpc_ping(caddr, clen, saddr, salen, prog,
> 							*p_vers, *p_prot, NULL))
> 					goto out_ok;
> 			}
> @@ -613,17 +611,18 @@ out_ok:
>  * If an error occurs or the requested service isn't available, zero  
> is
>  * returned; rpccreateerr.cf_stat is set to reflect the nature of  
> the error.
>  */
> -static int nfs_probe_nfsport(const struct sockaddr *sap, const  
> socklen_t salen,
> -				struct pmap *pmap)
> +static int nfs_probe_nfsport(struct sockaddr *caddr, const  
> socklen_t clen,
> +                             const struct sockaddr *sap, const  
> socklen_t salen,
> +                             struct pmap *pmap)
> {
> 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
> 		return 1;
>
> 	if (nfs_mount_data_version >= 4)
> -		return nfs_probe_port(sap, salen, pmap,
> +		return nfs_probe_port(caddr, clen, sap, salen, pmap,
> 					probe_nfs3_first, probe_tcp_first);
> 	else
> -		return nfs_probe_port(sap, salen, pmap,
> +		return nfs_probe_port(caddr, clen, sap, salen, pmap,
> 					probe_nfs2_only, probe_udp_only);
> }
>
> @@ -640,17 +639,18 @@ static int nfs_probe_nfsport(const struct  
> sockaddr *sap, const socklen_t salen,
>  * If an error occurs or the requested service isn't available, zero  
> is
>  * returned; rpccreateerr.cf_stat is set to reflect the nature of  
> the error.
>  */
> -static int nfs_probe_mntport(const struct sockaddr *sap, const  
> socklen_t salen,
> +static int nfs_probe_mntport(struct sockaddr *caddr, const  
> socklen_t clen,
> +                             const struct sockaddr *sap, const  
> socklen_t salen,
> 				struct pmap *pmap)
> {
> 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
> 		return 1;
>
> 	if (nfs_mount_data_version >= 4)
> -		return nfs_probe_port(sap, salen, pmap,
> +		return nfs_probe_port(caddr, clen, sap, salen, pmap,
> 					probe_mnt3_first, probe_udp_first);
> 	else
> -		return nfs_probe_port(sap, salen, pmap,
> +		return nfs_probe_port(caddr, clen, sap, salen, pmap,
> 					probe_mnt1_first, probe_udp_only);
> }
>
> @@ -666,13 +666,15 @@ static int nfs_probe_mntport(const struct  
> sockaddr *sap, const socklen_t salen,
> static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
> 			const socklen_t mnt_salen,
> 			struct pmap *mnt_pmap,
> +			struct sockaddr *nfs_caddr,
> +			const socklen_t nfs_clen,
> 			const struct sockaddr *nfs_saddr,
> 			const socklen_t nfs_salen,
> 			struct pmap *nfs_pmap)
> {
> -	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
> +	if (!nfs_probe_nfsport(nfs_caddr, nfs_clen, nfs_saddr, nfs_salen,  
> nfs_pmap))
> 		return 0;
> -	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
> +	return nfs_probe_mntport(nfs_caddr, nfs_clen, mnt_saddr,  
> mnt_salen, mnt_pmap);
> }
>
> /**
> @@ -693,6 +695,8 @@ static int nfs_probe_version_fixed(const struct  
> sockaddr *mnt_saddr,
> int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
> 			const socklen_t mnt_salen,
> 			struct pmap *mnt_pmap,
> +                        struct sockaddr *nfs_caddr,
> +			const socklen_t nfs_clen,
> 			const struct sockaddr *nfs_saddr,
> 			const socklen_t nfs_salen,
> 			struct pmap *nfs_pmap)
> @@ -707,6 +711,7 @@ int nfs_probe_bothports(const struct sockaddr  
> *mnt_saddr,
>
> 	if (nfs_pmap->pm_vers)
> 		return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap,
> +					       nfs_caddr, nfs_clen,
> 					       nfs_saddr, nfs_salen, nfs_pmap);
>
> 	memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
> @@ -716,9 +721,9 @@ int nfs_probe_bothports(const struct sockaddr  
> *mnt_saddr,
>
> 	for (; *probe_vers; probe_vers++) {
> 		nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
> -		if (nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap) != 0) {
> +		if (nfs_probe_nfsport(nfs_caddr, nfs_clen, nfs_saddr, nfs_salen,  
> nfs_pmap) != 0) {
> 			mnt_pmap->pm_vers = *probe_vers;
> -			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
> +			if (nfs_probe_mntport(nfs_caddr, nfs_clen, mnt_saddr, mnt_salen,  
> mnt_pmap) != 0)
> 				return 1;
> 			memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
> 		}
> @@ -748,11 +753,12 @@ int nfs_probe_bothports(const struct sockaddr  
> *mnt_saddr,
>  * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect
>  * the nature of the error.
>  */
> -int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
> +int probe_bothports(struct sockaddr* caddr, const socklen_t clen,  
> clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
> {
> 	return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr,
> 					sizeof(mnt_server->saddr),
> 					&mnt_server->pmap,
> +				 	caddr, clen,
> 					(struct sockaddr *)&nfs_server->saddr,
> 					sizeof(nfs_server->saddr),
> 					&nfs_server->pmap);
> @@ -764,9 +770,14 @@ static int nfs_probe_statd(void)
> 		.sin_family		= AF_INET,
> 		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
> 	};
> +	struct sockaddr_in laddr = {
> +		.sin_family		= AF_INET,
> +		.sin_addr.s_addr	= htonl(INADDR_ANY),
> +	};
> 	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
>
> -	return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
> +	return nfs_getport_ping((struct sockaddr*)&laddr, sizeof(laddr),
> +				(struct sockaddr *)&addr, sizeof(addr),
> 				program, (rpcvers_t)1, IPPROTO_UDP);
> }
>
> @@ -820,7 +831,7 @@ int start_statd(void)
>  * Note that a side effect of calling this function is that  
> rpccreateerr
>  * is set.
>  */
> -int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
> +int nfs_call_umount(struct sockaddr *caddr, const socklen_t clen,  
> clnt_addr_t *mnt_server, dirpath *argp)
> {
> 	struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
> 	socklen_t salen = sizeof(mnt_server->saddr);
> @@ -829,9 +840,9 @@ int nfs_call_umount(clnt_addr_t *mnt_server,  
> dirpath *argp)
> 	enum clnt_stat res = 0;
> 	int msock;
>
> -	if (!nfs_probe_mntport(sap, salen, pmap))
> +	if (!nfs_probe_mntport(caddr, clen, sap, salen, pmap))
> 		return 0;
> -	clnt = mnt_openclnt(mnt_server, &msock);
> +	clnt = mnt_openclnt(caddr, clen, mnt_server, &msock);
> 	if (!clnt)
> 		return 0;
> 	res = clnt_call(clnt, MOUNTPROC_UMNT,
> @@ -852,14 +863,14 @@ int nfs_call_umount(clnt_addr_t *mnt_server,  
> dirpath *argp)
>  *
>  * Returns an active handle for the remote's mountd service
>  */
> -CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
> +CLIENT *mnt_openclnt(struct sockaddr *caddr, const socklen_t clen,  
> clnt_addr_t *mnt_server, int *msock)
> {
> 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
> 	struct pmap *mnt_pmap = &mnt_server->pmap;
> 	CLIENT *clnt = NULL;
>
> 	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
> -	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
> +	*msock = get_socket(caddr, clen, mnt_saddr, mnt_pmap->pm_prot,  
> MOUNT_TIMEOUT,
> 				TRUE, FALSE);
> 	if (*msock == RPC_ANYSOCK) {
> 		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
> @@ -925,9 +936,9 @@ void mnt_closeclnt(CLIENT *clnt, int msock)
>  *
>  * Returns one if successful, otherwise zero.
>  */
> -int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
> -		const unsigned long vers, const unsigned int prot,
> -		struct sockaddr_in *caddr)
> +int clnt_ping(struct sockaddr* caddr, socklen_t clen,
> +	      struct sockaddr_in *saddr, const unsigned long prog,
> +	      const unsigned long vers, const unsigned int prot)
> {
> 	CLIENT *clnt = NULL;
> 	int sock, stat;
> @@ -935,7 +946,7 @@ int clnt_ping(struct sockaddr_in *saddr, const  
> unsigned long prog,
> 	struct sockaddr dissolve;
>
> 	rpc_createerr.cf_stat = stat = 0;
> -	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
> +	sock = get_socket(caddr, clen, saddr, prot, CONNECT_TIMEOUT,  
> FALSE, TRUE);
> 	if (sock == RPC_ANYSOCK) {
> 		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
> 			/*
> @@ -949,9 +960,8 @@ int clnt_ping(struct sockaddr_in *saddr, const  
> unsigned long prog,
>
> 	if (caddr) {
> 		/* Get the address of our end of this connection */
> -		socklen_t len = sizeof(*caddr);
> -		if (getsockname(sock, caddr, &len) != 0)
> -			caddr->sin_family = 0;
> +		if (getsockname(sock, caddr, &clen) != 0)
> +			((struct sockaddr_in*)(caddr))->sin_family = 0;
> 	}
>
> 	switch(prot) {
> @@ -1003,40 +1013,20 @@ int clnt_ping(struct sockaddr_in *saddr,  
> const unsigned long prog,
>  * This conserves the ephemeral port number space, helping reduce  
> failed
>  * socket binds during mount storms.
>  */
> -static int nfs_ca_sockname(const struct sockaddr *sap, const  
> socklen_t salen,
> +static int nfs_ca_sockname(const struct sockaddr* caddr, const  
> socklen_t clen,
> +                           const struct sockaddr *sap, const  
> socklen_t salen,
> 			   struct sockaddr *buf, socklen_t *buflen)
> {
> -	struct sockaddr_in sin = {
> -		.sin_family		= AF_INET,
> -		.sin_addr.s_addr	= htonl(INADDR_ANY),
> -	};
> -	struct sockaddr_in6 sin6 = {
> -		.sin6_family		= AF_INET6,
> -		.sin6_addr		= IN6ADDR_ANY_INIT,
> -	};
> 	int sock;
>
> 	sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
> 	if (sock < 0)
> 		return 0;
>
> -	switch (sap->sa_family) {
> -	case AF_INET:
> -		if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
> -			close(sock);
> -			return 0;
> -		}
> -		break;
> -	case AF_INET6:
> -		if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
> -			close(sock);
> -			return 0;
> -		}
> -		break;
> -	default:
> -		errno = EAFNOSUPPORT;
> -		return 0;
> -	}
> +        if (bind(sock, (struct sockaddr *)&caddr, clen) < 0) {
> +                close(sock);
> +                return 0;
> +        }
>
> 	if (connect(sock, sap, salen) < 0) {
> 		close(sock);
> @@ -1086,12 +1076,13 @@ static int nfs_ca_gai(const struct sockaddr  
> *sap, const socklen_t salen,
>  * available; returns 1 and fills in an appropriate ANYADDR address
>  * if a local address isn't available; otherwise, returns zero.
>  */
> -int nfs_callback_address(const struct sockaddr *sap, const  
> socklen_t salen,
> +int nfs_callback_address(struct sockaddr* caddr, const socklen_t  
> clen,
> +			 const struct sockaddr *sap, const socklen_t salen,
> 			 struct sockaddr *buf, socklen_t *buflen)
> {
> 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
>
> -	if (nfs_ca_sockname(sap, salen, buf, buflen) == 0)
> +	if (nfs_ca_sockname(caddr, clen, sap, salen, buf, buflen) == 0)
> 		if (nfs_ca_gai(sap, salen, buf, buflen) == 0)
> 			goto out_failed;
>
> diff --git a/utils/mount/network.h b/utils/mount/network.h
> index 075093d..5413686 100644
> --- a/utils/mount/network.h
> +++ b/utils/mount/network.h
> @@ -39,29 +39,32 @@ typedef struct {
> static const struct timeval TIMEOUT = { 20, 0 };
> static const struct timeval RETRY_TIMEOUT = { 3, 0 };
>
> -int probe_bothports(clnt_addr_t *, clnt_addr_t *);
> +int probe_bothports(struct sockaddr* caddr, const socklen_t clen,  
> clnt_addr_t *, clnt_addr_t *);
> int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
> -			struct pmap *, const struct sockaddr *,
> +			struct pmap *,
> +                        struct sockaddr *nfs_caddr,
> +			const socklen_t nfs_clen, const struct sockaddr *,
> 			const socklen_t, struct pmap *);
> int nfs_gethostbyname(const char *, struct sockaddr_in *);
> +int nfs_call_umount(struct sockaddr *caddr, const socklen_t clen,  
> clnt_addr_t *, dirpath *);
> int nfs_name_to_address(const char *, const sa_family_t,
> 		struct sockaddr *, socklen_t *);
> int nfs_string_to_sockaddr(const char *, const size_t,
> 			   struct sockaddr *, socklen_t *);
> int nfs_present_sockaddr(const struct sockaddr *,
> 			 const socklen_t, char *, const size_t);
> -int nfs_callback_address(const struct sockaddr *, const socklen_t,
> +int nfs_callback_address(struct sockaddr* caddr, const socklen_t  
> clen,
> +		const struct sockaddr *, const socklen_t,
> 		struct sockaddr *, socklen_t *);
> -int nfs_call_umount(clnt_addr_t *, dirpath *);
> -int clnt_ping(struct sockaddr_in *, const unsigned long,
> -		const unsigned long, const unsigned int,
> -		struct sockaddr_in *);
> +int clnt_ping(struct sockaddr* caddr, socklen_t clen,
> +		struct sockaddr_in *, const unsigned long,
> +		const unsigned long, const unsigned int);
>
> int start_statd(void);
>
> unsigned long nfsvers_to_mnt(const unsigned long);
>
> -CLIENT *mnt_openclnt(clnt_addr_t *, int *);
> +CLIENT *mnt_openclnt(struct sockaddr *caddr, const socklen_t clen,  
> clnt_addr_t *, int *);
> void mnt_closeclnt(CLIENT *, int);
>
> #endif	/* _NFS_UTILS_MOUNT_NETWORK_H */
> diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
> index a2f318f..e06025d 100644
> --- a/utils/mount/nfs4mount.c
> +++ b/utils/mount/nfs4mount.c
> @@ -420,9 +420,12 @@ int nfs4mount(const char *spec, const char  
> *node, int flags,
> 				data.proto == IPPROTO_UDP ? "udp" : "tcp",
> 				ntohs(server_addr.sin_port));
> 		}
> -		client_addr.sin_family = 0;
> -		client_addr.sin_addr.s_addr = 0;
> -		clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
> +		memset(&client_addr, 0, sizeof(client_addr));
> +		if (ip_addr_in_opts) {
> +			client_addr.sin_family = AF_INET;
> +			client_addr.sin_addr.s_addr = inet_addr(ip_addr);
> +		}
> +		clnt_ping((struct sockaddr*)&client_addr, sizeof(client_addr),  
> &server_addr, NFS_PROGRAM, 4, data.proto);
> 		if (rpc_createerr.cf_stat == RPC_SUCCESS) {
> 			if (!ip_addr_in_opts &&
> 			    client_addr.sin_family != 0 &&
> diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h
> index 2becfb1..9fe1251 100644
> --- a/utils/mount/nfs_mount.h
> +++ b/utils/mount/nfs_mount.h
> @@ -14,7 +14,7 @@
> #include <netinet/in.h>
> #include <arpa/inet.h>
>
> -#define NFS_MOUNT_VERSION	6
> +#define NFS_MOUNT_VERSION	7
> #define NFS_MAX_CONTEXT_LEN	256
>
> struct nfs2_fh {
> @@ -45,6 +45,7 @@ struct nfs_mount_data {
> 	struct nfs3_fh	root;			/* 4 */
> 	int		pseudoflavor;		/* 5 */
> 	char    context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
> +	struct sockaddr_storage clientaddr;		/* 7 */
>
> };
>
> diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
> index 6355681..8c01d58 100644
> --- a/utils/mount/nfsmount.c
> +++ b/utils/mount/nfsmount.c
> @@ -122,17 +122,17 @@ nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg,  
> mnt2res_t *mnt2res)
> }
>
> static int
> -nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
> -	       mntarg_t *mntarg, mntres_t *mntres)
> +nfs_call_mount(clnt_addr_t *mnt_server, struct sockaddr  
> *clientaddr, socklen_t clen,
> +	       clnt_addr_t *nfs_server, mntarg_t *mntarg, mntres_t *mntres)
> {
> 	CLIENT *clnt;
> 	enum clnt_stat stat;
> 	int msock;
>
> -	if (!probe_bothports(mnt_server, nfs_server))
> +	if (!probe_bothports(clientaddr, clen, mnt_server, nfs_server))
> 		goto out_bad;
>
> -	clnt = mnt_openclnt(mnt_server, &msock);
> +	clnt = mnt_openclnt(clientaddr, clen, mnt_server, &msock);
> 	if (!clnt)
> 		goto out_bad;
> 	/* make pointers in xdr_mountres3 NULL so
> @@ -167,6 +167,7 @@ parse_options(char *old_opts, struct  
> nfs_mount_data *data,
> 	      clnt_addr_t *nfs_server, char *new_opts, const int opt_size)
> {
> 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
> +	struct sockaddr_in *clientaddr;
> 	struct pmap *mnt_pmap = &mnt_server->pmap;
> 	struct pmap *nfs_pmap = &nfs_server->pmap;
> 	int len;
> @@ -178,6 +179,10 @@ parse_options(char *old_opts, struct  
> nfs_mount_data *data,
> 	data->flags = 0;
> 	*bg = 0;
>
> +	/* TODO:  Support IPv6 binding */
> +	clientaddr = (struct sockaddr_in *)&data->clientaddr;
> +	clientaddr->sin_family = AF_INET;
> +
> 	len = strlen(new_opts);
> 	for (p=old_opts, opt_b=NULL; p && *p; p++) {
> 		if (!opt_b)
> @@ -252,6 +257,19 @@ parse_options(char *old_opts, struct  
> nfs_mount_data *data,
> 			} else if (!strcmp(opt, "addr")) {
> 				/* ignore */;
> 				continue;
> +#if NFS_MOUNT_VERSION >= 7
> +			} else if (!strcmp(opt, "clientaddr")) {
> +				char ip_addr[sizeof("255.255.255.255")];
> +				if (strlen(opteq+1) >= sizeof(ip_addr)) {
> +					printf(_("Invalid client address %s"),
> +						 opteq+1);
> +					goto bad_parameter;
> +				}
> +				strncpy(ip_addr,opteq+1, sizeof(ip_addr));
> +				ip_addr[sizeof(ip_addr)-1] = '\0';
> +
> +				clientaddr->sin_addr.s_addr = inet_addr(ip_addr);
> +#endif
>  			} else if (sloppy)
> 				continue;
> 			else
> @@ -673,8 +691,10 @@ nfsmount(const char *spec, const char *node,  
> int flags,
> 			if (t - prevt < 30)
> 				sleep(30);
>
> -			stat = nfs_call_mount(&mnt_server, &nfs_server,
> -					      &dirname, &mntres);
> +			stat = nfs_call_mount(&mnt_server,
> +					      (struct sockaddr *)&data.clientaddr,
> +					      sizeof(data.clientaddr),
> +					      &nfs_server, &dirname, &mntres);
> 			if (stat)
> 				break;
> 			memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
> @@ -772,7 +792,8 @@ nfsmount(const char *spec, const char *node, int  
> flags,
> 					"not supported"),
> 					progname, hostname, dirname);
> 			/* server has registered us in rmtab, send umount */
> -			nfs_call_umount(&mnt_server, &dirname);
> +			nfs_call_umount((struct sockaddr *)&data.clientaddr,  
> sizeof(data.clientaddr),
> +					&mnt_server, &dirname);
> 			goto fail;
> 		}
> noauth_flavors:
> diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
> index b2327e0..cb9edfd 100644
> --- a/utils/mount/nfsumount.c
> +++ b/utils/mount/nfsumount.c
> @@ -25,6 +25,9 @@
> #include <sys/mount.h>
> #include <ctype.h>
> #include <pwd.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
>
> #include "xcommon.h"
> #include "fstab.h"
> @@ -147,6 +150,7 @@ static int do_nfs_umount23(const char *spec,  
> char *opts)
> {
> 	char *hostname;
> 	char *dirname;
> +	struct sockaddr_in caddr;
> 	clnt_addr_t mnt_server = { &hostname, };
> 	struct mntent mnt = { .mnt_opts = opts };
> 	struct pmap *pmap = &mnt_server.pmap;
> @@ -160,14 +164,37 @@ static int do_nfs_umount23(const char *spec,  
> char *opts)
> 	printf(_("host: %s, directory: %s\n"), hostname, dirname);
> #endif
>
> -	if (opts && (p = strstr(opts, "addr="))) {
> +	for (p = opts; (p = strstr(p, "addr=")); ) {
> 		char *q;
>
> +		/* This is a bit of a hack, but necessary to avoid using the
> +		 * client address as server address. */
> +		if (p >= opts + strlen("client") &&
> +		    strncmp(p - strlen("client"), "client",
> +		    	    strlen("client")) == 0) {
> +			p += strlen("addr=");
> +			continue;
> +		}
> +
> 		free(hostname);
> 		p += 5;
> 		q = p;
> 		while (*q && *q != ',') q++;
> 		hostname = xstrndup(p,q-p);
> +		break;
> +	}
> +
> +	memset(&caddr, 0, sizeof(caddr));
> +	if (opts && (p = strstr(opts, "clientaddr="))) {
> +		char *q, *tmp;
> +
> +		p += 11;
> +		q = p;
> +		while (*q && *q != ',') q++;
> +		tmp = xstrndup(p,q-p);
> +		caddr.sin_family = AF_INET;
> +		caddr.sin_addr.s_addr = inet_addr(tmp);
> +		free(tmp);
> 	}
>
> 	if (opts && (p = strstr(opts, "mounthost="))) {
> @@ -211,7 +238,7 @@ static int do_nfs_umount23(const char *spec,  
> char *opts)
> 		goto out;
> 	}
>
> -	if (!nfs_call_umount(&mnt_server, &dirname)) {
> +	if (!nfs_call_umount((struct sockaddr*)&caddr, sizeof(caddr),  
> &mnt_server, &dirname)) {
> 		nfs_error(_("%s: Server failed to unmount '%s'"),
> 				progname, spec);
> 		result = EX_FAIL;
> diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
> index 43791e6..54b5c94 100644
> --- a/utils/mount/stropts.c
> +++ b/utils/mount/stropts.c
> @@ -184,11 +184,17 @@ static int nfs_append_clientaddr_option(const  
> struct sockaddr *sap,
> 	struct sockaddr_storage dummy;
> 	struct sockaddr *my_addr = (struct sockaddr *)&dummy;
> 	socklen_t my_len = sizeof(dummy);
> +	struct sockaddr_storage dclient;
> +	struct sockaddr *my_cl = (struct sockaddr *)&dclient;
> +	socklen_t my_lenc = sizeof(dclient);
>
> 	if (po_contains(options, "clientaddr") == PO_FOUND)
> 		return 1;
>
> -	nfs_callback_address(sap, salen, my_addr, &my_len);
> +	memset(&dclient, 0, sizeof(dclient));
> +	my_cl->sa_family = sap->sa_family;
> +	
> +	nfs_callback_address(my_cl, my_lenc, sap, salen, my_addr, &my_len);
>
> 	return nfs_append_generic_address_option(my_addr, my_len,
> 							"clientaddr", options);
> @@ -322,8 +328,11 @@ static struct mount_options  
> *nfs_rewrite_mount_options(char *str)
> 	char *option, new_option[64];
> 	clnt_addr_t mnt_server = { };
> 	clnt_addr_t nfs_server = { };
> +        struct sockaddr_in clientaddr;
> 	int p;
>
> +        memset(&clientaddr, 0, sizeof(clientaddr));
> +
> 	options = po_split(str);
> 	if (!options) {
> 		errno = EFAULT;
> @@ -339,6 +348,12 @@ static struct mount_options  
> *nfs_rewrite_mount_options(char *str)
> 	} else
> 		goto err;
>
> +        option = po_get(options, "clientaddr");
> +        if (option) {
> +           clientaddr.sin_family = AF_INET;
> +           clientaddr.sin_addr.s_addr = inet_addr(option);
> +        }
> +
> 	option = po_get(options, "mountaddr");
> 	if (option) {
> 		mnt_server.saddr.sin_family = AF_INET;
> @@ -407,7 +422,7 @@ static struct mount_options  
> *nfs_rewrite_mount_options(char *str)
> 	po_remove_all(options, "tcp");
> 	po_remove_all(options, "udp");
>
> -	if (!probe_bothports(&mnt_server, &nfs_server)) {
> +	if (!probe_bothports((struct sockaddr*)&clientaddr,  
> sizeof(clientaddr), &mnt_server, &nfs_server)) {
> 		errno = ESPIPE;
> 		goto err;
> 	}
> diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c
> index 2695c51..2545ed6 100644
> --- a/utils/showmount/showmount.c
> +++ b/utils/showmount/showmount.c
> @@ -55,6 +55,7 @@ static struct option longopts[] =
> 	{ "all", 0, 0, 'a' },
> 	{ "directories", 0, 0, 'd' },
> 	{ "exports", 0, 0, 'e' },
> +	{ "local_ip", 1, 0, 'i' },
> 	{ "no-headers", 0, &headers, 0 },
> 	{ "version", 0, 0, 'v' },
> 	{ "help", 0, 0, 'h' },
> @@ -74,7 +75,7 @@ static void usage(FILE *fp, int n)
> {
> 	fprintf(fp, "Usage: %s [-adehv]\n", program_name);
> 	fprintf(fp, "       [--all] [--directories] [--exports]\n");
> -	fprintf(fp, "       [--no-headers] [--help] [--version] [host]\n");
> +	fprintf(fp, "       [--no-headers] [--help] [--version] [-- 
> local_ip a.b.c.d] [host]\n");
> 	exit(n);
> }
>
> @@ -190,14 +191,35 @@ done:
>  *
>  * Supports only AF_INET server addresses.
>  */
> -static CLIENT *nfs_get_mount_client(const char *hostname)
> +static CLIENT *nfs_get_mount_client(const char *hostname, const  
> char* local_ip)
> {
> 	struct hostent *hp;
> 	struct sockaddr_in server_addr;
> +	struct sockaddr_in local_addr;
> 	struct timeval pertry_timeout;
> 	CLIENT *mclient = NULL;
> 	int ret, msock;
>
> +	if (local_ip) {
> +		if (inet_aton(local_ip, &local_addr.sin_addr)) {
> +			server_addr.sin_family = AF_INET;
> +		}
> +		else {
> +			if ((hp = gethostbyname(hostname)) == NULL) {
> +				fprintf(stderr, "%s: can't get address for %s\n",
> +					program_name, hostname);
> +				exit(1);
> +			}
> +			server_addr.sin_family = AF_INET;
> +			memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
> +		}
> +	}
> +	else {
> +		local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
> +		local_addr.sin_port = 0;
> +		local_addr.sin_family = AF_INET;
> +	}
> +	
> 	if (inet_aton(hostname, &server_addr.sin_addr)) {
> 		server_addr.sin_family = AF_INET;
> 	}
> @@ -213,7 +235,8 @@ static CLIENT *nfs_get_mount_client(const char  
> *hostname)
>
> 	msock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
> 	if (msock != -1) {
> -		if (nfs_getport_ping((struct sockaddr *)&server_addr,
> +		if (nfs_getport_ping((struct sockaddr*)(&local_addr),  
> sizeof(local_addr),
> +				     (struct sockaddr *)&server_addr,
> 					sizeof(server_addr), MOUNTPROG,
> 					MOUNTVERS, IPPROTO_TCP)) {
> 			ret = connect_nb(msock, &server_addr, 0);
> @@ -228,7 +251,8 @@ static CLIENT *nfs_get_mount_client(const char  
> *hostname)
> 	}
>
> 	if (!mclient) {
> -		if (nfs_getport_ping((struct sockaddr *)&server_addr,
> +		if (nfs_getport_ping((struct sockaddr*)&local_addr,  
> sizeof(local_addr),
> +				     (struct sockaddr *)&server_addr,
> 					sizeof(server_addr), MOUNTPROG,
> 					MOUNTVERS, IPPROTO_UDP)) {
> 			clnt_pcreateerror("showmount");
> @@ -253,6 +277,7 @@ int main(int argc, char **argv)
> {
> 	char hostname_buf[MAXHOSTLEN];
> 	char *hostname;
> +	char* local_ip = NULL;
> 	enum clnt_stat clnt_stat;
> 	struct timeval total_timeout;
> 	int c;
> @@ -267,7 +292,7 @@ int main(int argc, char **argv)
> 	char **dumpv;
>
> 	program_name = argv[0];
> -	while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) !=  
> EOF) {
> +	while ((c = getopt_long(argc, argv, "adehvi:", longopts, NULL)) !=  
> EOF) {
> 		switch (c) {
> 		case 'a':
> 			aflag = 1;
> @@ -281,6 +306,9 @@ int main(int argc, char **argv)
> 		case 'h':
> 			usage(stdout, 0);
> 			break;
> +		case 'i':
> +			local_ip = strdup(optarg);
> +			break;
> 		case 'v':
> 			printf("%s\n", version);
> 			exit(0);
> @@ -326,7 +354,7 @@ int main(int argc, char **argv)
> 		break;
> 	}
>
> -	mclient = nfs_get_mount_client(hostname);
> +	mclient = nfs_get_mount_client(hostname, local_ip);
> 	mclient->cl_auth = authunix_create_default();
> 	total_timeout.tv_sec = TOTAL_TIMEOUT;
> 	total_timeout.tv_usec = 0;

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com




^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-22  2:38 ` Chuck Lever
@ 2009-01-22  5:35   ` Ben Greear
  2009-01-22 17:06     ` Chuck Lever
  0 siblings, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-01-22  5:35 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

Chuck Lever wrote:
> A handful of generic comments.
>
> 1.  This needs to be broken into smaller patches before submission; 
> preferably before you submit another version for review.  Take a look 
> at the linux-nfs@vger.kernel.org archives to see how we handle large 
> changes like this.
>
> 2.  You should support local addresses only in the text-based path 
> (utils/mount/stropts.c) and not in the legacy paths 
> (utils/mount/nfs[4]mount.c).  I don't think we're ever going to allow 
> a version 7 of the mount data structure.
I could remove the version 7 data field, but what about the other code 
that creates
sockets for 'pinging' nfs daemons and such?   Is that code deprecated now?

If nothing else, it looks like "probe_bothports" needs a client-addr to 
pass on
to methods it calls, and so forth.  That means that other code that 
calls those
methods needs to be updated, and thus my huge repetitive patch.

> 3.  There are some umount-related changes coming up for IPv6 that will 
> touch the umount paths here; that may require some changes in your 
> modifications.
That should be fine.   I tried to make my current patch friendly for 
IPv6 and want to support local
IPv6 binding as well.
>
> 4.  This needs to have support for a new mount option in the kernel 
> (not a command-line option to the mount.nfs command, as you have 
> implemented) to handle passing the address to the kernel.
I think I already have this ready in the kernel, but I've been using the 
version 7 field to pass
in data using mount.nfs.    Can you give an example of a user-space 
command that would use the
new mount option as you suggest?  Do you do /etc/fstab any different for 
instance?

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-22  5:35   ` Ben Greear
@ 2009-01-22 17:06     ` Chuck Lever
  2009-01-22 17:31       ` Ben Greear
  0 siblings, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2009-01-22 17:06 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-nfs, Patrick McHardy

On Jan 22, 2009, at Jan 22, 2009, 12:35 AM, Ben Greear wrote:
> Chuck Lever wrote:
>> A handful of generic comments.
>>
>> 1.  This needs to be broken into smaller patches before submission;  
>> preferably before you submit another version for review.  Take a  
>> look at the linux-nfs@vger.kernel.org archives to see how we handle  
>> large changes like this.
>>
>> 2.  You should support local addresses only in the text-based path  
>> (utils/mount/stropts.c) and not in the legacy paths (utils/mount/ 
>> nfs[4]mount.c).  I don't think we're ever going to allow a version  
>> 7 of the mount data structure.
> I could remove the version 7 data field, but what about the other  
> code that creates
> sockets for 'pinging' nfs daemons and such?   Is that code  
> deprecated now?

It's still used for text-based NFSv2/v3 mounts, and unmounts.  The  
mount option rewriting code in stropts.c probably needs to be  
sensitive of the local address too.  That's the piece that uses all  
the getport functions.

> If nothing else, it looks like "probe_bothports" needs a client-addr  
> to pass on
> to methods it calls, and so forth.  That means that other code that  
> calls those
> methods needs to be updated, and thus my huge repetitive patch.

Expanding the synopses of all the getport calls in support/nfs/*.c is  
part of what can be split into separate patches.  I would start with  
support/nfs/rpc_socket.c (the lowest level) and move upwards.

Note that only some of these functions are actually used.  Some are  
included just to provide a complete getport API.

Just an idea: you might consider allowing the passed in local sockaddr  
to point to NULL.  In that case, just default to INADDR_ANY.  That  
might make the upper levels easier to code.

>> 3.  There are some umount-related changes coming up for IPv6 that  
>> will touch the umount paths here; that may require some changes in  
>> your modifications.
> That should be fine.   I tried to make my current patch friendly for  
> IPv6 and want to support local
> IPv6 binding as well.

The upcoming IPv6 support adds a new unmount function that replaces  
the functions that currently support only sockaddr_in.  That new  
function is probably what you would need to modify instead of updating  
the mnt_* functions, which will only be used by the legacy part of the  
mount.nfs command after my patches.

>> 4.  This needs to have support for a new mount option in the kernel  
>> (not a command-line option to the mount.nfs command, as you have  
>> implemented) to handle passing the address to the kernel.
> I think I already have this ready in the kernel, but I've been using  
> the version 7 field to pass
> in data using mount.nfs.

A new mount data version does not have anything to do with text-based  
mounts, which is how this should be supported.  You will have to add  
support for a new mount option in the text-based mount option parser  
in the kernel's fs/nfs/super.c.

>    Can you give an example of a user-space command that would use the
> new mount option as you suggest?  Do you do /etc/fstab any different  
> for instance?

Let's call it "localaddr=".  Something like:

   mount -o tcp,vers=3,localaddr=192.168.0.59 server:/export

I think this address would also need to be passed to the kernel's  
lockd at mount time by expanding the argument list to nlmclnt_init()  
and nlmclnt_lookup_host() in the kernel NFS client.  I think lockd  
already has some ability to support a local address, but that may be  
server-side only.

Still thinking about what needs to be done for NSM/statd.

You will also need to take some care with how NFSv4 generates the  
SETCLIENTID request, which sends the client's IP callback address and  
port to the server.  That will have to take the specified local  
address instead.  Take a look at the logic in stropts.c around the  
clientaddr= option.

If neither localaddr= or clientaddr= is specified, call  
nfs_callback_address().  If clientaddr= is specified, use the value of  
clientaddr=.  If localaddr= is specified, but clientaddr= is not, use  
the localaddr= value.

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-22 17:06     ` Chuck Lever
@ 2009-01-22 17:31       ` Ben Greear
  2009-01-23 17:18         ` Chuck Lever
  0 siblings, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-01-22 17:31 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

Chuck Lever wrote:
> On Jan 22, 2009, at Jan 22, 2009, 12:35 AM, Ben Greear wrote:
>> Chuck Lever wrote:
>>> A handful of generic comments.
>>>
>>> 1.  This needs to be broken into smaller patches before submission; 
>>> preferably before you submit another version for review.  Take a look 
>>> at the linux-nfs@vger.kernel.org archives to see how we handle large 
>>> changes like this.
>>>
>>> 2.  You should support local addresses only in the text-based path 
>>> (utils/mount/stropts.c) and not in the legacy paths 
>>> (utils/mount/nfs[4]mount.c).  I don't think we're ever going to allow 
>>> a version 7 of the mount data structure.
>> I could remove the version 7 data field, but what about the other code 
>> that creates
>> sockets for 'pinging' nfs daemons and such?   Is that code deprecated 
>> now?
> 
> It's still used for text-based NFSv2/v3 mounts, and unmounts.  The mount 
> option rewriting code in stropts.c probably needs to be sensitive of the 
> local address too.  That's the piece that uses all the getport functions.
> 
>> If nothing else, it looks like "probe_bothports" needs a client-addr 
>> to pass on
>> to methods it calls, and so forth.  That means that other code that 
>> calls those
>> methods needs to be updated, and thus my huge repetitive patch.
> 
> Expanding the synopses of all the getport calls in support/nfs/*.c is 
> part of what can be split into separate patches.  I would start with 
> support/nfs/rpc_socket.c (the lowest level) and move upwards.

I assume each patch would need to compile, so as I change the low
level, then I'm going to have to change the rest of the levels until
it will compile.  Unless each intermediate patch adds dummy addrs
(ie, ADDR_ANY sockaddr structs), and removes the lower level dummy,
I don't see how to break this into several patches.  And adding/removing
dummy code just to break up the patches seems a lot of wasted effort to me.

> Note that only some of these functions are actually used.  Some are 
> included just to provide a complete getport API.
> 
> Just an idea: you might consider allowing the passed in local sockaddr 
> to point to NULL.  In that case, just default to INADDR_ANY.  That might 
> make the upper levels easier to code.

I thought this might make it harder to deal with IPv6 v/s IPv4.  If
we're always passing in an struct sockaddr then it can have the appropriate
family already set.  This also allows the code to create INADDR_ANY addresses in just
a few places instead of all the low-level methods, and keeps us from having
to do lots of error checking against a null local-addr.

>>> 3.  There are some umount-related changes coming up for IPv6 that 
>>> will touch the umount paths here; that may require some changes in 
>>> your modifications.
>> That should be fine.   I tried to make my current patch friendly for 
>> IPv6 and want to support local
>> IPv6 binding as well.
> 
> The upcoming IPv6 support adds a new unmount function that replaces the 
> functions that currently support only sockaddr_in.  That new function is 
> probably what you would need to modify instead of updating the mnt_* 
> functions, which will only be used by the legacy part of the mount.nfs 
> command after my patches.

Any idea when this will hit the git repository?

>>    Can you give an example of a user-space command that would use the
>> new mount option as you suggest?  Do you do /etc/fstab any different 
>> for instance?
> 
> Let's call it "localaddr=".  Something like:
> 
>   mount -o tcp,vers=3,localaddr=192.168.0.59 server:/export

Why not clientaddr?  That is already used for NFS version 4, and it works
just fine for version 3 as well.  I can't think of any reason to have
a new variable.

> I think this address would also need to be passed to the kernel's lockd 
> at mount time by expanding the argument list to nlmclnt_init() and 
> nlmclnt_lookup_host() in the kernel NFS client.  I think lockd already 
> has some ability to support a local address, but that may be server-side 
> only.

I'll look at this as well.  I have a kernel patch that *appears* to work,
but it may still be missing binding in a few of the more obscure paths.

> Still thinking about what needs to be done for NSM/statd.
> 
> You will also need to take some care with how NFSv4 generates the 
> SETCLIENTID request, which sends the client's IP callback address and 
> port to the server.  That will have to take the specified local address 
> instead.  Take a look at the logic in stropts.c around the clientaddr= 
> option.
> 
> If neither localaddr= or clientaddr= is specified, call 
> nfs_callback_address().  If clientaddr= is specified, use the value of 
> clientaddr=.  If localaddr= is specified, but clientaddr= is not, use 
> the localaddr= value.

Thanks for the suggestions!

Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-22 17:31       ` Ben Greear
@ 2009-01-23 17:18         ` Chuck Lever
  2009-01-23 17:39           ` Ben Greear
  2009-02-21  7:43           ` Ben Greear
  0 siblings, 2 replies; 21+ messages in thread
From: Chuck Lever @ 2009-01-23 17:18 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-nfs, Patrick McHardy

On Jan 22, 2009, at Jan 22, 2009, 12:31 PM, Ben Greear wrote:
> Chuck Lever wrote:
>> On Jan 22, 2009, at Jan 22, 2009, 12:35 AM, Ben Greear wrote:
>>> Chuck Lever wrote:
>>>> A handful of generic comments.
>>>>
>>>> 1.  This needs to be broken into smaller patches before  
>>>> submission; preferably before you submit another version for  
>>>> review.  Take a look at the linux-nfs@vger.kernel.org archives to  
>>>> see how we handle large changes like this.
>>>>
>>>> 2.  You should support local addresses only in the text-based  
>>>> path (utils/mount/stropts.c) and not in the legacy paths (utils/ 
>>>> mount/nfs[4]mount.c).  I don't think we're ever going to allow a  
>>>> version 7 of the mount data structure.
>>> I could remove the version 7 data field, but what about the other  
>>> code that creates
>>> sockets for 'pinging' nfs daemons and such?   Is that code  
>>> deprecated now?
>> It's still used for text-based NFSv2/v3 mounts, and unmounts.  The  
>> mount option rewriting code in stropts.c probably needs to be  
>> sensitive of the local address too.  That's the piece that uses all  
>> the getport functions.
>>> If nothing else, it looks like "probe_bothports" needs a client- 
>>> addr to pass on
>>> to methods it calls, and so forth.  That means that other code  
>>> that calls those
>>> methods needs to be updated, and thus my huge repetitive patch.
>> Expanding the synopses of all the getport calls in support/nfs/*.c  
>> is part of what can be split into separate patches.  I would start  
>> with support/nfs/rpc_socket.c (the lowest level) and move upwards.
>
> I assume each patch would need to compile, so as I change the low
> level, then I'm going to have to change the rest of the levels until
> it will compile.  Unless each intermediate patch adds dummy addrs
> (ie, ADDR_ANY sockaddr structs), and removes the lower level dummy,
> I don't see how to break this into several patches.  And adding/ 
> removing
> dummy code just to break up the patches seems a lot of wasted effort  
> to me.

Passing a NULL pointer for the local bind address would save you some  
effort here.

The point of breaking this change up is that:

1.  You can do fine-grained unit testing of your changes
2.  It's easier for us to review a major change like this in small  
chunks
3.  It's easier for us to revert the changes selectively if necessary
4.  It gives more opportunity to document specific features in the  
description of each patch

Btw, I've found that the convention for naming the variable that  
points to the local address is "lbaddr," "bind_addr," or "laddr", but  
not "caddr".  "caddr" usually means "memory address of a character  
string".  See caddr_t.

I'm sure you can find examples for caddr, but I would prefer "laddr"  
or something like that.

>> Note that only some of these functions are actually used.  Some are  
>> included just to provide a complete getport API.
>> Just an idea: you might consider allowing the passed in local  
>> sockaddr to point to NULL.  In that case, just default to  
>> INADDR_ANY.  That might make the upper levels easier to code.
>
> I thought this might make it harder to deal with IPv6 v/s IPv4.  If
> we're always passing in an struct sockaddr then it can have the  
> appropriate
> family already set.  This also allows the code to create INADDR_ANY  
> addresses in just
> a few places instead of all the low-level methods, and keeps us from  
> having
> to do lots of error checking against a null local-addr.

The local bind address family is the same as the remote's address  
family.  If you pass in a NULL pointer, the code can examine the  
remote address and choose an ANYADDR of the same family for the bind  
address, same as it works today in rpc_socket.c:nfs_bind().

This way stropts.c would construct a special local bind address only  
if the localaddr= mount option was specified; otherwise it just passes  
in NULL/0.  This also reduces the amount of change needed at legacy  
callsites.

>>>> 3.  There are some umount-related changes coming up for IPv6 that  
>>>> will touch the umount paths here; that may require some changes  
>>>> in your modifications.
>>> That should be fine.   I tried to make my current patch friendly  
>>> for IPv6 and want to support local
>>> IPv6 binding as well.
>> The upcoming IPv6 support adds a new unmount function that replaces  
>> the functions that currently support only sockaddr_in.  That new  
>> function is probably what you would need to modify instead of  
>> updating the mnt_* functions, which will only be used by the legacy  
>> part of the mount.nfs command after my patches.
>
> Any idea when this will hit the git repository?

It's next in line after Steve merges up what I sent him most  
recently.  So in the next month or so, I would guess.

>>>   Can you give an example of a user-space command that would use the
>>> new mount option as you suggest?  Do you do /etc/fstab any  
>>> different for instance?
>> Let's call it "localaddr=".  Something like:
>>  mount -o tcp,vers=3,localaddr=192.168.0.59 server:/export
>
> Why not clientaddr?  That is already used for NFS version 4, and it  
> works
> just fine for version 3 as well.  I can't think of any reason to have
> a new variable.

That may make sense for NFSv2/v3, but nfs(5) documents the clientaddr  
specifically as the client's NFSv4 callback server address.  Is there  
ever a case where we want to specify a NFSv4 callback address that's  
different from the source address of your NFSv4 forward channel?  Are  
we sure these will always be the same?

Conversely, do we always want to limit our NFSv4 traffic to a single  
bind address when clientaddr= is specified?

IMO the coding and documentation overhead for reusing clientaddr would  
be the same or greater than creating a new mount option, and the  
result would be more confusing.  These two options do different  
things.  I'd be interested to hear opinions from others on the list.

"bindaddr=" might be more clear than "localaddr=".  I checked Solaris  
10, but it doesn't seem to have a similar capability.  So, no guidance  
there...

>> I think this address would also need to be passed to the kernel's  
>> lockd at mount time by expanding the argument list to  
>> nlmclnt_init() and nlmclnt_lookup_host() in the kernel NFS client.   
>> I think lockd already has some ability to support a local address,  
>> but that may be server-side only.
>
> I'll look at this as well.  I have a kernel patch that *appears* to  
> work,
> but it may still be missing binding in a few of the more obscure  
> paths.
>
>> Still thinking about what needs to be done for NSM/statd.
>> You will also need to take some care with how NFSv4 generates the  
>> SETCLIENTID request, which sends the client's IP callback address  
>> and port to the server.  That will have to take the specified local  
>> address instead.  Take a look at the logic in stropts.c around the  
>> clientaddr= option.
>> If neither localaddr= or clientaddr= is specified, call  
>> nfs_callback_address().  If clientaddr= is specified, use the value  
>> of clientaddr=.  If localaddr= is specified, but clientaddr= is  
>> not, use the localaddr= value.
>
> Thanks for the suggestions!

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-23 17:18         ` Chuck Lever
@ 2009-01-23 17:39           ` Ben Greear
  2009-02-21  7:43           ` Ben Greear
  1 sibling, 0 replies; 21+ messages in thread
From: Ben Greear @ 2009-01-23 17:39 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

Chuck Lever wrote:

>> Why not clientaddr?  That is already used for NFS version 4, and it works
>> just fine for version 3 as well.  I can't think of any reason to have
>> a new variable.
> 
> That may make sense for NFSv2/v3, but nfs(5) documents the clientaddr 
> specifically as the client's NFSv4 callback server address.  Is there 
> ever a case where we want to specify a NFSv4 callback address that's 
> different from the source address of your NFSv4 forward channel?  Are we 
> sure these will always be the same?
> 
> Conversely, do we always want to limit our NFSv4 traffic to a single 
> bind address when clientaddr= is specified?
> 
> IMO the coding and documentation overhead for reusing clientaddr would 
> be the same or greater than creating a new mount option, and the result 
> would be more confusing.  These two options do different things.  I'd be 
> interested to hear opinions from others on the list.
> 
> "bindaddr=" might be more clear than "localaddr=".  I checked Solaris 
> 10, but it doesn't seem to have a similar capability.  So, no guidance 
> there...

Either way is fine with me.  I'll change it to be bindaddr unless
I hear another opinion.

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-01-23 17:18         ` Chuck Lever
  2009-01-23 17:39           ` Ben Greear
@ 2009-02-21  7:43           ` Ben Greear
  2009-02-21 17:16             ` Trond Myklebust
  2009-02-21 22:09             ` Chuck Lever
  1 sibling, 2 replies; 21+ messages in thread
From: Ben Greear @ 2009-02-21  7:43 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

[-- Attachment #1: Type: text/plain, Size: 578 bytes --]

I re-worked the kernel nfs local-address-binding logic against 
2.6.29-rc5.  In quick testing,
this works with IPv4 and NFSv3 at least.  I changed the attribute to be 
called 'bindaddr'
as previously suggested.

I didn't actually make any further changes to the mount.nfs tool and it 
took the bindaddr=a.b.c.d
just fine, so maybe there are no changes at all needed in user-space.

Comments & suggestions welcome.

Thanks,
Ben

Signed-Off-By:  Ben Greear<greearb@candelatech.com>

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



[-- Attachment #2: nfs_bind.patch --]
[-- Type: text/x-patch, Size: 10272 bytes --]

diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 3e634f2..1c7011e 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -213,7 +213,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 	int ret = SVC_OK;
 
 	/* Don't talk to strangers */
-	clp = nfs_find_client(svc_addr(rqstp), 4);
+	clp = nfs_find_client(svc_daddr(rqstp), svc_addr(rqstp), 4);
 	if (clp == NULL)
 		return SVC_DROP;
 
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index bb25d21..94fd8b4 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -39,6 +39,7 @@ struct cb_compound_hdr_res {
 
 struct cb_getattrargs {
 	struct sockaddr *addr;
+	struct sockaddr *bindaddr;
 	struct nfs_fh fh;
 	uint32_t bitmap[2];
 };
@@ -54,6 +55,7 @@ struct cb_getattrres {
 
 struct cb_recallargs {
 	struct sockaddr *addr;
+	struct sockaddr *bindaddr;
 	struct nfs_fh fh;
 	nfs4_stateid stateid;
 	uint32_t truncate;
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index f7e83e2..cd75849 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -25,7 +25,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
 
 	res->bitmap[0] = res->bitmap[1] = 0;
 	res->status = htonl(NFS4ERR_BADHANDLE);
-	clp = nfs_find_client(args->addr, 4);
+	clp = nfs_find_client(args->bindaddr, args->addr, 4);
 	if (clp == NULL)
 		goto out;
 
@@ -68,7 +68,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
 	__be32 res;
 	
 	res = htonl(NFS4ERR_BADHANDLE);
-	clp = nfs_find_client(args->addr, 4);
+	clp = nfs_find_client(args->bindaddr, args->addr, 4);
 	if (clp == NULL)
 		goto out;
 
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index dd0ef34..a342e8e 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -177,6 +177,7 @@ static __be32 decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr
 	if (unlikely(status != 0))
 		goto out;
 	args->addr = svc_addr(rqstp);
+	args->bindaddr = svc_daddr(rqstp);
 	status = decode_bitmap(xdr, args->bitmap);
 out:
 	dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
@@ -189,6 +190,7 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 	__be32 status;
 
 	args->addr = svc_addr(rqstp);
+	args->bindaddr = svc_daddr(rqstp);
 	status = decode_stateid(xdr, &args->stateid);
 	if (unlikely(status != 0))
 		goto out;
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 9b728f3..6cd8bd5 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -98,6 +98,7 @@ struct rpc_program		nfsacl_program = {
 struct nfs_client_initdata {
 	const char *hostname;
 	const struct sockaddr *addr;
+	const struct sockaddr *bindaddr;
 	size_t addrlen;
 	const struct nfs_rpc_ops *rpc_ops;
 	int proto;
@@ -130,7 +131,8 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 
 	memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
 	clp->cl_addrlen = cl_init->addrlen;
-
+	memcpy(&clp->bindaddr, cl_init->bindaddr, cl_init->addrlen);
+	
 	if (cl_init->hostname) {
 		clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
 		if (!clp->cl_hostname)
@@ -276,7 +278,8 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
  * Find a client by IP address and protocol version
  * - returns NULL if no such client
  */
-struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
+struct nfs_client *nfs_find_client(const struct sockaddr *bindaddr,
+				   const struct sockaddr *addr, u32 nfsversion)
 {
 	struct nfs_client *clp;
 
@@ -293,6 +296,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
 			continue;
 
 		/* Match only the IP address, not the port number */
+		if (!nfs_sockaddr_match_ipaddr(bindaddr, (const struct sockaddr*)(&clp->bindaddr)))
+			continue;
 		if (!nfs_sockaddr_match_ipaddr(addr, clap))
 			continue;
 
@@ -357,6 +362,11 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
 		if (clp->cl_proto != data->proto)
 			continue;
 
+		/* Check to make sure local-IP bindings match, but just the IP-addr. */
+		if (!nfs_sockaddr_match_ipaddr((const struct sockaddr*)(&clp->bindaddr),
+					       data->bindaddr))
+			continue;
+		
 		/* Match the full socket address */
 		if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0)
 			continue;
@@ -493,6 +503,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 	struct rpc_clnt		*clnt = NULL;
 	struct rpc_create_args args = {
 		.protocol	= clp->cl_proto,
+		.saddress	= (struct sockaddr *)&clp->bindaddr,
 		.address	= (struct sockaddr *)&clp->cl_addr,
 		.addrsize	= clp->cl_addrlen,
 		.timeout	= timeparms,
@@ -670,6 +681,7 @@ static int nfs_init_server(struct nfs_server *server,
 		.hostname = data->nfs_server.hostname,
 		.addr = (const struct sockaddr *)&data->nfs_server.address,
 		.addrlen = data->nfs_server.addrlen,
+		.bindaddr = (const struct sockaddr *)&data->bindaddr.address,
 		.rpc_ops = &nfs_v2_clientops,
 		.proto = data->nfs_server.protocol,
 	};
@@ -1035,6 +1047,7 @@ static int nfs4_set_client(struct nfs_server *server,
 		const struct sockaddr *addr,
 		const size_t addrlen,
 		const char *ip_addr,
+		const struct sockaddr* bindaddr,
 		rpc_authflavor_t authflavour,
 		int proto, const struct rpc_timeout *timeparms)
 {
@@ -1042,6 +1055,7 @@ static int nfs4_set_client(struct nfs_server *server,
 		.hostname = hostname,
 		.addr = addr,
 		.addrlen = addrlen,
+		.bindaddr = bindaddr,
 		.rpc_ops = &nfs_v4_clientops,
 		.proto = proto,
 	};
@@ -1096,6 +1110,7 @@ static int nfs4_init_server(struct nfs_server *server,
 			(const struct sockaddr *)&data->nfs_server.address,
 			data->nfs_server.addrlen,
 			data->client_address,
+			(const struct sockaddr *)&data->bindaddr.address,
 			data->auth_flavors[0],
 			data->nfs_server.protocol,
 			&timeparms);
@@ -1214,6 +1229,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 				data->addr,
 				data->addrlen,
 				parent_client->cl_ipaddr,
+				(const struct sockaddr *)(&parent_client->bindaddr),
 				data->authflavor,
 				parent_server->client->cl_xprt->prot,
 				parent_server->client->cl_timeout);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 340ede8..330d10f 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -41,6 +41,12 @@ struct nfs_parsed_mount_data {
 	unsigned int		auth_flavor_len;
 	rpc_authflavor_t	auth_flavors[1];
 	char			*client_address;
+	
+	struct {
+		struct sockaddr_storage	address;
+		size_t			addrlen;
+		char			*hostname;
+	} bindaddr;
 
 	struct {
 		struct sockaddr_storage	address;
@@ -66,6 +72,7 @@ struct nfs_parsed_mount_data {
 /* mount_clnt.c */
 struct nfs_mount_request {
 	struct sockaddr		*sap;
+	struct sockaddr		*bindaddr;
 	size_t			salen;
 	char			*hostname;
 	char			*dirpath;
@@ -81,7 +88,8 @@ extern int nfs_mount(struct nfs_mount_request *info);
 extern struct rpc_program nfs_program;
 
 extern void nfs_put_client(struct nfs_client *);
-extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32);
+extern struct nfs_client *nfs_find_client(const struct sockaddr *,
+					  const struct sockaddr *, u32);
 extern struct nfs_client *nfs_find_client_next(struct nfs_client *);
 extern struct nfs_server *nfs_create_server(
 					const struct nfs_parsed_mount_data *,
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index ca905a5..89acb0c 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -44,6 +44,7 @@ int nfs_mount(struct nfs_mount_request *info)
 	};
 	struct rpc_create_args args = {
 		.protocol	= info->protocol,
+		.saddress	= info->bindaddr,
 		.address	= info->sap,
 		.addrsize	= info->salen,
 		.servername	= info->hostname,
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index d6686f4..90f29bd 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -91,7 +91,7 @@ enum {
 
 	/* Mount options that take string arguments */
 	Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
-	Opt_addr, Opt_mountaddr, Opt_clientaddr,
+	Opt_addr, Opt_mountaddr, Opt_clientaddr, Opt_bindaddr,
 	Opt_lookupcache,
 
 	/* Special mount options */
@@ -155,6 +155,7 @@ static const match_table_t nfs_mount_option_tokens = {
 	{ Opt_mountproto, "mountproto=%s" },
 	{ Opt_addr, "addr=%s" },
 	{ Opt_clientaddr, "clientaddr=%s" },
+	{ Opt_bindaddr, "bindaddr=%s" },
 	{ Opt_mounthost, "mounthost=%s" },
 	{ Opt_mountaddr, "mountaddr=%s" },
 
@@ -563,6 +564,15 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
 	if (clp->rpc_ops->version == 4)
 		seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr);
 #endif
+
+	if (clp->bindaddr.ss_family == AF_INET6) {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(&clp->bindaddr);
+		seq_printf(m, ",bindaddr=%pI6", &sin6->sin6_addr);
+	}
+	else {
+		struct sockaddr_in *sin = (struct sockaddr_in *)(&clp->bindaddr);
+		seq_printf(m, ",bindaddr=%pI4", &sin->sin_addr.s_addr);
+	}
 }
 
 /*
@@ -1251,6 +1261,16 @@ static int nfs_parse_mount_options(char *raw,
 			kfree(mnt->client_address);
 			mnt->client_address = string;
 			break;
+		case Opt_bindaddr:
+			string = match_strdup(args);
+			if (string == NULL)
+				goto out_nomem;
+			nfs_parse_ip_address(string, strlen(string),
+					     (struct sockaddr *)&mnt->bindaddr.address,
+					     &mnt->bindaddr.addrlen);
+			kfree(mnt->bindaddr.hostname);
+			mnt->bindaddr.hostname = string;
+			break;
 		case Opt_mounthost:
 			string = match_strdup(args);
 			if (string == NULL)
@@ -1340,6 +1360,8 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
 	struct nfs_mount_request request = {
 		.sap		= (struct sockaddr *)
 						&args->mount_server.address,
+		.bindaddr	= (struct sockaddr *)&args->bindaddr.address,
+		.salen		= args->mount_server.addrlen,
 		.dirpath	= args->nfs_server.export_path,
 		.protocol	= args->mount_server.protocol,
 		.fh		= root_fh,
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 9bb81ae..ebe612f 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -64,6 +64,9 @@ struct nfs_client {
 	char			cl_ipaddr[48];
 	unsigned char		cl_id_uniquifier;
 #endif
+
+	/* If we should bind to a local IP, it should be specified below. */
+	struct sockaddr_storage	bindaddr;
 };
 
 /*

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-21  7:43           ` Ben Greear
@ 2009-02-21 17:16             ` Trond Myklebust
  2009-02-21 22:09             ` Chuck Lever
  1 sibling, 0 replies; 21+ messages in thread
From: Trond Myklebust @ 2009-02-21 17:16 UTC (permalink / raw)
  To: Ben Greear; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

On Fri, 2009-02-20 at 23:43 -0800, Ben Greear wrote:
> I re-worked the kernel nfs local-address-binding logic against 
> 2.6.29-rc5.  In quick testing,
> this works with IPv4 and NFSv3 at least.  I changed the attribute to be 
> called 'bindaddr'
> as previously suggested.

Most of the changes are to NFSv4-specific code, and I definitely want
those code paths tested before considering this patch.

> I didn't actually make any further changes to the mount.nfs tool and it 
> took the bindaddr=a.b.c.d
> just fine, so maybe there are no changes at all needed in user-space.
> 
> Comments & suggestions welcome.

Remind me again what the application is?

Trond


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-21  7:43           ` Ben Greear
  2009-02-21 17:16             ` Trond Myklebust
@ 2009-02-21 22:09             ` Chuck Lever
  2009-02-22  5:52               ` Ben Greear
                                 ` (2 more replies)
  1 sibling, 3 replies; 21+ messages in thread
From: Chuck Lever @ 2009-02-21 22:09 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-nfs, Patrick McHardy

Hi Ben-

On Feb 21, 2009, at Feb 21, 2009, 2:43 AM, Ben Greear wrote:
> I re-worked the kernel nfs local-address-binding logic against  
> 2.6.29-rc5.  In quick testing,
> this works with IPv4 and NFSv3 at least.  I changed the attribute to  
> be called 'bindaddr'
> as previously suggested.
>
> I didn't actually make any further changes to the mount.nfs tool and  
> it took the bindaddr=a.b.c.d
> just fine, so maybe there are no changes at all needed in user-space.

You probably want the code in support/nfs/getport.c to send requests  
from your bindaddr, if the mount command has to contact the server to  
renegotiate mount options.

> Comments & suggestions welcome.
>
> Thanks,
> Ben
>
> Signed-Off-By:  Ben Greear<greearb@candelatech.com>
>
> -- 
> Ben Greear <greearb@candelatech.com> Candela Technologies Inc  http://www.candelatech.com
>
>
> diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
> index 3e634f2..1c7011e 100644
> --- a/fs/nfs/callback.c
> +++ b/fs/nfs/callback.c
> @@ -213,7 +213,7 @@ static int nfs_callback_authenticate(struct  
> svc_rqst *rqstp)
> 	int ret = SVC_OK;
>
> 	/* Don't talk to strangers */
> -	clp = nfs_find_client(svc_addr(rqstp), 4);
> +	clp = nfs_find_client(svc_daddr(rqstp), svc_addr(rqstp), 4);

It's not clear to me why the callback server needs to be aware of the  
mount point's bind address.  Can you explain this a little more?  I  
would think the bind address would be pertinent for sending callback  
service replies, but I don't see code here to do that.

Would lockd also need to have this information too (passed in via  
nlmclnt_inet)?

Would we also want kernel rpcbind requests to be sensitive to the  
passed-in bind address?

>
> 	if (clp == NULL)
> 		return SVC_DROP;
>
> diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
> index bb25d21..94fd8b4 100644
> --- a/fs/nfs/callback.h
> +++ b/fs/nfs/callback.h
> @@ -39,6 +39,7 @@ struct cb_compound_hdr_res {
>
> struct cb_getattrargs {
> 	struct sockaddr *addr;
> +	struct sockaddr *bindaddr;
> 	struct nfs_fh fh;
> 	uint32_t bitmap[2];
> };
> @@ -54,6 +55,7 @@ struct cb_getattrres {
>
> struct cb_recallargs {
> 	struct sockaddr *addr;
> +	struct sockaddr *bindaddr;
> 	struct nfs_fh fh;
> 	nfs4_stateid stateid;
> 	uint32_t truncate;
> diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
> index f7e83e2..cd75849 100644
> --- a/fs/nfs/callback_proc.c
> +++ b/fs/nfs/callback_proc.c
> @@ -25,7 +25,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs  
> *args, struct cb_getattrres *
>
> 	res->bitmap[0] = res->bitmap[1] = 0;
> 	res->status = htonl(NFS4ERR_BADHANDLE);
> -	clp = nfs_find_client(args->addr, 4);
> +	clp = nfs_find_client(args->bindaddr, args->addr, 4);
> 	if (clp == NULL)
> 		goto out;
>
> @@ -68,7 +68,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs  
> *args, void *dummy)
> 	__be32 res;
> 	
> 	res = htonl(NFS4ERR_BADHANDLE);
> -	clp = nfs_find_client(args->addr, 4);
> +	clp = nfs_find_client(args->bindaddr, args->addr, 4);
> 	if (clp == NULL)
> 		goto out;
>
> diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
> index dd0ef34..a342e8e 100644
> --- a/fs/nfs/callback_xdr.c
> +++ b/fs/nfs/callback_xdr.c
> @@ -177,6 +177,7 @@ static __be32 decode_getattr_args(struct  
> svc_rqst *rqstp, struct xdr_stream *xdr
> 	if (unlikely(status != 0))
> 		goto out;
> 	args->addr = svc_addr(rqstp);
> +	args->bindaddr = svc_daddr(rqstp);
> 	status = decode_bitmap(xdr, args->bitmap);
> out:
> 	dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
> @@ -189,6 +190,7 @@ static __be32 decode_recall_args(struct svc_rqst  
> *rqstp, struct xdr_stream *xdr,
> 	__be32 status;
>
> 	args->addr = svc_addr(rqstp);
> +	args->bindaddr = svc_daddr(rqstp);
> 	status = decode_stateid(xdr, &args->stateid);
> 	if (unlikely(status != 0))
> 		goto out;
> diff --git a/fs/nfs/client.c b/fs/nfs/client.c
> index 9b728f3..6cd8bd5 100644
> --- a/fs/nfs/client.c
> +++ b/fs/nfs/client.c
> @@ -98,6 +98,7 @@ struct rpc_program		nfsacl_program = {
> struct nfs_client_initdata {
> 	const char *hostname;
> 	const struct sockaddr *addr;
> +	const struct sockaddr *bindaddr;
> 	size_t addrlen;
> 	const struct nfs_rpc_ops *rpc_ops;
> 	int proto;
> @@ -130,7 +131,8 @@ static struct nfs_client *nfs_alloc_client(const  
> struct nfs_client_initdata *cl_
>
> 	memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
> 	clp->cl_addrlen = cl_init->addrlen;
> -
> +	memcpy(&clp->bindaddr, cl_init->bindaddr, cl_init->addrlen);
> +	
> 	if (cl_init->hostname) {
> 		clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
> 		if (!clp->cl_hostname)
> @@ -276,7 +278,8 @@ static int nfs_sockaddr_match_ipaddr(const  
> struct sockaddr *sa1,
>  * Find a client by IP address and protocol version
>  * - returns NULL if no such client
>  */
> -struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32  
> nfsversion)
> +struct nfs_client *nfs_find_client(const struct sockaddr *bindaddr,
> +				   const struct sockaddr *addr, u32 nfsversion)
> {
> 	struct nfs_client *clp;
>
> @@ -293,6 +296,8 @@ struct nfs_client *nfs_find_client(const struct  
> sockaddr *addr, u32 nfsversion)
> 			continue;
>
> 		/* Match only the IP address, not the port number */
> +		if (!nfs_sockaddr_match_ipaddr(bindaddr, (const struct sockaddr*) 
> (&clp->bindaddr)))
> +			continue;
> 		if (!nfs_sockaddr_match_ipaddr(addr, clap))
> 			continue;
>
> @@ -357,6 +362,11 @@ static struct nfs_client  
> *nfs_match_client(const struct nfs_client_initdata *dat
> 		if (clp->cl_proto != data->proto)
> 			continue;
>
> +		/* Check to make sure local-IP bindings match, but just the IP- 
> addr. */
> +		if (!nfs_sockaddr_match_ipaddr((const struct sockaddr*)(&clp- 
> >bindaddr),
> +					       data->bindaddr))
> +			continue;
> +		
> 		/* Match the full socket address */
> 		if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0)
> 			continue;
> @@ -493,6 +503,7 @@ static int nfs_create_rpc_client(struct  
> nfs_client *clp,
> 	struct rpc_clnt		*clnt = NULL;
> 	struct rpc_create_args args = {
> 		.protocol	= clp->cl_proto,
> +		.saddress	= (struct sockaddr *)&clp->bindaddr,
> 		.address	= (struct sockaddr *)&clp->cl_addr,
> 		.addrsize	= clp->cl_addrlen,
> 		.timeout	= timeparms,
> @@ -670,6 +681,7 @@ static int nfs_init_server(struct nfs_server  
> *server,
> 		.hostname = data->nfs_server.hostname,
> 		.addr = (const struct sockaddr *)&data->nfs_server.address,
> 		.addrlen = data->nfs_server.addrlen,
> +		.bindaddr = (const struct sockaddr *)&data->bindaddr.address,
> 		.rpc_ops = &nfs_v2_clientops,
> 		.proto = data->nfs_server.protocol,
> 	};
> @@ -1035,6 +1047,7 @@ static int nfs4_set_client(struct nfs_server  
> *server,
> 		const struct sockaddr *addr,
> 		const size_t addrlen,
> 		const char *ip_addr,
> +		const struct sockaddr* bindaddr,
> 		rpc_authflavor_t authflavour,
> 		int proto, const struct rpc_timeout *timeparms)
> {
> @@ -1042,6 +1055,7 @@ static int nfs4_set_client(struct nfs_server  
> *server,
> 		.hostname = hostname,
> 		.addr = addr,
> 		.addrlen = addrlen,
> +		.bindaddr = bindaddr,
> 		.rpc_ops = &nfs_v4_clientops,
> 		.proto = proto,
> 	};
> @@ -1096,6 +1110,7 @@ static int nfs4_init_server(struct nfs_server  
> *server,
> 			(const struct sockaddr *)&data->nfs_server.address,
> 			data->nfs_server.addrlen,
> 			data->client_address,
> +			(const struct sockaddr *)&data->bindaddr.address,
> 			data->auth_flavors[0],
> 			data->nfs_server.protocol,
> 			&timeparms);
> @@ -1214,6 +1229,7 @@ struct nfs_server  
> *nfs4_create_referral_server(struct nfs_clone_mount *data,
> 				data->addr,
> 				data->addrlen,
> 				parent_client->cl_ipaddr,
> +				(const struct sockaddr *)(&parent_client->bindaddr),
> 				data->authflavor,
> 				parent_server->client->cl_xprt->prot,
> 				parent_server->client->cl_timeout);
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index 340ede8..330d10f 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -41,6 +41,12 @@ struct nfs_parsed_mount_data {
> 	unsigned int		auth_flavor_len;
> 	rpc_authflavor_t	auth_flavors[1];
> 	char			*client_address;
> +	
> +	struct {
> +		struct sockaddr_storage	address;
> +		size_t			addrlen;
> +		char			*hostname;
> +	} bindaddr;
>
> 	struct {
> 		struct sockaddr_storage	address;
> @@ -66,6 +72,7 @@ struct nfs_parsed_mount_data {
> /* mount_clnt.c */
> struct nfs_mount_request {
> 	struct sockaddr		*sap;
> +	struct sockaddr		*bindaddr;
> 	size_t			salen;
> 	char			*hostname;
> 	char			*dirpath;
> @@ -81,7 +88,8 @@ extern int nfs_mount(struct nfs_mount_request  
> *info);
> extern struct rpc_program nfs_program;
>
> extern void nfs_put_client(struct nfs_client *);
> -extern struct nfs_client *nfs_find_client(const struct sockaddr *,  
> u32);
> +extern struct nfs_client *nfs_find_client(const struct sockaddr *,
> +					  const struct sockaddr *, u32);
> extern struct nfs_client *nfs_find_client_next(struct nfs_client *);
> extern struct nfs_server *nfs_create_server(
> 					const struct nfs_parsed_mount_data *,
> diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
> index ca905a5..89acb0c 100644
> --- a/fs/nfs/mount_clnt.c
> +++ b/fs/nfs/mount_clnt.c
> @@ -44,6 +44,7 @@ int nfs_mount(struct nfs_mount_request *info)
> 	};
> 	struct rpc_create_args args = {
> 		.protocol	= info->protocol,
> +		.saddress	= info->bindaddr,
> 		.address	= info->sap,
> 		.addrsize	= info->salen,
> 		.servername	= info->hostname,
> diff --git a/fs/nfs/super.c b/fs/nfs/super.c
> index d6686f4..90f29bd 100644
> --- a/fs/nfs/super.c
> +++ b/fs/nfs/super.c
> @@ -91,7 +91,7 @@ enum {
>
> 	/* Mount options that take string arguments */
> 	Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
> -	Opt_addr, Opt_mountaddr, Opt_clientaddr,
> +	Opt_addr, Opt_mountaddr, Opt_clientaddr, Opt_bindaddr,
> 	Opt_lookupcache,
>
> 	/* Special mount options */
> @@ -155,6 +155,7 @@ static const match_table_t  
> nfs_mount_option_tokens = {
> 	{ Opt_mountproto, "mountproto=%s" },
> 	{ Opt_addr, "addr=%s" },
> 	{ Opt_clientaddr, "clientaddr=%s" },
> +	{ Opt_bindaddr, "bindaddr=%s" },
> 	{ Opt_mounthost, "mounthost=%s" },
> 	{ Opt_mountaddr, "mountaddr=%s" },
>
> @@ -563,6 +564,15 @@ static void nfs_show_mount_options(struct  
> seq_file *m, struct nfs_server *nfss,
> 	if (clp->rpc_ops->version == 4)
> 		seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr);
> #endif
> +
> +	if (clp->bindaddr.ss_family == AF_INET6) {
> +		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(&clp- 
> >bindaddr);
> +		seq_printf(m, ",bindaddr=%pI6", &sin6->sin6_addr);
> +	}
> +	else {
> +		struct sockaddr_in *sin = (struct sockaddr_in *)(&clp->bindaddr);
> +		seq_printf(m, ",bindaddr=%pI4", &sin->sin_addr.s_addr);
> +	}
> }
>
> /*
> @@ -1251,6 +1261,16 @@ static int nfs_parse_mount_options(char *raw,
> 			kfree(mnt->client_address);
> 			mnt->client_address = string;
> 			break;
> +		case Opt_bindaddr:
> +			string = match_strdup(args);
> +			if (string == NULL)
> +				goto out_nomem;
> +			nfs_parse_ip_address(string, strlen(string),
> +					     (struct sockaddr *)&mnt->bindaddr.address,
> +					     &mnt->bindaddr.addrlen);
> +			kfree(mnt->bindaddr.hostname);
> +			mnt->bindaddr.hostname = string;

You don't appear to use bindaddr.hostname anywhere, and I don't think  
it is adequate to free it only here.

Maybe you don't need it at all?

> +			break;
> 		case Opt_mounthost:
> 			string = match_strdup(args);
> 			if (string == NULL)
> @@ -1340,6 +1360,8 @@ static int nfs_try_mount(struct  
> nfs_parsed_mount_data *args,
> 	struct nfs_mount_request request = {
> 		.sap		= (struct sockaddr *)
> 						&args->mount_server.address,
> +		.bindaddr	= (struct sockaddr *)&args->bindaddr.address,
> +		.salen		= args->mount_server.addrlen,
> 		.dirpath	= args->nfs_server.export_path,
> 		.protocol	= args->mount_server.protocol,
> 		.fh		= root_fh,
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index 9bb81ae..ebe612f 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -64,6 +64,9 @@ struct nfs_client {
> 	char			cl_ipaddr[48];
> 	unsigned char		cl_id_uniquifier;
> #endif
> +
> +	/* If we should bind to a local IP, it should be specified below. */
> +	struct sockaddr_storage	bindaddr;
> };
>
> /*

-- 
Chuck Lever
chuck[dot]lever[at]oracle[dot]com





^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-21 22:09             ` Chuck Lever
@ 2009-02-22  5:52               ` Ben Greear
  2009-02-22 19:09                 ` Trond Myklebust
  2009-02-22  6:24               ` Ben Greear
  2009-02-22  7:05               ` Ben Greear
  2 siblings, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-02-22  5:52 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

Chuck Lever wrote:
> Hi Ben-
>
> On Feb 21, 2009, at Feb 21, 2009, 2:43 AM, Ben Greear wrote:
>> I re-worked the kernel nfs local-address-binding logic against 
>> 2.6.29-rc5.  In quick testing,
>> this works with IPv4 and NFSv3 at least.  I changed the attribute to 
>> be called 'bindaddr'
>> as previously suggested.
>>
>> I didn't actually make any further changes to the mount.nfs tool and 
>> it took the bindaddr=a.b.c.d
>> just fine, so maybe there are no changes at all needed in user-space.
>
> You probably want the code in support/nfs/getport.c to send requests 
> from your bindaddr, if the mount command has to contact the server to 
> renegotiate mount options.

Any clue as to which part of this code to modify and how to test if it 
actually
does anything useful?

>>
>>     /* Don't talk to strangers */
>> -    clp = nfs_find_client(svc_addr(rqstp), 4);
>> +    clp = nfs_find_client(svc_daddr(rqstp), svc_addr(rqstp), 4);
>
> It's not clear to me why the callback server needs to be aware of the 
> mount point's bind address.  Can you explain this a little more?  I 
> would think the bind address would be pertinent for sending callback 
> service replies, but I don't see code here to do that.
I want to have a unique mount per local IP, so if I mount the same 
server from 1.1.1.1 and from 1.1.1.2, I
want two unique mounts.  I believe the way to do this is to 
differentiate based on the IP addr,
which is what this code is supposed to be doing.
> Would lockd also need to have this information too (passed in via 
> nlmclnt_inet)?
>
> Would we also want kernel rpcbind requests to be sensitive to the 
> passed-in bind address?
As much as possible, I want each mount to appear to be a unique machine 
with a particular
IP..so likely the answer is yes to both the above questions.  If you 
have an idea of which
code should be modified, please point me at it...I'll try searching on 
my own in the meantime.

>> +        case Opt_bindaddr:
>> +            string = match_strdup(args);
>> +            if (string == NULL)
>> +                goto out_nomem;
>> +            nfs_parse_ip_address(string, strlen(string),
>> +                         (struct sockaddr *)&mnt->bindaddr.address,
>> +                         &mnt->bindaddr.addrlen);
>> +            kfree(mnt->bindaddr.hostname);
>> +            mnt->bindaddr.hostname = string;
>
> You don't appear to use bindaddr.hostname anywhere, and I don't think 
> it is adequate to free it only here.
>
> Maybe you don't need it at all?

Yeah, I'll get rid of that.

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-21 22:09             ` Chuck Lever
  2009-02-22  5:52               ` Ben Greear
@ 2009-02-22  6:24               ` Ben Greear
  2009-02-22 20:01                 ` Chuck Lever
  2009-02-22  7:05               ` Ben Greear
  2 siblings, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-02-22  6:24 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

Chuck Lever wrote:
>
> Would lockd also need to have this information too (passed in via 
> nlmclnt_inet)?
I found an fs/lockd directory, but cannot find 'nlmclnt_inet' anywhere 
in the kernel or nfs-utils.

>
> Would we also want kernel rpcbind requests to be sensitive to the 
> passed-in bind address?

It seems from searching net/sunrpc dir that kernel_bind is called 
appropriately which should
bind the a local addr if correct information is passed in, at least.   
This must be working
(assuming NFS calls this code eventually) since I *am* getting at least 
most of the NFS traffic
correctly bound to the specified bindaddr.

I wouldn't be surprised if the binaddr isn't being configured in the 
lockd properly,
but I'm not yet sure of how to get that information to lockd since I 
just started looking
at that code...

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-21 22:09             ` Chuck Lever
  2009-02-22  5:52               ` Ben Greear
  2009-02-22  6:24               ` Ben Greear
@ 2009-02-22  7:05               ` Ben Greear
  2 siblings, 0 replies; 21+ messages in thread
From: Ben Greear @ 2009-02-22  7:05 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, Patrick McHardy

[-- Attachment #1: Type: text/plain, Size: 3231 bytes --]

Chuck Lever wrote:
> Hi Ben-
>
> On Feb 21, 2009, at Feb 21, 2009, 2:43 AM, Ben Greear wrote:
>> I re-worked the kernel nfs local-address-binding logic against 
>> 2.6.29-rc5.  In quick testing,
>> this works with IPv4 and NFSv3 at least.  I changed the attribute to 
>> be called 'bindaddr'
>> as previously suggested.
>>
>> I didn't actually make any further changes to the mount.nfs tool and 
>> it took the bindaddr=a.b.c.d
>> just fine, so maybe there are no changes at all needed in user-space.
>
> You probably want the code in support/nfs/getport.c to send requests 
> from your bindaddr, if the mount command has to contact the server to 
> renegotiate mount options.
>
>> Comments & suggestions welcome.
>>
>> Thanks,
>> Ben
>>
>> Signed-Off-By:  Ben Greear<greearb@candelatech.com>
>>
>> -- 
>> Ben Greear <greearb@candelatech.com> Candela Technologies Inc  
>> http://www.candelatech.com
>>
>>
>> diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
>> index 3e634f2..1c7011e 100644
>> --- a/fs/nfs/callback.c
>> +++ b/fs/nfs/callback.c
>> @@ -213,7 +213,7 @@ static int nfs_callback_authenticate(struct 
>> svc_rqst *rqstp)
>>     int ret = SVC_OK;
>>
>>     /* Don't talk to strangers */
>> -    clp = nfs_find_client(svc_addr(rqstp), 4);
>> +    clp = nfs_find_client(svc_daddr(rqstp), svc_addr(rqstp), 4);
>
> It's not clear to me why the callback server needs to be aware of the 
> mount point's bind address.  Can you explain this a little more?  I 
> would think the bind address would be pertinent for sending callback 
> service replies, but I don't see code here to do that.
>
> Would lockd also need to have this information too (passed in via 
> nlmclnt_inet)?
>
> Would we also want kernel rpcbind requests to be sensitive to the 
> passed-in bind address?

The attached patch (on top of my other patch) is my attempt at making 
lockd aware.

I tried running:

cd [my nfs mount directory]
touch foo
flock -x foo -c "echo hello"

while sniffing the interface it should be bound to, and I see what I 
believe is proper output (just the flock part):

300.647123 192.168.1.185 -> 192.168.1.5  NFS V3 GETATTR Call, FH:0x34b21b2e
300.647460  192.168.1.5 -> 192.168.1.185 NFS V3 GETATTR Reply (Call In 
133)  Regular File mode:0644 uid:0 gid:0
300.647650 192.168.1.185 -> 192.168.1.5  TCP 689 > 2049 [ACK] Seq=5409 
Ack=9565 Win=34560 Len=0
300.647846 192.168.1.185 -> 192.168.1.5  NFS V3 ACCESS Call, FH:0x34b21b2e
300.648046  192.168.1.5 -> 192.168.1.185 NFS V3 ACCESS Reply (Call In 136)
300.648267 192.168.1.185 -> 192.168.1.5  NLM V4 LOCK Call FH:0x34b21b2e 
svid:3 pos:0-0
300.648491  192.168.1.5 -> 192.168.1.185 NLM V4 LOCK Reply (Call In 138)
300.648540 192.168.1.185 -> 192.168.1.5  TCP 839 > 38489 [ACK] Seq=633 
Ack=121 Win=5888 Len=0
300.657602 192.168.1.185 -> 192.168.1.5  NLM V4 UNLOCK Call 
FH:0x34b21b2e svid:3 pos:0-0
300.657894  192.168.1.5 -> 192.168.1.185 NLM V4 UNLOCK Reply (Call In 141)
300.687712 192.168.1.185 -> 192.168.1.5  TCP 689 > 2049 [ACK] Seq=5545 
Ack=9689 Win=34560 Len=0
300.697678 192.168.1.185 -> 192.168.1.5  TCP 839 > 38489 [ACK] Seq=833 
Ack=161 Win=5888 Len=0

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



[-- Attachment #2: lockd_bind.patch --]
[-- Type: text/x-patch, Size: 3581 bytes --]

diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 1f3b0fc..d826c9c 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -59,7 +59,8 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
 	if (status < 0)
 		return ERR_PTR(status);
 
-	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
+	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->bindaddr,
+				   nlm_init->addrlen,
 				   nlm_init->protocol, nlm_version,
 				   nlm_init->hostname, nlm_init->noresvport);
 	if (host == NULL) {
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 99d737b..dc8f067 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -244,15 +244,13 @@ nlm_destroy_host(struct nlm_host *host)
  * created and returned.
  */
 struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
+				     const struct sockaddr* bindaddr,
 				     const size_t salen,
 				     const unsigned short protocol,
 				     const u32 version,
 				     const char *hostname,
 				     int noresvport)
 {
-	const struct sockaddr source = {
-		.sa_family	= AF_UNSPEC,
-	};
 	struct nlm_lookup_host_info ni = {
 		.server		= 0,
 		.sap		= sap,
@@ -261,8 +259,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
 		.version	= version,
 		.hostname	= hostname,
 		.hostname_len	= strlen(hostname),
-		.src_sap	= &source,
-		.src_len	= sizeof(source),
+		.src_sap	= bindaddr,
+		.src_len	= salen,
 		.noresvport	= noresvport,
 	};
 
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 6cd8bd5..7022868 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -551,6 +551,7 @@ static int nfs_start_lockd(struct nfs_server *server)
 	struct nlmclnt_initdata nlm_init = {
 		.hostname	= clp->cl_hostname,
 		.address	= (struct sockaddr *)&clp->cl_addr,
+		.bindaddr	= (struct sockaddr*)&clp->bindaddr,
 		.addrlen	= clp->cl_addrlen,
 		.protocol	= server->flags & NFS_MOUNT_TCP ?
 						IPPROTO_TCP : IPPROTO_UDP,
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 330d10f..f790f4d 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -45,7 +45,6 @@ struct nfs_parsed_mount_data {
 	struct {
 		struct sockaddr_storage	address;
 		size_t			addrlen;
-		char			*hostname;
 	} bindaddr;
 
 	struct {
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 90f29bd..bcfb70a 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1268,8 +1268,6 @@ static int nfs_parse_mount_options(char *raw,
 			nfs_parse_ip_address(string, strlen(string),
 					     (struct sockaddr *)&mnt->bindaddr.address,
 					     &mnt->bindaddr.addrlen);
-			kfree(mnt->bindaddr.hostname);
-			mnt->bindaddr.hostname = string;
 			break;
 		case Opt_mounthost:
 			string = match_strdup(args);
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index fbc48f8..c1a4b6b 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -38,6 +38,7 @@ extern struct nlmsvc_binding *	nlmsvc_ops;
 struct nlmclnt_initdata {
 	const char		*hostname;
 	const struct sockaddr	*address;
+	const struct sockaddr *bindaddr;
 	size_t			addrlen;
 	unsigned short		protocol;
 	u32			nfs_version;
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index aa6fe70..0a06833 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -217,6 +217,7 @@ void		  nlmclnt_next_cookie(struct nlm_cookie *);
  * Host cache
  */
 struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
+				      const struct sockaddr* bindaddr,
 					const size_t salen,
 					const unsigned short protocol,
 					const u32 version,

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-22  5:52               ` Ben Greear
@ 2009-02-22 19:09                 ` Trond Myklebust
       [not found]                   ` <1235329791.7331.75.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
  0 siblings, 1 reply; 21+ messages in thread
From: Trond Myklebust @ 2009-02-22 19:09 UTC (permalink / raw)
  To: Ben Greear; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

On Sat, 2009-02-21 at 21:52 -0800, Ben Greear wrote:
> Chuck Lever wrote:
> > Hi Ben-
> >
> > On Feb 21, 2009, at Feb 21, 2009, 2:43 AM, Ben Greear wrote:
> >> I re-worked the kernel nfs local-address-binding logic against 
> >> 2.6.29-rc5.  In quick testing,
> >> this works with IPv4 and NFSv3 at least.  I changed the attribute to 
> >> be called 'bindaddr'
> >> as previously suggested.
> >>
> >> I didn't actually make any further changes to the mount.nfs tool and 
> >> it took the bindaddr=a.b.c.d
> >> just fine, so maybe there are no changes at all needed in user-space.
> >
> > You probably want the code in support/nfs/getport.c to send requests 
> > from your bindaddr, if the mount command has to contact the server to 
> > renegotiate mount options.
> 
> Any clue as to which part of this code to modify and how to test if it 
> actually
> does anything useful?
> 
> >>
> >>     /* Don't talk to strangers */
> >> -    clp = nfs_find_client(svc_addr(rqstp), 4);
> >> +    clp = nfs_find_client(svc_daddr(rqstp), svc_addr(rqstp), 4);
> >
> > It's not clear to me why the callback server needs to be aware of the 
> > mount point's bind address.  Can you explain this a little more?  I 
> > would think the bind address would be pertinent for sending callback 
> > service replies, but I don't see code here to do that.
> I want to have a unique mount per local IP, so if I mount the same 
> server from 1.1.1.1 and from 1.1.1.2, I
> want two unique mounts.  I believe the way to do this is to 
> differentiate based on the IP addr,
> which is what this code is supposed to be doing.

You can already do that using the -onosharecache option.

I really dislike this idea of adding routing information at the sunrpc
level. So, I repeat: what is the application for all this? I've heard
mutterings about routing and multi-homed servers, but absolutely zero in
the way of specific applications and requirements.

Why can't you for instance do exactly the same thing, simply by setting
up two separate local networks; one for each NIC?

Trond


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-22  6:24               ` Ben Greear
@ 2009-02-22 20:01                 ` Chuck Lever
  0 siblings, 0 replies; 21+ messages in thread
From: Chuck Lever @ 2009-02-22 20:01 UTC (permalink / raw)
  To: Ben Greear; +Cc: linux-nfs, Patrick McHardy

On Feb 22, 2009, at Feb 22, 2009, 1:24 AM, Ben Greear wrote:
> Chuck Lever wrote:
>>
>> Would lockd also need to have this information too (passed in via  
>> nlmclnt_inet)?
> I found an fs/lockd directory, but cannot find 'nlmclnt_inet'  
> anywhere in the kernel or nfs-utils.

I misspelled the name.  I meant nlmclnt_init().  But it looks like you  
already found that.

>> Would we also want kernel rpcbind requests to be sensitive to the  
>> passed-in bind address?
>
> It seems from searching net/sunrpc dir that kernel_bind is called  
> appropriately which should
> bind the a local addr if correct information is passed in, at  
> least.   This must be working
> (assuming NFS calls this code eventually) since I *am* getting at  
> least most of the NFS traffic
> correctly bound to the specified bindaddr.

rpcbind is not the same as a socket bind.  It's a database that maps  
an RPC program name/number to a socket port. Look in linux/net/sunrpc/ 
rpcb_clnt.c.  For the mount command, it's in nfs-utils/support/nfs/ 
{getport,rpc_socket}.c.

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
       [not found]                   ` <1235329791.7331.75.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
@ 2009-02-22 20:29                     ` Chuck Lever
  2009-02-22 22:01                       ` Trond Myklebust
  2009-02-22 23:17                     ` Ben Greear
  1 sibling, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2009-02-22 20:29 UTC (permalink / raw)
  To: Trond Myklebust; +Cc: Linux NFS Mailing List, Patrick McHardy, Ben Greear

On Feb 22, 2009, at Feb 22, 2009, 2:09 PM, Trond Myklebust wrote:
> I really dislike this idea of adding routing information at the sunrpc
> level.

Well I think we have had problems in the past where RPC replies on  
multi-homed systems go to the wrong interface and are dropped.  Isn't  
that why we now have the source address field in rpc_create_args?  I  
suspect we might have similar problems with NSM, for instance.

It is also the case that rpcbind v3 and v4 allow an RPC service to  
register a bind address that is not ANYADDR.  That's what universal  
addresses are for.

So by itself I don't find this feature request terribly objectionable,  
and I think it's worth some discussion.

> So, I repeat: what is the application for all this? I've heard
> mutterings about routing and multi-homed servers, but absolutely  
> zero in
> the way of specific applications and requirements.

I concur that we should get more information about what is at issue.

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-22 20:29                     ` Chuck Lever
@ 2009-02-22 22:01                       ` Trond Myklebust
  0 siblings, 0 replies; 21+ messages in thread
From: Trond Myklebust @ 2009-02-22 22:01 UTC (permalink / raw)
  To: Chuck Lever; +Cc: Linux NFS Mailing List, Patrick McHardy, Ben Greear

On Sun, 2009-02-22 at 15:29 -0500, Chuck Lever wrote:
> On Feb 22, 2009, at Feb 22, 2009, 2:09 PM, Trond Myklebust wrote:
> > I really dislike this idea of adding routing information at the sunrpc
> > level.
> 
> Well I think we have had problems in the past where RPC replies on  
> multi-homed systems go to the wrong interface and are dropped.  Isn't  
> that why we now have the source address field in rpc_create_args?

I fail to see the relevance to this case. We're not talking about RPC
callbacks here.

> I suspect we might have similar problems with NSM, for instance.

See Tom Talpey's talk at Connectathon a few years ago. This is why we
now have the 'nsm_use_hostnames' sysctl, and why the statd callback code
saves FQDNs rather than ip addresses.

Trond


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
       [not found]                   ` <1235329791.7331.75.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
  2009-02-22 20:29                     ` Chuck Lever
@ 2009-02-22 23:17                     ` Ben Greear
  2009-02-22 23:41                       ` Trond Myklebust
  1 sibling, 1 reply; 21+ messages in thread
From: Ben Greear @ 2009-02-22 23:17 UTC (permalink / raw)
  To: Trond Myklebust; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

Trond Myklebust wrote:
>> I want to have a unique mount per local IP, so if I mount the same 
>> server from 1.1.1.1 and from 1.1.1.2, I
>> want two unique mounts.  I believe the way to do this is to 
>> differentiate based on the IP addr,
>> which is what this code is supposed to be doing.
>>     
>
> You can already do that using the -onosharecache option.
>   
I wasn't aware of that option.  Even with it, I think my changes are
useful, at least for my particular use.
> I really dislike this idea of adding routing information at the sunrpc
> level. So, I repeat: what is the application for all this? I've heard
> mutterings about routing and multi-homed servers, but absolutely zero in
> the way of specific applications and requirements.
>
> Why can't you for instance do exactly the same thing, simply by setting
> up two separate local networks; one for each NIC?
>   
My specific application is a testing tool that emulates 1000+ unique NFS 
clients,
primarily for testing  & loading NFS servers.
I put each client on a mac-vlan and put them all on the same subnet so 
that I don't
need any routers between my box and the nfs server.  (We can also put
them on different subnets and use different routers, and the specific 
source-ip
also helps there...)

I use routing tricks to enforce that a particular source-IP uses a 
specific routing
table, and that ties pkts to a specific mac-vlan interface.  The mount 
bindaddr
option then binds a mount to a specific local IP and thus to a specific 
mac-vlan.

This shows 1000+ mounts on my test box, and the nfs server sees 1000
unique clients (all with different MACs, IPS, etc).

It's possible that this is the only useful thing anyone will ever do 
with this option,
and if so, probably not a good enough reason to add it to the tree.  
But, I think
it's more likely that it will also help someone else who is trying to do 
something
we've never considered.

I'm not really making any changes to the sunrpc layer..it already has 
the option
to bind to a local address it seems.  I'm just allowing the user to fill 
in this value
before calling the sunrpc (current code in the tree just uses 'any' for 
the source
address).

If you still see no use for this option, just say so and I'll quit 
bugging you about
it.  I appreciate the help given so far to make the patch work better 
for me,
and can keep it in my private tree easily enough.

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
  2009-02-22 23:17                     ` Ben Greear
@ 2009-02-22 23:41                       ` Trond Myklebust
       [not found]                         ` <1235346094.7331.111.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
  0 siblings, 1 reply; 21+ messages in thread
From: Trond Myklebust @ 2009-02-22 23:41 UTC (permalink / raw)
  To: Ben Greear; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

On Sun, 2009-02-22 at 15:17 -0800, Ben Greear wrote:
> My specific application is a testing tool that emulates 1000+ unique NFS 
> clients,
> primarily for testing  & loading NFS servers.
> I put each client on a mac-vlan and put them all on the same subnet so 
> that I don't
> need any routers between my box and the nfs server.  (We can also put
> them on different subnets and use different routers, and the specific 
> source-ip
> also helps there...)
> 
> I use routing tricks to enforce that a particular source-IP uses a 
> specific routing
> table, and that ties pkts to a specific mac-vlan interface.  The mount 
> bindaddr
> option then binds a mount to a specific local IP and thus to a specific 
> mac-vlan.
> 
> This shows 1000+ mounts on my test box, and the nfs server sees 1000
> unique clients (all with different MACs, IPS, etc).

Any reason why you couldn't multi-home the server too, and use the
destination IP address to control the route on the clients?

  Trond


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
       [not found]                         ` <1235346094.7331.111.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
@ 2009-02-22 23:45                           ` Ben Greear
  0 siblings, 0 replies; 21+ messages in thread
From: Ben Greear @ 2009-02-22 23:45 UTC (permalink / raw)
  To: Trond Myklebust; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

Trond Myklebust wrote:
> On Sun, 2009-02-22 at 15:17 -0800, Ben Greear wrote:
>   
>> My specific application is a testing tool that emulates 1000+ unique NFS 
>> clients,
>> primarily for testing  & loading NFS servers.
>> I put each client on a mac-vlan and put them all on the same subnet so 
>> that I don't
>> need any routers between my box and the nfs server.  (We can also put
>> them on different subnets and use different routers, and the specific 
>> source-ip
>> also helps there...)
>>
>> I use routing tricks to enforce that a particular source-IP uses a 
>> specific routing
>> table, and that ties pkts to a specific mac-vlan interface.  The mount 
>> bindaddr
>> option then binds a mount to a specific local IP and thus to a specific 
>> mac-vlan.
>>
>> This shows 1000+ mounts on my test box, and the nfs server sees 1000
>> unique clients (all with different MACs, IPS, etc).
>>     
>
> Any reason why you couldn't multi-home the server too, and use the
> destination IP address to control the route on the clients?
>   
I'm trying to emulate 1000 NFS clients running against a 'normal' NFS 
server,
and in that case, the NFS server would have a single (or small number of IP
addrs), and each client would have a unique IP and MAC.

Thanks,
Ben

>   Trond
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>   


-- 
Ben Greear <greearb@candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: PATCH:  Support binding to a local IPv4 address when mounting a server.
@ 2009-02-21 18:18 Ben Greear
  0 siblings, 0 replies; 21+ messages in thread
From: Ben Greear @ 2009-02-21 18:18 UTC (permalink / raw)
  To: Trond Myklebust; +Cc: Chuck Lever, linux-nfs, Patrick McHardy

[-- Attachment #1: Type: text/plain, Size: 1255 bytes --]

 I tested nfs4 later last night and it worked fine.  We'll continue to test more....

The purpose is to allow binding to a specific IP on a multi homed device...this allows tricks with routing tables, for instance...

Thanks,
Ben

Trond Myklebust <trond.myklebust@fys.uio.no> wrote:

>On Fri, 2009-02-20 at 23:43 -0800, Ben Greear wrote:
>> I re-worked the kernel nfs local-address-binding logic against 
>> 2.6.29-rc5.  In quick testing,
>> this works with IPv4 and NFSv3 at least.  I changed the attribute to be 
>> called 'bindaddr'
>> as previously suggested.
>
>Most of the changes are to NFSv4-specific code, and I definitely want
>those code paths tested before considering this patch.
>
>> I didn't actually make any further changes to the mount.nfs tool and it 
>> took the bindaddr=a.b.c.d
>> just fine, so maybe there are no changes at all needed in user-space.
>> 
>> Comments & suggestions welcome.
>
>Remind me again what the application is?
>
>Trond
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Sent from my Android phone with K-9. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-02-22 23:45 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-22  1:01 PATCH: Support binding to a local IPv4 address when mounting a server Ben Greear
2009-01-22  2:38 ` Chuck Lever
2009-01-22  5:35   ` Ben Greear
2009-01-22 17:06     ` Chuck Lever
2009-01-22 17:31       ` Ben Greear
2009-01-23 17:18         ` Chuck Lever
2009-01-23 17:39           ` Ben Greear
2009-02-21  7:43           ` Ben Greear
2009-02-21 17:16             ` Trond Myklebust
2009-02-21 22:09             ` Chuck Lever
2009-02-22  5:52               ` Ben Greear
2009-02-22 19:09                 ` Trond Myklebust
     [not found]                   ` <1235329791.7331.75.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
2009-02-22 20:29                     ` Chuck Lever
2009-02-22 22:01                       ` Trond Myklebust
2009-02-22 23:17                     ` Ben Greear
2009-02-22 23:41                       ` Trond Myklebust
     [not found]                         ` <1235346094.7331.111.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
2009-02-22 23:45                           ` Ben Greear
2009-02-22  6:24               ` Ben Greear
2009-02-22 20:01                 ` Chuck Lever
2009-02-22  7:05               ` Ben Greear
2009-02-21 18:18 Ben Greear

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.