diff mbox

[v2] nfs-utils: Support binding to source address.

Message ID 1307574083-27608-1-git-send-email-greearb@candelatech.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ben Greear June 8, 2011, 11:01 p.m. UTC
From: Ben Greear <greearb@candelatech.com>

This lets one specify the source IP address for
sockets, allowing users to leverage routing rules
on multi-homed systems.

Kernel patches to RPC and NFS are needed to complete
full functionality.

Signed-off-by: Ben Greear <greearb@candelatech.com>
---

v2:  Use union nfs_sockaddr in local_bind_info struct.
     Remove cmd-line and getenv parsing.
     Add option parsing to umount logic.
     Update man.nfs page.

:100644 100644 d50fe94... d8ef257... M	support/include/nfsrpc.h
:100644 100644 9af2543... b1089ce... M	support/include/sockaddr.h
:100644 100644 d74400b... e8256ff... M	support/nfs/getport.c
:100644 100644 c14efe8... 0a3fb7a... M	support/nfs/rpc_socket.c
:100644 100644 b1b5793... ccd44a2... M	utils/gssd/gssd.h
:100644 100644 41328c9... e584d20... M	utils/gssd/gssd_proc.c
:100644 100644 f3f0a83... 4e08bc4... M	utils/mount/mount.c
:100644 100644 d1f91dc... fd194a5... M	utils/mount/network.c
:100644 100644 81c6f22... 4af8fd1... M	utils/mount/network.h
:100644 100644 be91a25... bc957f3... M	utils/mount/nfs.man
:100644 100644 b03792e... 8bf379c... M	utils/mount/nfs4_mount.h
:100644 100644 028e7cd... 432947d... M	utils/mount/nfs4mount.c
:100644 100644 2becfb1... 07b9cc3... M	utils/mount/nfs_mount.h
:100644 100644 1298fe4... 7d9a925... M	utils/mount/nfsmount.c
:100644 100644 8cd2852... 03ccf6d... M	utils/mount/nfsumount.c
:100644 100644 f1aa503... 31c41d6... M	utils/mount/stropts.c
:100644 100644 b4fd888... 68cebaa... M	utils/mount/stropts.h
:100644 100644 298db39... c3d3467... M	utils/mount/utils.c
:100644 100644 3fcd504... 8471458... M	utils/mount/utils.h
 support/include/nfsrpc.h   |   23 ++++--
 support/include/sockaddr.h |    6 ++
 support/nfs/getport.c      |   37 ++++++---
 support/nfs/rpc_socket.c   |   97 +++++++++++++++++-------
 utils/gssd/gssd.h          |    2 +
 utils/gssd/gssd_proc.c     |   33 +++++----
 utils/mount/mount.c        |   21 +++--
 utils/mount/network.c      |  181 +++++++++++++++++++++++++++++++++++---------
 utils/mount/network.h      |   20 +++--
 utils/mount/nfs.man        |    6 ++
 utils/mount/nfs4_mount.h   |    5 +-
 utils/mount/nfs4mount.c    |    6 +-
 utils/mount/nfs_mount.h    |    5 +-
 utils/mount/nfsmount.c     |   14 ++--
 utils/mount/nfsumount.c    |   27 ++++++-
 utils/mount/stropts.c      |   15 +++-
 utils/mount/stropts.h      |    2 +-
 utils/mount/utils.c        |    6 +-
 utils/mount/utils.h        |    3 +-
 19 files changed, 374 insertions(+), 135 deletions(-)

Comments

Chuck Lever June 9, 2011, 2:05 p.m. UTC | #1
On Jun 8, 2011, at 7:01 PM, greearb@candelatech.com wrote:

> From: Ben Greear <greearb@candelatech.com>
> 
> This lets one specify the source IP address for
> sockets, allowing users to leverage routing rules
> on multi-homed systems.
> 
> Kernel patches to RPC and NFS are needed to complete
> full functionality.
> 
> Signed-off-by: Ben Greear <greearb@candelatech.com>
> ---
> 
> v2:  Use union nfs_sockaddr in local_bind_info struct.
>     Remove cmd-line and getenv parsing.
>     Add option parsing to umount logic.
>     Update man.nfs page.

Thanks for that, but...

I'm going to side with Neil on this: your solution is invasive.  We need to see much better rationale for the complexity of your design.  Why isn't an automatic solution adequate for your needs?  Don't design for every general case, just solve your immediate problem and explain your solution.

I'm not saying "no" just please show your work more.  Clearly our community has not had to address this problem before now, so we need some explanation to do a proper review.

More below.

> :100644 100644 d50fe94... d8ef257... M	support/include/nfsrpc.h
> :100644 100644 9af2543... b1089ce... M	support/include/sockaddr.h
> :100644 100644 d74400b... e8256ff... M	support/nfs/getport.c
> :100644 100644 c14efe8... 0a3fb7a... M	support/nfs/rpc_socket.c
> :100644 100644 b1b5793... ccd44a2... M	utils/gssd/gssd.h
> :100644 100644 41328c9... e584d20... M	utils/gssd/gssd_proc.c
> :100644 100644 f3f0a83... 4e08bc4... M	utils/mount/mount.c
> :100644 100644 d1f91dc... fd194a5... M	utils/mount/network.c
> :100644 100644 81c6f22... 4af8fd1... M	utils/mount/network.h
> :100644 100644 be91a25... bc957f3... M	utils/mount/nfs.man
> :100644 100644 b03792e... 8bf379c... M	utils/mount/nfs4_mount.h
> :100644 100644 028e7cd... 432947d... M	utils/mount/nfs4mount.c
> :100644 100644 2becfb1... 07b9cc3... M	utils/mount/nfs_mount.h
> :100644 100644 1298fe4... 7d9a925... M	utils/mount/nfsmount.c
> :100644 100644 8cd2852... 03ccf6d... M	utils/mount/nfsumount.c
> :100644 100644 f1aa503... 31c41d6... M	utils/mount/stropts.c
> :100644 100644 b4fd888... 68cebaa... M	utils/mount/stropts.h
> :100644 100644 298db39... c3d3467... M	utils/mount/utils.c
> :100644 100644 3fcd504... 8471458... M	utils/mount/utils.h
> support/include/nfsrpc.h   |   23 ++++--
> support/include/sockaddr.h |    6 ++
> support/nfs/getport.c      |   37 ++++++---
> support/nfs/rpc_socket.c   |   97 +++++++++++++++++-------
> utils/gssd/gssd.h          |    2 +
> utils/gssd/gssd_proc.c     |   33 +++++----
> utils/mount/mount.c        |   21 +++--
> utils/mount/network.c      |  181 +++++++++++++++++++++++++++++++++++---------
> utils/mount/network.h      |   20 +++--
> utils/mount/nfs.man        |    6 ++
> utils/mount/nfs4_mount.h   |    5 +-
> utils/mount/nfs4mount.c    |    6 +-
> utils/mount/nfs_mount.h    |    5 +-
> utils/mount/nfsmount.c     |   14 ++--
> utils/mount/nfsumount.c    |   27 ++++++-
> utils/mount/stropts.c      |   15 +++-
> utils/mount/stropts.h      |    2 +-
> utils/mount/utils.c        |    6 +-
> utils/mount/utils.h        |    3 +-
> 19 files changed, 374 insertions(+), 135 deletions(-)
> 
> diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
> index d50fe94..d8ef257 100644
> --- a/support/include/nfsrpc.h
> +++ b/support/include/nfsrpc.h
> @@ -55,6 +55,8 @@
> #define NSMPROG		((rpcprog_t)100024)
> #endif
> 
> +struct local_bind_info;
> +
> /**
>  * nfs_clear_rpc_createerr - zap all error reporting fields
>  *
> @@ -75,7 +77,8 @@ extern rpcprog_t	nfs_getrpcbyname(const rpcprog_t, const char *table[]);
> extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
> 				const socklen_t, const unsigned short,
> 				const rpcprog_t, const rpcvers_t,
> -				struct timeval *);
> +				struct timeval *,
> +				struct local_bind_info *);
> 
> /*
>  * Acquire an RPC CLIENT * with a privileged source port
> @@ -83,7 +86,8 @@ extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
> extern CLIENT		*nfs_get_priv_rpcclient( const struct sockaddr *,
> 				const socklen_t, const unsigned short,
> 				const rpcprog_t, const rpcvers_t,
> -				struct timeval *);
> +				struct timeval *,
> +				struct local_bind_info *);
> 
> /*
>  * Convert a netid to a protocol number and protocol family
> @@ -116,7 +120,8 @@ extern int		nfs_getport_ping(struct sockaddr *sap,
> 				const socklen_t salen,
> 				const rpcprog_t program,
> 				const rpcvers_t version,
> -				const unsigned short protocol);
> +				const unsigned short protocol,
> +				struct local_bind_info *local_ip);
> 
> /*
>  * Generic function that maps an RPC service tuple to an IP port
> @@ -124,14 +129,16 @@ extern int		nfs_getport_ping(struct sockaddr *sap,
>  */
> extern unsigned short	nfs_getport(const struct sockaddr *,
> 				const socklen_t, const rpcprog_t,
> -				const rpcvers_t, const unsigned short);
> +				const rpcvers_t, const unsigned short,
> +				struct local_bind_info *local_ip);
> 
> /*
>  * Generic function that maps an RPC service tuple to an IP port
>  * number of the service on the local host
>  */
> extern unsigned short	nfs_getlocalport(const rpcprot_t,
> -				const rpcvers_t, const unsigned short);
> +				const rpcvers_t, const unsigned short,
> +				struct local_bind_info *local_ip);
> 
> /*
>  * Function to invoke an rpcbind v3/v4 GETADDR request
> @@ -153,7 +160,8 @@ extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
> 				const unsigned long,
> 				const unsigned long,
> 				const unsigned long,
> -				const struct timeval *);
> +				const struct timeval *,
> +				struct local_bind_info *local_ip);
> 
> /*
>  * Contact a remote RPC service to discover whether it is responding
> @@ -164,7 +172,8 @@ extern int		nfs_rpc_ping(const struct sockaddr *sap,
> 				const rpcprog_t program,
> 				const rpcvers_t version,
> 				const unsigned short protocol,
> -				const struct timeval *timeout);
> +				const struct timeval *timeout,
> +				struct local_bind_info *local_ip);
> 
> /* create AUTH_SYS handle with no supplemental groups */
> extern AUTH *			 nfs_authsys_create(void);
> diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
> index 9af2543..b1089ce 100644
> --- a/support/include/sockaddr.h
> +++ b/support/include/sockaddr.h
> @@ -46,6 +46,12 @@ union nfs_sockaddr {
> 	struct sockaddr_in6	s6;
> };
> 
> +struct local_bind_info {
> +	union nfs_sockaddr addr;
> +	socklen_t addrlen;
> +	bool is_set;
> +};

Please use "struct sockaddr *" or provide a very clear rationale (not "we may need this someday") for why "struct sockaddr *" is not adequate for solving your immediate problem.

> +
> #if SIZEOF_SOCKLEN_T - 0 == 0
> #define socklen_t unsigned int
> #endif
> diff --git a/support/nfs/getport.c b/support/nfs/getport.c
> index d74400b..e8256ff 100644
> --- a/support/nfs/getport.c
> +++ b/support/nfs/getport.c
> @@ -181,7 +181,8 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
> 				     const socklen_t salen,
> 				     const unsigned short transport,
> 				     const rpcvers_t version,
> -				     struct timeval *timeout)
> +				     struct timeval *timeout,
> +				     struct local_bind_info *local_ip)
> {
> 	static const char *rpcb_pgmtbl[] = {
> 		"rpcbind",
> @@ -195,7 +196,7 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
> 
> 	nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport)));
> 	clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog,
> -							version, timeout);
> +				 version, timeout, local_ip);
> 	nfs_gp_map_tcp_errorcodes(transport);
> 	return clnt;
> }
> @@ -729,7 +730,8 @@ static unsigned short nfs_gp_getport(CLIENT *client,
>  */
> int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
> 		 const rpcprog_t program, const rpcvers_t version,
> -		 const unsigned short protocol, const struct timeval *timeout)
> +		 const unsigned short protocol, const struct timeval *timeout,
> +		 struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *saddr = &address.sa;
> @@ -744,7 +746,7 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
> 
> 	memcpy(saddr, sap, (size_t)salen);
> 	client = nfs_get_rpcclient(saddr, salen, protocol,
> -						program, version, &tout);
> +				   program, version, &tout, local_ip);
> 	if (client != NULL) {
> 		result = nfs_gp_ping(client, tout);
> 		nfs_gp_map_tcp_errorcodes(protocol);
> @@ -798,7 +800,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
> 			   const socklen_t salen,
> 			   const rpcprog_t program,
> 			   const rpcvers_t version,
> -			   const unsigned short protocol)
> +			   const unsigned short protocol,
> +			   struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *saddr = &address.sa;
> @@ -810,7 +813,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
> 
> 	memcpy(saddr, sap, (size_t)salen);
> 	client = nfs_gp_get_rpcbclient(saddr, salen, protocol,
> -						default_rpcb_version, &timeout);
> +				       default_rpcb_version, &timeout,
> +				       local_ip);
> 	if (client != NULL) {
> 		port = nfs_gp_getport(client, saddr, program,
> 					version, protocol, timeout);
> @@ -840,7 +844,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
>  */
> int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
> 		     const rpcprog_t program, const rpcvers_t version,
> -		     const unsigned short protocol)
> +		     const unsigned short protocol,
> +		     struct local_bind_info *local_ip)
> {
> 	struct timeval timeout = { -1, 0 };
> 	unsigned short port = 0;
> @@ -850,7 +855,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
> 	nfs_clear_rpc_createerr();
> 
> 	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
> -						default_rpcb_version, &timeout);
> +				       default_rpcb_version, &timeout,
> +				       local_ip);
> 	if (client != NULL) {
> 		port = nfs_gp_getport(client, sap, program,
> 					version, protocol, timeout);
> @@ -868,7 +874,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
> 		nfs_clear_rpc_createerr();
> 
> 		client = nfs_get_rpcclient(saddr, salen, protocol,
> -						program, version, &timeout);
> +					   program, version, &timeout,
> +					   local_ip);
> 		if (client != NULL) {
> 			result = nfs_gp_ping(client, timeout);
> 			nfs_gp_map_tcp_errorcodes(protocol);
> @@ -909,7 +916,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
>  */
> unsigned short nfs_getlocalport(const rpcprot_t program,
> 				const rpcvers_t version,
> -				const unsigned short protocol)
> +				const unsigned short protocol,
> +				struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *lb_addr = &address.sa;
> @@ -946,7 +954,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
> 
> 		if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
> 			port = nfs_getport(lb_addr, lb_len,
> -						program, version, protocol);
> +					   program, version, protocol,
> +					   local_ip);
> 		} else
> 			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
> 	}
> @@ -1074,7 +1083,8 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
> 			       const unsigned long program,
> 			       const unsigned long version,
> 			       const unsigned long protocol,
> -			       const struct timeval *timeout)
> +			       const struct timeval *timeout,
> +			       struct local_bind_info *local_ip)
> {
> 	struct sockaddr_in address;
> 	struct sockaddr *saddr = (struct sockaddr *)&address;
> @@ -1094,7 +1104,8 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
> 
> 	memcpy(saddr, sin, sizeof(address));
> 	client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin),
> -					transport, PMAPVERS, &tout);
> +				       transport, PMAPVERS, &tout,
> +				       local_ip);
> 	if (client != NULL) {
> 		port = nfs_gp_pmap_getport(client, &parms, tout);
> 		CLNT_DESTROY(client);
> diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
> index c14efe8..0a3fb7a 100644
> --- a/support/nfs/rpc_socket.c
> +++ b/support/nfs/rpc_socket.c
> @@ -112,8 +112,12 @@ 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(const int sock, const sa_family_t family,
> +		    struct local_bind_info *local_ip)
> {
> +	struct sockaddr *sa = NULL;
> +	socklen_t salen = 0;
> +
> 	struct sockaddr_in sin = {
> 		.sin_family		= AF_INET,
> 		.sin_addr.s_addr	= htonl(INADDR_ANY),
> @@ -123,15 +127,26 @@ static int nfs_bind(const int sock, const sa_family_t family)
> 		.sin6_addr		= IN6ADDR_ANY_INIT,
> 	};
> 
> -	switch (family) {
> -	case AF_INET:
> -		return bind(sock, (struct sockaddr *)(char *)&sin,
> -					(socklen_t)sizeof(sin));
> -	case AF_INET6:
> -		return bind(sock, (struct sockaddr *)(char *)&sin6,
> -					(socklen_t)sizeof(sin6));
> +	if (local_ip && local_ip->is_set) {
> +		sa = &local_ip->addr.sa;
> +		salen = local_ip->addrlen;
> +	} else {
> +		switch (family) {
> +		case AF_INET:
> +			sa = (struct sockaddr *)&sin;
> +			salen = sizeof(sin);
> +			break;
> +		case AF_INET6:
> +			sa = (struct sockaddr *)&sin6;
> +			salen = sizeof(sin6);
> +		default:
> +			break;
> +		}
> 	}
> 
> +	if (sa)
> +		return bind(sock, sa, salen);
> +
> 	errno = EAFNOSUPPORT;
> 	return -1;
> }
> @@ -144,8 +159,10 @@ static int nfs_bind(const int sock, const sa_family_t family)
>  * Returns zero on success, or returns -1 on error.  errno is
>  * set to reflect the nature of the error.
>  */
> -static int nfs_bindresvport(const int sock, const sa_family_t family)
> +static int nfs_bindresvport(const int sock, const sa_family_t family,
> +			    struct local_bind_info *local_ip)
> {
> +	struct sockaddr *sa = NULL;
> 	struct sockaddr_in sin = {
> 		.sin_family		= AF_INET,
> 		.sin_addr.s_addr	= htonl(INADDR_ANY),
> @@ -155,13 +172,23 @@ static int nfs_bindresvport(const int sock, const sa_family_t family)
> 		.sin6_addr		= IN6ADDR_ANY_INIT,
> 	};
> 
> -	switch (family) {
> -	case AF_INET:
> -		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
> -	case AF_INET6:
> -		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
> +	if (local_ip && local_ip->is_set) {
> +		sa = &local_ip->addr.sa;
> +	} else {
> +		switch (family) {
> +		case AF_INET:
> +			sa = (struct sockaddr *)&sin;
> +			break;
> +		case AF_INET6:
> +			sa = (struct sockaddr *)&sin6;
> +		default:
> +			break;
> +		}
> 	}
> 
> +	if (sa)
> +		return bindresvport_sa(sock, sa);
> +
> 	errno = EAFNOSUPPORT;
> 	return -1;
> }
> @@ -174,14 +201,24 @@ static int nfs_bindresvport(const int sock, const sa_family_t family)
>  * Returns zero on success, or returns -1 on error.  errno is
>  * set to reflect the nature of the error.
>  */
> -static int nfs_bindresvport(const int sock, const sa_family_t family)
> +static int nfs_bindresvport(const int sock, const sa_family_t family,
> +			    struct local_bind_info *local_ip)
> {
> +	struct sockaddr_in laddr;
> 	if (family != AF_INET) {
> 		errno = EAFNOSUPPORT;
> 		return -1;
> 	}
> 
> -	return bindresvport(sock, NULL);
> +	laddr.sin_family = family;
> +	laddr.sin_port = 0;
> +	if (local_ip && local_ip->is_set) {
> +		struct sockaddr_in *si = &local_ip->addr.sa;
> +		laddr.sin_addr.s_addr = si->sin_addr.s_addr;
> +	} else {
> +		laddr.sin_addr.s_addr = htonl(INADDR_ANY);
> +	}
> +	return bindresvport(sock, &laddr);
> }
> 
> #endif	/* !HAVE_LIBTIRPC */
> @@ -273,7 +310,8 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
> 				 const rpcprog_t program,
> 				 const rpcvers_t version,
> 				 struct timeval *timeout,
> -				 const int resvport)
> +				 const int resvport,
> +				 struct local_bind_info *local_ip)
> {
> 	CLIENT *client;
> 	int ret, sock;
> @@ -301,9 +339,9 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
> 	}
> 
> 	if (resvport)
> -		ret = nfs_bindresvport(sock, sap->sa_family);
> +		ret = nfs_bindresvport(sock, sap->sa_family, local_ip);
> 	else
> -		ret = nfs_bind(sock, sap->sa_family);
> +		ret = nfs_bind(sock, sap->sa_family, local_ip);
> 	if (ret < 0) {
> 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
> 		rpc_createerr.cf_error.re_errno = errno;
> @@ -355,7 +393,8 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
> 				 const rpcprog_t program,
> 				 const rpcvers_t version,
> 				 struct timeval *timeout,
> -				 const int resvport)
> +				 const int resvport,
> +				 struct local_bind_info *local_ip)
> {
> 	CLIENT *client;
> 	int ret, sock;
> @@ -383,9 +422,9 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
> 	}
> 
> 	if (resvport)
> -		ret = nfs_bindresvport(sock, sap->sa_family);
> +		ret = nfs_bindresvport(sock, sap->sa_family, local_ip);
> 	else
> -		ret = nfs_bind(sock, sap->sa_family);
> +		ret = nfs_bind(sock, sap->sa_family, local_ip);
> 	if (ret < 0) {
> 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
> 		rpc_createerr.cf_error.re_errno = errno;
> @@ -442,7 +481,8 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
> 			  const unsigned short transport,
> 			  const rpcprog_t program,
> 			  const rpcvers_t version,
> -			  struct timeval *timeout)
> +			  struct timeval *timeout,
> +			  struct local_bind_info *local_ip)
> {
> 	nfs_clear_rpc_createerr();
> 
> @@ -465,11 +505,11 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
> 	switch (transport) {
> 	case IPPROTO_TCP:
> 		return nfs_get_tcpclient(sap, salen, program, version,
> -						timeout, 0);
> +					 timeout, 0, local_ip);
> 	case 0:
> 	case IPPROTO_UDP:
> 		return nfs_get_udpclient(sap, salen, program, version,
> -						timeout, 0);
> +					 timeout, 0, local_ip);
> 	}
> 
> 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
> @@ -499,7 +539,8 @@ CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
> 			       const unsigned short transport,
> 			       const rpcprog_t program,
> 			       const rpcvers_t version,
> -			       struct timeval *timeout)
> +			       struct timeval *timeout,
> +			       struct local_bind_info *local_ip)
> {
> 	nfs_clear_rpc_createerr();
> 
> @@ -522,11 +563,11 @@ CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
> 	switch (transport) {
> 	case IPPROTO_TCP:
> 		return nfs_get_tcpclient(sap, salen, program, version,
> -						timeout, 1);
> +					 timeout, 1, local_ip);
> 	case 0:
> 	case IPPROTO_UDP:
> 		return nfs_get_udpclient(sap, salen, program, version,
> -						timeout, 1);
> +					 timeout, 1, local_ip);
> 	}
> 
> 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
> diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
> index b1b5793..ccd44a2 100644
> --- a/utils/gssd/gssd.h
> +++ b/utils/gssd/gssd.h
> @@ -34,6 +34,7 @@
> #include <sys/types.h>
> #include <sys/queue.h>
> #include <gssapi/gssapi.h>
> +#include "sockaddr.h"
> 
> #define MAX_FILE_NAMELEN	32
> #define FD_ALLOC_BLOCK		256
> @@ -85,6 +86,7 @@ struct clnt_info {
> 	int                     gssd_fd;
> 	int                     gssd_poll_index;
> 	struct sockaddr_storage addr;
> +	struct local_bind_info  local_ip;
> };
> 
> TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list;
> diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
> index 41328c9..e584d20 100644
> --- a/utils/gssd/gssd_proc.c
> +++ b/utils/gssd/gssd_proc.c
> @@ -726,7 +726,8 @@ out_err:
> static int
> populate_port(struct sockaddr *sa, const socklen_t salen,
> 	      const rpcprog_t program, const rpcvers_t version,
> -	      const unsigned short protocol)
> +	      const unsigned short protocol,
> +	      struct local_bind_info *local_ip)
> {
> 	struct sockaddr_in	*s4 = (struct sockaddr_in *) sa;
> #ifdef IPV6_SUPPORTED
> @@ -774,7 +775,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen,
> 		goto set_port;
> 	}
> 
> -	port = nfs_getport(sa, salen, program, version, protocol);
> +	port = nfs_getport(sa, salen, program, version, protocol, local_ip);
> 	if (!port) {
> 		printerr(0, "ERROR: unable to obtain port for prog %ld "
> 			    "vers %ld\n", program, version);
> @@ -807,7 +808,8 @@ int create_auth_rpc_client(struct clnt_info *clp,
> 			   CLIENT **clnt_return,
> 			   AUTH **auth_return,
> 			   uid_t uid,
> -			   int authtype)
> +			   int authtype,
> +			   struct local_bind_info *local_ip)
> {
> 	CLIENT			*rpc_clnt = NULL;
> 	struct rpc_gss_sec	sec;
> @@ -899,11 +901,12 @@ int create_auth_rpc_client(struct clnt_info *clp,
> 		goto out_fail;
> 	}
> 
> -	if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
> +	if (!populate_port(addr, salen, clp->prog, clp->vers,
> +			   protocol, local_ip))
> 		goto out_fail;
> 
> 	rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
> -				     clp->vers, &timeout);
> +				     clp->vers, &timeout, local_ip);
> 	if (!rpc_clnt) {
> 		snprintf(rpc_errmsg, sizeof(rpc_errmsg),
> 			 "WARNING: can't create %s rpc_clnt to server %s for "
> @@ -955,7 +958,7 @@ int create_auth_rpc_client(struct clnt_info *clp,
>  */
> static void
> process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
> -		    char *service)
> +		    char *service, struct local_bind_info *local_ip)
> {
> 	CLIENT			*rpc_clnt = NULL;
> 	AUTH			*auth = NULL;
> @@ -1011,7 +1014,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
> 				downcall_err = -EKEYEXPIRED;
> 			else if (!err)
> 				create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
> -							     AUTHTYPE_KRB5);
> +								     AUTHTYPE_KRB5, local_ip);
> 			if (create_resp == 0)
> 				break;
> 		}
> @@ -1038,7 +1041,8 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
> 					gssd_setup_krb5_machine_gss_ccache(*ccname);
> 					if ((create_auth_rpc_client(clp, &rpc_clnt,
> 								    &auth, uid,
> -								    AUTHTYPE_KRB5)) == 0) {
> +								    AUTHTYPE_KRB5,
> +								    local_ip)) == 0) {
> 						/* Success! */
> 						success++;
> 						break;
> @@ -1108,7 +1112,8 @@ out_return_error:
>  * context on behalf of the kernel
>  */
> static void
> -process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
> +process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd,
> +		     struct local_bind_info *local_ip)
> {
> 	CLIENT			*rpc_clnt = NULL;
> 	AUTH			*auth = NULL;
> @@ -1120,7 +1125,7 @@ process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
> 	token.length = 0;
> 	token.value = NULL;
> 
> -	if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
> +	if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3, local_ip)) {
> 		printerr(0, "WARNING: Failed to create spkm3 context for "
> 			    "user with uid %d\n", uid);
> 		goto out_return_error;
> @@ -1167,7 +1172,7 @@ handle_krb5_upcall(struct clnt_info *clp)
> 		return;
> 	}
> 
> -	return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
> +	process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL, &clp->local_ip);
> }
> 
> void
> @@ -1181,7 +1186,7 @@ handle_spkm3_upcall(struct clnt_info *clp)
> 		return;
> 	}
> 
> -	return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
> +	process_spkm3_upcall(clp, uid, clp->spkm3_fd, &clp->local_ip);
> }
> 
> void
> @@ -1291,9 +1296,9 @@ handle_gssd_upcall(struct clnt_info *clp)
> 	}
> 
> 	if (strcmp(mech, "krb5") == 0)
> -		process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
> +		process_krb5_upcall(clp, uid, clp->gssd_fd, target, service, &clp->local_ip);
> 	else if (strcmp(mech, "spkm3") == 0)
> -		process_spkm3_upcall(clp, uid, clp->gssd_fd);
> +		process_spkm3_upcall(clp, uid, clp->gssd_fd, &clp->local_ip);
> 	else
> 		printerr(0, "WARNING: handle_gssd_upcall: "
> 			    "received unknown gss mech '%s'\n", mech);
> diff --git a/utils/mount/mount.c b/utils/mount/mount.c
> index f3f0a83..4e08bc4 100644
> --- a/utils/mount/mount.c
> +++ b/utils/mount/mount.c
> @@ -48,6 +48,7 @@
> #include "error.h"
> #include "stropts.h"
> #include "utils.h"
> +#include "network.h"
> 
> char *progname;
> int nfs_mount_data_version;
> @@ -55,6 +56,7 @@ int nomtab;
> int verbose;
> int sloppy;
> int string;
> +struct local_bind_info glb_local_ip;
> 
> #define FOREGROUND	(0)
> #define BACKGROUND	(1)
> @@ -305,6 +307,9 @@ static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
> 
> 	if ((len -= strlen(opt)) > 0)
> 		strcat(extra_opts, opt);
> +
> +	if (strncmp(opt, "srcaddr=", strlen("srcaddr=")) == 0)
> +		parse_local_bind(&glb_local_ip, opt + strlen("srcaddr="));
> }

Why is this needed?  Is it just to support legacy (non-string) mounts?

> /*
> @@ -345,21 +350,21 @@ static void parse_opts(const char *options, int *flags, char **extra_opts)
> }
> 
> static int try_mount(char *spec, char *mount_point, int flags,
> -			char *fs_type, char **extra_opts, char *mount_opts,
> -			int fake, int bg)
> +		     char *fs_type, char **extra_opts, char *mount_opts,
> +		     int fake, int bg, struct local_bind_info *local_ip)
> {
> 	int ret;
> 
> 	if (string)
> 		ret = nfsmount_string(spec, mount_point, fs_type, flags,
> -					extra_opts, fake, bg);
> +				      extra_opts, fake, bg, local_ip);
> 	else {
> 		if (strcmp(fs_type, "nfs4") == 0)
> 			ret = nfs4mount(spec, mount_point, flags,
> -					extra_opts, fake, bg);
> +					extra_opts, fake, bg, local_ip);
> 		else
> 			ret = nfsmount(spec, mount_point, flags,
> -					extra_opts, fake, bg);
> +				       extra_opts, fake, bg, local_ip);
> 	}

You're better off not supporting srcaddr= for legacy mounts.  The kernel will never ever support adding a srcaddr field to the nfs_mount_data blob, so why go to the trouble in user space?

> 	if (ret)
> @@ -515,7 +520,7 @@ int main(int argc, char *argv[])
> 	}
> 
> 	mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts,
> -				mount_opts, fake, FOREGROUND);
> +			    mount_opts, fake, FOREGROUND, &glb_local_ip);
> 	if (mnt_err == EX_BG) {
> 		printf(_("%s: backgrounding \"%s\"\n"),
> 			progname, spec);
> @@ -535,8 +540,8 @@ int main(int argc, char *argv[])
> 		}
> 
> 		mnt_err = try_mount(spec, mount_point, flags, fs_type,
> -					&extra_opts, mount_opts, fake,
> -					BACKGROUND);
> +				    &extra_opts, mount_opts, fake,
> +				    BACKGROUND, &glb_local_ip);
> 		if (verbose && mnt_err)
> 			printf(_("%s: giving up \"%s\"\n"),
> 				progname, spec);
> diff --git a/utils/mount/network.c b/utils/mount/network.c
> index d1f91dc..fd194a5 100644
> --- a/utils/mount/network.c
> +++ b/utils/mount/network.c
> @@ -404,30 +404,123 @@ out:
> 	return 0;
> }
> 
> +
> +void
> +parse_local_bind(struct local_bind_info *laddr, const char* str) {
> +	/* str is an IP address. */
> +	int aiErr;
> +	unsigned int i;
> +	struct addrinfo *aiHead;
> +	struct addrinfo hints;
> +	char *node = NULL; /* ip addr */
> +	char *service = NULL; /* port */
> +	char *tmp = xstrdup(str);
> +
> +	laddr->is_set = 0;
> +
> +	memset(&hints, 0, sizeof(hints));
> +
> +	hints.ai_flags  = AI_NUMERICSERV;
> +	hints.ai_socktype = SOCK_STREAM;
> +	hints.ai_protocol = IPPROTO_TCP;
> +
> +	if (str[0] == '[') {

Please don't.  The square bracket thing is just for the NFS special device.  It really isn't needed for a mount option that takes just a presentation IP address.  Or, are you expecting the user to specify a source port number here as well?

Since parse_local_bind() is a globally visible function, please add a documenting comment that lists parameters (both input and output) and how it is expected to behave.

> +		/* IPv6 addr */
> +		hints.ai_family = PF_INET6;
> +		node = tmp + 1;
> +		for (i = 0; i < strlen(node); i++) {
> +			if (node[i] == ']') {
> +				node[i] = 0;
> +				service = &(node[i+1]);
> +				break;
> +			}
> +		}
> +	} else {
> +		hints.ai_family = PF_INET;
> +		node = tmp;
> +		service = node;
> +	}
> +
> +	if (service) {
> +		int found_port = 0;
> +		for (i = 0; i < strlen(service); i++) {
> +			if (service[i] == ':') {
> +				service += i+1;
> +				found_port = 1;
> +				break;
> +			}
> +		}
> +		if (!found_port)
> +			service = NULL;
> +	}
> +
> +	aiErr = getaddrinfo(node, service, &hints, &aiHead);
> +
> +	/* If we tried PF_INET and it failed, try IPv6 instead
> +	 * to see if it resolves properly.
> +	 */
> +	if ((aiErr != 0) && (hints.ai_family == PF_INET)) {
> +		hints.ai_family = PF_INET6;
> +		aiErr = getaddrinfo(node, service, &hints, &aiHead);
> +	}
> +
> +	if (aiErr != 0) {
> +		printf("node: %s service: %s  ai_family: %s  aiErr: %i %s\n",
> +		       node, service,
> +		       hints.ai_family == PF_INET6 ? "INET6" : "INET",
> +		       aiErr, gai_strerror(aiErr));
> +		perror("getaddrinfo");
> +	} else {
> +		if (aiHead) {
> +			memcpy(&laddr->addr, aiHead->ai_addr, aiHead->ai_addrlen);
> +			laddr->addrlen = aiHead->ai_addrlen;
> +			laddr->is_set = true;
> +			freeaddrinfo(aiHead);
> +		}
> +	}
> +	free(tmp);
> +}
> +
> /*
>  * Create a socket that is locally bound to a reserved or non-reserved port.
>  *
>  * 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)
> +		      unsigned int timeout, int resvp, int conn,
> +		      struct local_bind_info *local_ip)
> {
> 	int so, cc, type;
> 	struct sockaddr_in laddr;
> 	socklen_t namelen = sizeof(laddr);
> +	int f = AF_INET;
> +
> +	if (local_ip && local_ip->is_set)
> +		f = local_ip->addr.sa.sa_family;
> 
> 	type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
> -	if ((so = socket (AF_INET, type, p_prot)) < 0)
> +
> +	so = socket(f, type, p_prot);
> +	if (so < 0)
> 		goto err_socket;
> 
> -	laddr.sin_family = AF_INET;
> +	laddr.sin_family = f;
> 	laddr.sin_port = 0;
> 	laddr.sin_addr.s_addr = htonl(INADDR_ANY);
> 	if (resvp) {
> +		/* TODO:  Support IPv6 */
> +		if (local_ip && local_ip->is_set
> +		    && local_ip->addr.sa.sa_family == AF_INET) {
> +			struct sockaddr_in *si = &local_ip->addr.s4;
> +			laddr.sin_addr.s_addr = si->sin_addr.s_addr;
> +		}
> 		if (bindresvport(so, &laddr) < 0)
> 			goto err_bindresvport;
> 	} else {
> -		cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
> +		if (local_ip && local_ip->is_set)
> +			cc = bind(so, &local_ip->addr.sa, local_ip->addrlen);
> +		else
> +			cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
> 		if (cc < 0)
> 			goto err_bind;
> 	}
> @@ -537,7 +630,8 @@ static void nfs_pp_debug2(const char *str)
>  */
> static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
> 			  struct pmap *pmap, const unsigned long *versions,
> -			  const unsigned int *protos)
> +			  const unsigned int *protos,
> +			  struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *saddr = &address.sa;
> @@ -555,14 +649,16 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
> 		if (verbose)
> 			printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"),
> 				progname, prog, *p_vers, *p_prot);
> -		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
> +		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot,
> +				     local_ip);
> 		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,
> -							*p_vers, *p_prot, NULL))
> +						 *p_vers, *p_prot, NULL,
> +						 local_ip))
> 					goto out_ok;
> 			} else
> 				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
> @@ -615,7 +711,8 @@ out_ok:
>  * 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)
> +			     struct pmap *pmap,
> +			     struct local_bind_info *local_ip)
> {
> 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
> 		return 1;
> @@ -626,10 +723,12 @@ static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
> 		probe_proto = nfs_default_proto();
> 
> 		return nfs_probe_port(sap, salen, pmap,
> -					probe_nfs3_first, probe_proto);
> +				      probe_nfs3_first, probe_proto,
> +				      local_ip);
> 	} else
> 		return nfs_probe_port(sap, salen, pmap,
> -					probe_nfs2_only, probe_udp_only);
> +				      probe_nfs2_only, probe_udp_only,
> +				      local_ip);
> }
> 
> /*
> @@ -646,17 +745,20 @@ static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
>  * 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,
> -				struct pmap *pmap)
> +			     struct pmap *pmap,
> +			     struct local_bind_info *local_ip)
> {
> 	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,
> -					probe_mnt3_first, probe_udp_first);
> +				      probe_mnt3_first, probe_udp_first,
> +				      local_ip);
> 	else
> 		return nfs_probe_port(sap, salen, pmap,
> -					probe_mnt1_first, probe_udp_only);
> +				      probe_mnt1_first, probe_udp_only,
> +				      local_ip);
> }
> 
> /*
> @@ -673,11 +775,12 @@ static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
> 			struct pmap *mnt_pmap,
> 			const struct sockaddr *nfs_saddr,
> 			const socklen_t nfs_salen,
> -			struct pmap *nfs_pmap)
> +			struct pmap *nfs_pmap,
> +			struct local_bind_info *local_ip)
> {
> -	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
> +	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, local_ip))
> 		return 0;
> -	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
> +	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap, local_ip);
> }
> 
> /**
> @@ -700,7 +803,8 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
> 			struct pmap *mnt_pmap,
> 			const struct sockaddr *nfs_saddr,
> 			const socklen_t nfs_salen,
> -			struct pmap *nfs_pmap)
> +			struct pmap *nfs_pmap,
> +			struct local_bind_info *local_ip)
> {
> 	struct pmap save_nfs, save_mnt;
> 	const unsigned long *probe_vers;
> @@ -712,7 +816,8 @@ 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_saddr, nfs_salen, nfs_pmap);
> +					       nfs_saddr, nfs_salen, nfs_pmap,
> +					       local_ip);
> 
> 	memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
> 	memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
> @@ -721,9 +826,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_saddr, nfs_salen, nfs_pmap, local_ip) != 0) {
> 			mnt_pmap->pm_vers = *probe_vers;
> -			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
> +			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap, local_ip) != 0)
> 				return 1;
> 			memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
> 		}
> @@ -753,7 +858,8 @@ 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(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
> +		    struct local_bind_info *local_ip)
> {
> 	struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
> 	struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
> @@ -761,7 +867,7 @@ int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
> 	return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
> 					&mnt_server->pmap,
> 					nfs_addr, sizeof(nfs_server->saddr),
> -					&nfs_server->pmap);
> +				   &nfs_server->pmap, local_ip);
> }
> 
> static int nfs_probe_statd(void)
> @@ -773,7 +879,8 @@ static int nfs_probe_statd(void)
> 	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
> 
> 	return nfs_getport_ping(SAFE_SOCKADDR(&addr), sizeof(addr),
> -				program, (rpcvers_t)1, IPPROTO_UDP);
> +				program, (rpcvers_t)1, IPPROTO_UDP,
> +				NULL);
> }
> 
> /**
> @@ -829,7 +936,8 @@ int start_statd(void)
>  * We use a fast timeout since this call is advisory only.
>  */
> int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
> -		      const struct pmap *pmap, const dirpath *argp)
> +		      const struct pmap *pmap, const dirpath *argp,
> +		      struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *saddr = &address.sa;
> @@ -841,7 +949,7 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
> 	enum clnt_stat res = 0;
> 
> 	memcpy(saddr, sap, salen);
> -	if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
> +	if (nfs_probe_mntport(saddr, salen, &mnt_pmap, local_ip) == 0) {
> 		if (verbose)
> 			nfs_error(_("%s: Failed to discover mountd port%s"),
> 				progname, clnt_spcreateerror(""));
> @@ -851,7 +959,7 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
> 
> 	client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
> 					mnt_pmap.pm_prog, mnt_pmap.pm_vers,
> -					&timeout);
> +					&timeout, local_ip);
> 	if (client == NULL) {
> 		if (verbose)
> 			nfs_error(_("%s: Failed to create RPC client%s"),
> @@ -899,7 +1007,8 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
>  * 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(clnt_addr_t *mnt_server, dirpath *argp,
> +		    struct local_bind_info *local_ip)
> {
> 	struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
> 	socklen_t salen = sizeof(mnt_server->saddr);
> @@ -908,9 +1017,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(sap, salen, pmap, local_ip))
> 		return 0;
> -	clnt = mnt_openclnt(mnt_server, &msock);
> +	clnt = mnt_openclnt(mnt_server, &msock, local_ip);
> 	if (!clnt)
> 		return 0;
> 	res = clnt_call(clnt, MOUNTPROC_UMNT,
> @@ -931,7 +1040,8 @@ 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(clnt_addr_t *mnt_server, int *msock,
> +		     struct local_bind_info *local_ip)
> {
> 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
> 	struct pmap *mnt_pmap = &mnt_server->pmap;
> @@ -939,7 +1049,7 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
> 
> 	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
> 	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
> -				TRUE, FALSE);
> +			    TRUE, FALSE, local_ip);
> 	if (*msock == RPC_ANYSOCK) {
> 		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
> 			/*
> @@ -1008,7 +1118,7 @@ void mnt_closeclnt(CLIENT *clnt, int msock)
>  */
> int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
> 		const unsigned long vers, const unsigned int prot,
> -		struct sockaddr_in *caddr)
> +	      struct sockaddr_in *caddr, struct local_bind_info *local_ip)
> {
> 	CLIENT *clnt = NULL;
> 	int sock, status;
> @@ -1016,7 +1126,7 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
> 	struct sockaddr dissolve;
> 
> 	rpc_createerr.cf_stat = status = 0;
> -	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
> +	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE, local_ip);
> 	if (sock == RPC_ANYSOCK) {
> 		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
> 			/*
> @@ -1659,7 +1769,8 @@ out:
>  * parsed successfully; otherwise EX_FAIL.
>  */
> int nfs_umount_do_umnt(struct mount_options *options,
> -		       char **hostname, char **dirname)
> +		       char **hostname, char **dirname,
> +		       struct local_bind_info *local_ip)
> {
> 	union nfs_sockaddr address;
> 	struct sockaddr *sap = &address.sa;
> @@ -1686,7 +1797,7 @@ int nfs_umount_do_umnt(struct mount_options *options,
> 		/* nfs_lookup reports any errors */
> 		return EX_FAIL;
> 
> -	if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
> +	if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname, local_ip) == 0)
> 		/* nfs_advise_umount reports any errors */
> 		return EX_FAIL;
> 
> diff --git a/utils/mount/network.h b/utils/mount/network.h
> index 81c6f22..4af8fd1 100644
> --- a/utils/mount/network.h
> +++ b/utils/mount/network.h
> @@ -25,6 +25,7 @@
> #define _NFS_UTILS_MOUNT_NETWORK_H
> 
> #include <rpc/pmap_prot.h>
> +#include "sockaddr.h"
> 
> #define MNT_SENDBUFSIZE (2048U)
> #define MNT_RECVBUFSIZE (1024U)
> @@ -35,14 +36,17 @@ typedef struct {
> 	struct pmap pmap;
> } clnt_addr_t;
> 
> +void parse_local_bind(struct local_bind_info *laddr, const char* str);
> +
> /* RPC call timeout values */
> 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(clnt_addr_t *, clnt_addr_t *, struct local_bind_info *);
> int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
> 			struct pmap *, const struct sockaddr *,
> -			const socklen_t, struct pmap *);
> +			const socklen_t, struct pmap *,
> +			struct local_bind_info *);
> int nfs_gethostbyname(const char *, struct sockaddr_in *);
> int nfs_lookup(const char *hostname, const sa_family_t family,
> 		struct sockaddr *sap, socklen_t *salen);
> @@ -53,7 +57,7 @@ int nfs_callback_address(const struct sockaddr *, const socklen_t,
> 		struct sockaddr *, socklen_t *);
> int clnt_ping(struct sockaddr_in *, const unsigned long,
> 		const unsigned long, const unsigned int,
> -		struct sockaddr_in *);
> +	      struct sockaddr_in *, struct local_bind_info *);
> 
> struct mount_options;
> 
> @@ -69,13 +73,15 @@ int start_statd(void);
> 
> unsigned long nfsvers_to_mnt(const unsigned long);
> 
> -int nfs_call_umount(clnt_addr_t *, dirpath *);
> +int nfs_call_umount(clnt_addr_t *, dirpath *, struct local_bind_info *);
> int nfs_advise_umount(const struct sockaddr *, const socklen_t,
> -		      const struct pmap *, const dirpath *);
> -CLIENT *mnt_openclnt(clnt_addr_t *, int *);
> +		      const struct pmap *, const dirpath *,
> +		      struct local_bind_info *);
> +CLIENT *mnt_openclnt(clnt_addr_t *, int *, struct local_bind_info *);
> void mnt_closeclnt(CLIENT *, int);
> 
> int nfs_umount_do_umnt(struct mount_options *options,
> -		       char **hostname, char **dirname);
> +		       char **hostname, char **dirname,
> +		       struct local_bind_info *local_ip);
> 
> #endif	/* _NFS_UTILS_MOUNT_NETWORK_H */
> diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
> index be91a25..bc957f3 100644
> --- a/utils/mount/nfs.man
> +++ b/utils/mount/nfs.man
> @@ -463,6 +463,12 @@ by other clients, but can impact application and server performance.
> .IP
> The DATA AND METADATA COHERENCE section contains a
> detailed discussion of these trade-offs.
> +.TP 1.5i
> +.BI srcaddr= n.n.n.n

Since this mount option can take an IPv6 address, "n.n.n.n" is not appropriate here.  Simply use "n".

> +Specifies a single IPv4 or IPv6 address
> +to which the NFS client will bind its sockets.
> +This can help force NFS to use a particular IP
> +address on a multi-homed machine.
> .SS "Options for NFS versions 2 and 3 only"
> Use these options, along with the options in the above subsection,
> for NFS versions 2 and 3 only.
Ben Greear June 9, 2011, 3:50 p.m. UTC | #2
On 06/09/2011 07:05 AM, Chuck Lever wrote:
>
> On Jun 8, 2011, at 7:01 PM, greearb@candelatech.com wrote:
>
>> From: Ben Greear<greearb@candelatech.com>
>>
>> This lets one specify the source IP address for
>> sockets, allowing users to leverage routing rules
>> on multi-homed systems.
>>
>> Kernel patches to RPC and NFS are needed to complete
>> full functionality.
>>
>> Signed-off-by: Ben Greear<greearb@candelatech.com>
>> ---
>>
>> v2:  Use union nfs_sockaddr in local_bind_info struct.
>>      Remove cmd-line and getenv parsing.
>>      Add option parsing to umount logic.
>>      Update man.nfs page.
>
> Thanks for that, but...
>
> I'm going to side with Neil on this: your solution is invasive.  We need to see much better rationale for the complexity of your design.  Why isn't an automatic solution adequate for your needs?  Don't design for every general case, just solve your immediate problem and explain your solution.
>
> I'm not saying "no" just please show your work more.  Clearly our community has not had to address this problem before now, so we need some explanation to do a proper review.

Ok, first my use case:

I want to have multiple interfaces on the system each make an independent
connection to the nfs server(s).  Each interface may be on the same subnet,
or on different subnets, and there may be routers attached to each
interface.  In order to make the traffic go out one interface or another,
I set up ip routing rules that say: use routing table X if source-ip is Y.
I can then set up the various routing tables to go to the correct external
routers.  If it's not a routed network, then the traffic will still originate
on the correct interface.

We use this primarily for stress-testing NFS servers (create 1000 unique
mounts, for instance).

Other users might want the same functionality to use two interfaces on the
same network to communicate to an nfs server, perhaps to utilize two 1Gbps
network interfaces that eventually connect to a 10Gbps file-server, and thus
get double the total throughput.

A second use case:

User has two IPs on the same interface, and wants to use a specific
one for NFS traffic.  Aside from the testing scenario above, I'm not
sure how this helps, but I also know that people do clever things that
no one has thought of if you allow them the tools.

Third case:

User has trusted network A and un-trusted B.  They would like to make
sure that NFS traffic always originates on A.  So, they can bind the
local IP to A and not have to worry about what automatic address selection
might choose.

>> +struct local_bind_info {
>> +	union nfs_sockaddr addr;
>> +	socklen_t addrlen;
>> +	bool is_set;
>> +};
>
> Please use "struct sockaddr *" or provide a very clear rationale (not "we may need this someday") for why "struct sockaddr *" is not adequate for solving your immediate problem.

First, it's nice to be able to pass around the addrlen and not just
assume the length based on the family.

Second:  I *will* submit a patch to allow binding to a network device with
SO_BINDTODEVICE if I can get this first patch accepted.  Using SO_BINDTODEVICE
offers similar functionality, but it doesn't require routing rules to force
traffic out an interface.  This is another way to help keep nfs traffic on
trusted interfaces, for instance.

>> @@ -305,6 +307,9 @@ static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
>>
>> 	if ((len -= strlen(opt))>  0)
>> 		strcat(extra_opts, opt);
>> +
>> +	if (strncmp(opt, "srcaddr=", strlen("srcaddr=")) == 0)
>> +		parse_local_bind(&glb_local_ip, opt + strlen("srcaddr="));
>> }
>
> Why is this needed?  Is it just to support legacy (non-string) mounts?

No, it allows us to bind mount.nfs sockets to the IP that the mount options are asking the
kernel to bind to.  It's the *only* way to get the mount.nfs process to bind
to an IP now that I removed the cmd-line args and environ variable hacks.

>> /*
>> @@ -345,21 +350,21 @@ static void parse_opts(const char *options, int *flags, char **extra_opts)
>> }
>>
>> static int try_mount(char *spec, char *mount_point, int flags,
>> -			char *fs_type, char **extra_opts, char *mount_opts,
>> -			int fake, int bg)
>> +		     char *fs_type, char **extra_opts, char *mount_opts,
>> +		     int fake, int bg, struct local_bind_info *local_ip)
>> {
>> 	int ret;
>>
>> 	if (string)
>> 		ret = nfsmount_string(spec, mount_point, fs_type, flags,
>> -					extra_opts, fake, bg);
>> +				      extra_opts, fake, bg, local_ip);
>> 	else {
>> 		if (strcmp(fs_type, "nfs4") == 0)
>> 			ret = nfs4mount(spec, mount_point, flags,
>> -					extra_opts, fake, bg);
>> +					extra_opts, fake, bg, local_ip);
>> 		else
>> 			ret = nfsmount(spec, mount_point, flags,
>> -					extra_opts, fake, bg);
>> +				       extra_opts, fake, bg, local_ip);
>> 	}
>
> You're better off not supporting srcaddr= for legacy mounts.  The kernel will never ever support adding a srcaddr field to the nfs_mount_data blob, so why go to the trouble in user space?

Ok.  I wasn't attempting to support non-string mounts.  I'll look this over and try
to clean it up.

>> +void
>> +parse_local_bind(struct local_bind_info *laddr, const char* str) {
>> +	/* str is an IP address. */
>> +	int aiErr;
>> +	unsigned int i;
>> +	struct addrinfo *aiHead;
>> +	struct addrinfo hints;
>> +	char *node = NULL; /* ip addr */
>> +	char *service = NULL; /* port */
>> +	char *tmp = xstrdup(str);
>> +
>> +	laddr->is_set = 0;
>> +
>> +	memset(&hints, 0, sizeof(hints));
>> +
>> +	hints.ai_flags  = AI_NUMERICSERV;
>> +	hints.ai_socktype = SOCK_STREAM;
>> +	hints.ai_protocol = IPPROTO_TCP;
>> +
>> +	if (str[0] == '[') {
>
> Please don't.  The square bracket thing is just for the NFS special device.  It really isn't needed for a mount option that takes just a presentation IP address.  Or, are you expecting the user to specify a source port number here as well?

I personally don't want to bind to a particular port, but it seems like something
that someone might want to do some day, so it would be nice to get the parsing
able to handle it up front to ease compatibility issues.

>
> Since parse_local_bind() is a globally visible function, please add a documenting comment that lists parameters (both input and output) and how it is expected to behave.

Ok.

>> @@ -463,6 +463,12 @@ by other clients, but can impact application and server performance.
>> .IP
>> The DATA AND METADATA COHERENCE section contains a
>> detailed discussion of these trade-offs.
>> +.TP 1.5i
>> +.BI srcaddr= n.n.n.n
>
> Since this mount option can take an IPv6 address, "n.n.n.n" is not appropriate here.  Simply use "n".

Ok.

Thanks,
Ben
Steve Dickson June 9, 2011, 5:18 p.m. UTC | #3
Hello,


On 06/08/2011 07:01 PM, greearb@candelatech.com wrote:
> From: Ben Greear <greearb@candelatech.com>
> 
> This lets one specify the source IP address for
> sockets, allowing users to leverage routing rules
> on multi-homed systems.
Right out of the box I do think some support like 
this would be good... but I not convinced on how needed
something like this is which means there is a lot of 
change here for basically not... But lets continue 
to talk about to see where we end up.... 

One thing you must do is break up this patch in to
a more readable patch series. There is no way I going
to commit a patch that is close to 1500 lines
that so invasive. 

Here is how I create patch series. 

git pull devel-tree
git checkout -b devel-branch

make changes
git commit -s -a 
make more changes 
git commit -s -a 
[Note, each one of the commits (or patches) have to be stand-alone patches ]
[Which means after each commit the tree has to compile and be functional   ]

When all the changes are done, create the patches like:

git format-patch -n -o /tmp/sumbit --cover-letter  commit1^..commitN

edit /tmp/summit/*cover* to explain your changes

Then post the patches with git send-email command:

git send-email $DRYRUN  --suppress-cc=all --suppress-from --no-chain-reply-to --from "Your Name <emailaddress@foobar.com>" --to Linux NFS Mailing list <linux-nfs@vger.kernel.org>" --smtp-server smtp.foobar.com /tmp/submit/*.patch

Note, I'm not sure the --smtp argument is needed... but it works..

steved.
--
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 June 9, 2011, 5:29 p.m. UTC | #4
On 06/09/2011 10:18 AM, Steve Dickson wrote:
> Hello,
>
>
> On 06/08/2011 07:01 PM, greearb@candelatech.com wrote:
>> From: Ben Greear<greearb@candelatech.com>
>>
>> This lets one specify the source IP address for
>> sockets, allowing users to leverage routing rules
>> on multi-homed systems.
> Right out of the box I do think some support like
> this would be good... but I not convinced on how needed
> something like this is which means there is a lot of
> change here for basically not... But lets continue
> to talk about to see where we end up....
>
> One thing you must do is break up this patch in to
> a more readable patch series. There is no way I going
> to commit a patch that is close to 1500 lines
> that so invasive.

The vast majority of the patch is adding an extra
argument to various methods to get things to compile.

Do you want me to break those changes into individual
chunks, or should I just separate the parsing & documentation
chunks out?

I can dummy stuff out with passing NULL around to make
lots of smaller patches, but I'm not sure it helps
any readability.

Thanks,
Ben
Chuck Lever June 9, 2011, 6:39 p.m. UTC | #5
On Jun 9, 2011, at 11:50 AM, Ben Greear wrote:

> On 06/09/2011 07:05 AM, Chuck Lever wrote:
>> 
>> On Jun 8, 2011, at 7:01 PM, greearb@candelatech.com wrote:
>> 
>>> From: Ben Greear<greearb@candelatech.com>
>>> 
>>> This lets one specify the source IP address for
>>> sockets, allowing users to leverage routing rules
>>> on multi-homed systems.
>>> 
>>> Kernel patches to RPC and NFS are needed to complete
>>> full functionality.
>>> 
>>> Signed-off-by: Ben Greear<greearb@candelatech.com>
>>> ---
>>> 
>>> v2:  Use union nfs_sockaddr in local_bind_info struct.
>>>     Remove cmd-line and getenv parsing.
>>>     Add option parsing to umount logic.
>>>     Update man.nfs page.
>> 
>> Thanks for that, but...
>> 
>> I'm going to side with Neil on this: your solution is invasive.  We need to see much better rationale for the complexity of your design.  Why isn't an automatic solution adequate for your needs?  Don't design for every general case, just solve your immediate problem and explain your solution.
>> 
>> I'm not saying "no" just please show your work more.  Clearly our community has not had to address this problem before now, so we need some explanation to do a proper review.
> 
> Ok, first my use case:
> 
> I want to have multiple interfaces on the system each make an independent
> connection to the nfs server(s).  Each interface may be on the same subnet,
> or on different subnets, and there may be routers attached to each
> interface.  In order to make the traffic go out one interface or another,
> I set up ip routing rules that say: use routing table X if source-ip is Y.
> I can then set up the various routing tables to go to the correct external
> routers.  If it's not a routed network, then the traffic will still originate
> on the correct interface.
> 
> We use this primarily for stress-testing NFS servers (create 1000 unique
> mounts, for instance).

Typically this kind of server testing is better done with multiple clients.  This can be done with virtual guests, for example, if you don't have a lot of hardware.  It's certainly not a common use case, though.  (A frequently occurring use case would be one justification for significant additional complexity).

> Other users might want the same functionality to use two interfaces on the
> same network to communicate to an nfs server, perhaps to utilize two 1Gbps
> network interfaces that eventually connect to a 10Gbps file-server, and thus
> get double the total throughput.

Bonding is supposed to be supported by pNFS, I thought.

> A second use case:
> 
> User has two IPs on the same interface, and wants to use a specific
> one for NFS traffic.  Aside from the testing scenario above, I'm not
> sure how this helps, but I also know that people do clever things that
> no one has thought of if you allow them the tools.
> 
> Third case:
> 
> User has trusted network A and un-trusted B.  They would like to make
> sure that NFS traffic always originates on A.  So, they can bind the
> local IP to A and not have to worry about what automatic address selection
> might choose.

NFS has krb5p for preventing traffic sniffing.  It's all done well above the IP layer.

I'm not convinced.  I think we can accomplish these items more simply by refining other areas of the NFS client.

>>> +struct local_bind_info {
>>> +	union nfs_sockaddr addr;
>>> +	socklen_t addrlen;
>>> +	bool is_set;
>>> +};
>> 
>> Please use "struct sockaddr *" or provide a very clear rationale (not "we may need this someday") for why "struct sockaddr *" is not adequate for solving your immediate problem.
> 
> First, it's nice to be able to pass around the addrlen and not just
> assume the length based on the family.

Can you elaborate on "nice" ?  We're really only concerned about just three families: AF_INET, AF_INET6, and AF_LOCAL.

> Second:  I *will* submit a patch to allow binding to a network device with
> SO_BINDTODEVICE if I can get this first patch accepted.

Then that subsequent patch is where this new structure belongs.  That groups the changes together logically.

> Using SO_BINDTODEVICE
> offers similar functionality, but it doesn't require routing rules to force
> traffic out an interface.  This is another way to help keep nfs traffic on
> trusted interfaces, for instance.

It might be nicer if all of the parameters used to define the transport endpoints were included in the same structure.  Then you pass a single pointer around that encompasses both the remote address and the source address.  That seems more logical to me than passing a struct sockaddr * for the remote address and a pointer to an arbitrary structure for the source address.

Reshaping internal APIs is something you can split out of any patch to support source address binding.

>>> @@ -305,6 +307,9 @@ static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
>>> 
>>> 	if ((len -= strlen(opt))>  0)
>>> 		strcat(extra_opts, opt);
>>> +
>>> +	if (strncmp(opt, "srcaddr=", strlen("srcaddr=")) == 0)
>>> +		parse_local_bind(&glb_local_ip, opt + strlen("srcaddr="));
>>> }
>> 
>> Why is this needed?  Is it just to support legacy (non-string) mounts?
> 
> No, it allows us to bind mount.nfs sockets to the IP that the mount options are asking the
> kernel to bind to.  It's the *only* way to get the mount.nfs process to bind
> to an IP now that I removed the cmd-line args and environ variable hacks.

I don't understand how that can be the case.  The srcaddr mount option should be parsed in stropt.c, where it is used to generate sockets for rpcbind and MNT calls.  This works the same for all of the sockets mount.nfs uses for string mounts.  I don't see any need for this logic in mount.c, especially if you don't support srcaddr= for legacy (nfs_mount_data) style mounts.

> 
>>> /*
>>> @@ -345,21 +350,21 @@ static void parse_opts(const char *options, int *flags, char **extra_opts)
>>> }
>>> 
>>> static int try_mount(char *spec, char *mount_point, int flags,
>>> -			char *fs_type, char **extra_opts, char *mount_opts,
>>> -			int fake, int bg)
>>> +		     char *fs_type, char **extra_opts, char *mount_opts,
>>> +		     int fake, int bg, struct local_bind_info *local_ip)
>>> {
>>> 	int ret;
>>> 
>>> 	if (string)
>>> 		ret = nfsmount_string(spec, mount_point, fs_type, flags,
>>> -					extra_opts, fake, bg);
>>> +				      extra_opts, fake, bg, local_ip);
>>> 	else {
>>> 		if (strcmp(fs_type, "nfs4") == 0)
>>> 			ret = nfs4mount(spec, mount_point, flags,
>>> -					extra_opts, fake, bg);
>>> +					extra_opts, fake, bg, local_ip);
>>> 		else
>>> 			ret = nfsmount(spec, mount_point, flags,
>>> -					extra_opts, fake, bg);
>>> +				       extra_opts, fake, bg, local_ip);
>>> 	}
>> 
>> You're better off not supporting srcaddr= for legacy mounts.  The kernel will never ever support adding a srcaddr field to the nfs_mount_data blob, so why go to the trouble in user space?
> 
> Ok.  I wasn't attempting to support non-string mounts.  I'll look this over and try
> to clean it up.
> 
>>> +void
>>> +parse_local_bind(struct local_bind_info *laddr, const char* str) {
>>> +	/* str is an IP address. */
>>> +	int aiErr;
>>> +	unsigned int i;
>>> +	struct addrinfo *aiHead;
>>> +	struct addrinfo hints;
>>> +	char *node = NULL; /* ip addr */
>>> +	char *service = NULL; /* port */
>>> +	char *tmp = xstrdup(str);
>>> +
>>> +	laddr->is_set = 0;
>>> +
>>> +	memset(&hints, 0, sizeof(hints));
>>> +
>>> +	hints.ai_flags  = AI_NUMERICSERV;
>>> +	hints.ai_socktype = SOCK_STREAM;
>>> +	hints.ai_protocol = IPPROTO_TCP;
>>> +
>>> +	if (str[0] == '[') {
>> 
>> Please don't.  The square bracket thing is just for the NFS special device.  It really isn't needed for a mount option that takes just a presentation IP address.  Or, are you expecting the user to specify a source port number here as well?
> 
> I personally don't want to bind to a particular port, but it seems like something
> that someone might want to do some day, so it would be nice to get the parsing
> able to handle it up front to ease compatibility issues.

Unless you have a specific use case today, we prefer not to speculate about how this might be used.  Let's design exactly what we need right now.

If you want to look at a UI precedent for mount.nfs, ports are specified via separate mount options.  "srcport=" for example, might be used to specify the source port.  In any event, that support can be added later if desired.  Let's keep this as simple and to the point as possible.
Steve Dickson June 9, 2011, 9:16 p.m. UTC | #6
On 06/09/2011 01:29 PM, Ben Greear wrote:
> On 06/09/2011 10:18 AM, Steve Dickson wrote:
>> Hello,
>>
>>
>> On 06/08/2011 07:01 PM, greearb@candelatech.com wrote:
>>> From: Ben Greear<greearb@candelatech.com>
>>>
>>> This lets one specify the source IP address for
>>> sockets, allowing users to leverage routing rules
>>> on multi-homed systems.
>> Right out of the box I do think some support like
>> this would be good... but I not convinced on how needed
>> something like this is which means there is a lot of
>> change here for basically not... But lets continue
>> to talk about to see where we end up....
>>
>> One thing you must do is break up this patch in to
>> a more readable patch series. There is no way I going
>> to commit a patch that is close to 1500 lines
>> that so invasive.
> 
> The vast majority of the patch is adding an extra
> argument to various methods to get things to compile.
Yeah I know... I saw that... But there has to be
some logical way to break this up.. 

> 
> Do you want me to break those changes into individual
> chunks, or should I just separate the parsing & documentation
> chunks out?
I guess that would be start.... but just breaking this up
unto just a couple patches probably will not do anything for
readability either... 
 
> 
> I can dummy stuff out with passing NULL around to make
> lots of smaller patches, but I'm not sure it helps
> any readability.
I don't know either... but it make me very nervous 
to apply a 1400+ line patch especially when its basically
rewriting mount... I've broken mount before... Its not
a pretty sight... ;-)

steved.  
> 
> Thanks,
> Ben
> 
> 
--
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 June 9, 2011, 9:25 p.m. UTC | #7
On 06/09/2011 02:16 PM, Steve Dickson wrote:
>
>
> On 06/09/2011 01:29 PM, Ben Greear wrote:
>> On 06/09/2011 10:18 AM, Steve Dickson wrote:
>>> Hello,
>>>
>>>
>>> On 06/08/2011 07:01 PM, greearb@candelatech.com wrote:
>>>> From: Ben Greear<greearb@candelatech.com>
>>>>
>>>> This lets one specify the source IP address for
>>>> sockets, allowing users to leverage routing rules
>>>> on multi-homed systems.
>>> Right out of the box I do think some support like
>>> this would be good... but I not convinced on how needed
>>> something like this is which means there is a lot of
>>> change here for basically not... But lets continue
>>> to talk about to see where we end up....
>>>
>>> One thing you must do is break up this patch in to
>>> a more readable patch series. There is no way I going
>>> to commit a patch that is close to 1500 lines
>>> that so invasive.
>>
>> The vast majority of the patch is adding an extra
>> argument to various methods to get things to compile.
> Yeah I know... I saw that... But there has to be
> some logical way to break this up..
>
>>
>> Do you want me to break those changes into individual
>> chunks, or should I just separate the parsing&  documentation
>> chunks out?
> I guess that would be start.... but just breaking this up
> unto just a couple patches probably will not do anything for
> readability either...
>
>>
>> I can dummy stuff out with passing NULL around to make
>> lots of smaller patches, but I'm not sure it helps
>> any readability.
> I don't know either... but it make me very nervous
> to apply a 1400+ line patch especially when its basically
> rewriting mount... I've broken mount before... Its not
> a pretty sight... ;-)

How about an initial patch that just passes a container down
to the bind logic with the 'sap' and 'salen' into a container struct.

Should have zero functionality change, but fairly large patch
(basically the same places as where I added the 'local_ip' argument).

Then, I can post a patch to enable packaging a src-addr in that
container as well.  That should be a much smaller patch and so
may seem less scary.

Thanks,
Ben
Ben Greear June 9, 2011, 11:47 p.m. UTC | #8
On 06/09/2011 11:39 AM, Chuck Lever wrote:
>
> On Jun 9, 2011, at 11:50 AM, Ben Greear wrote:
>
>> On 06/09/2011 07:05 AM, Chuck Lever wrote:
>>>
>>> On Jun 8, 2011, at 7:01 PM, greearb@candelatech.com wrote:
>>>
>>>> From: Ben Greear<greearb@candelatech.com>
>>>>
>>>> This lets one specify the source IP address for
>>>> sockets, allowing users to leverage routing rules
>>>> on multi-homed systems.
>>>>
>>>> Kernel patches to RPC and NFS are needed to complete
>>>> full functionality.
>>>>
>>>> Signed-off-by: Ben Greear<greearb@candelatech.com>
>>>> ---
>>>>
>>>> v2:  Use union nfs_sockaddr in local_bind_info struct.
>>>>      Remove cmd-line and getenv parsing.
>>>>      Add option parsing to umount logic.
>>>>      Update man.nfs page.
>>>
>>> Thanks for that, but...
>>>
>>> I'm going to side with Neil on this: your solution is invasive.  We need to see much better rationale for the complexity of your design.  Why isn't an automatic solution adequate for your needs?  Don't design for every general case, just solve your immediate problem and explain your solution.
>>>
>>> I'm not saying "no" just please show your work more.  Clearly our community has not had to address this problem before now, so we need some explanation to do a proper review.
>>
>> Ok, first my use case:
>>
>> I want to have multiple interfaces on the system each make an independent
>> connection to the nfs server(s).  Each interface may be on the same subnet,
>> or on different subnets, and there may be routers attached to each
>> interface.  In order to make the traffic go out one interface or another,
>> I set up ip routing rules that say: use routing table X if source-ip is Y.
>> I can then set up the various routing tables to go to the correct external
>> routers.  If it's not a routed network, then the traffic will still originate
>> on the correct interface.
>>
>> We use this primarily for stress-testing NFS servers (create 1000 unique
>> mounts, for instance).
>
> Typically this kind of server testing is better done with multiple clients.  This can be done with virtual guests, for example, if you don't have a lot of hardware.  It's certainly not a common use case, though.  (A frequently occurring use case would be one justification for significant additional complexity).

I can get 1000+ emulated clients, each with it's own MAC, IP, etc on a mid-range
system, and even more on higher-end hardware.  I'm sure that's higher density
than you can get with virtual guests, and it's also a lot easier to manage.

Regardless, the ability for users to have control over their network config
is more useful than any of my personal use cases..

>> Other users might want the same functionality to use two interfaces on the
>> same network to communicate to an nfs server, perhaps to utilize two 1Gbps
>> network interfaces that eventually connect to a 10Gbps file-server, and thus
>> get double the total throughput.
>
> Bonding is supposed to be supported by pNFS, I thought.

What I propose is a very simple change from standard old nfs
usage.  Somehow I doubt bonding pNFS is quite so simple to deal
with.

>> A second use case:
>>
>> User has two IPs on the same interface, and wants to use a specific
>> one for NFS traffic.  Aside from the testing scenario above, I'm not
>> sure how this helps, but I also know that people do clever things that
>> no one has thought of if you allow them the tools.
>>
>> Third case:
>>
>> User has trusted network A and un-trusted B.  They would like to make
>> sure that NFS traffic always originates on A.  So, they can bind the
>> local IP to A and not have to worry about what automatic address selection
>> might choose.
>
> NFS has krb5p for preventing traffic sniffing.  It's all done well above the IP layer.
>
> I'm not convinced.  I think we can accomplish these items more simply by refining other areas of the NFS client.

Well, we differ in opinion.  I'm going to try a bit more to find some acceptable
solution, but if everyone sees this effort as DOA, please let me know now so I can
move on to other things.

>>>> +struct local_bind_info {
>>>> +	union nfs_sockaddr addr;
>>>> +	socklen_t addrlen;
>>>> +	bool is_set;
>>>> +};
>>>
>>> Please use "struct sockaddr *" or provide a very clear rationale (not "we may need this someday") for why "struct sockaddr *" is not adequate for solving your immediate problem.
>>
>> First, it's nice to be able to pass around the addrlen and not just
>> assume the length based on the family.
>
> Can you elaborate on "nice" ?  We're really only concerned about just three families: AF_INET, AF_INET6, and AF_LOCAL.
>
>> Second:  I *will* submit a patch to allow binding to a network device with
>> SO_BINDTODEVICE if I can get this first patch accepted.
>
> Then that subsequent patch is where this new structure belongs.  That groups the changes together logically.

That would be two huge boring patches that touch the same code.

>
>> Using SO_BINDTODEVICE
>> offers similar functionality, but it doesn't require routing rules to force
>> traffic out an interface.  This is another way to help keep nfs traffic on
>> trusted interfaces, for instance.
>
> It might be nicer if all of the parameters used to define the transport endpoints were included in the same structure.  Then you pass a single pointer around that encompasses both the remote address and the source address.  That seems more logical to me than passing a struct sockaddr * for the remote address and a pointer to an arbitrary structure for the source address.
>
> Reshaping internal APIs is something you can split out of any patch to support source address binding.

I made an attempt at this.  It seems way more risky, ugly, and more code than my
posted patch.  The primary reason being that often copies are made of the
addr before it's passed to other methods.  So, you would end up creating lots
of on-stack config structs and then doing copies before passing them on to
more functions.

If changing the 'local_ip' argument everywhere to be a sockaddr* will make
you happy, I will just do that and forget about implementing SO_BINDTODEVICE,
and assume salen based on the sa_family.

>>>> @@ -305,6 +307,9 @@ static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
>>>>
>>>> 	if ((len -= strlen(opt))>   0)
>>>> 		strcat(extra_opts, opt);
>>>> +
>>>> +	if (strncmp(opt, "srcaddr=", strlen("srcaddr=")) == 0)
>>>> +		parse_local_bind(&glb_local_ip, opt + strlen("srcaddr="));
>>>> }
>>>
>>> Why is this needed?  Is it just to support legacy (non-string) mounts?
>>
>> No, it allows us to bind mount.nfs sockets to the IP that the mount options are asking the
>> kernel to bind to.  It's the *only* way to get the mount.nfs process to bind
>> to an IP now that I removed the cmd-line args and environ variable hacks.
>
> I don't understand how that can be the case.  The srcaddr mount option should be parsed in stropt.c, where it is used to generate sockets for rpcbind and MNT calls.  This works the same for all of the sockets mount.nfs uses for string mounts.  I don't see any need for this logic in mount.c, especially if you don't support srcaddr= for legacy (nfs_mount_data) style mounts.

Ok, I will look at that again.

>> I personally don't want to bind to a particular port, but it seems like something
>> that someone might want to do some day, so it would be nice to get the parsing
>> able to handle it up front to ease compatibility issues.
>
> Unless you have a specific use case today, we prefer not to speculate about how this might be used.  Let's design exactly what we need right now.
>
> If you want to look at a UI precedent for mount.nfs, ports are specified via separate mount options.  "srcport=" for example, might be used to specify the source port.  In any event, that support can be added later if desired.  Let's keep this as simple and to the point as possible.

Ok, I'll make the srcaddr=n argument a plain numeric IP.  I'll try to resolve IPv4
first, and if that doesn't work, will try it as IPv6.

Thanks,
Ben
Ben Greear June 9, 2011, 11:48 p.m. UTC | #9
On 06/09/2011 02:25 PM, Ben Greear wrote:
> On 06/09/2011 02:16 PM, Steve Dickson wrote:

> How about an initial patch that just passes a container down
> to the bind logic with the 'sap' and 'salen' into a container struct.
>
> Should have zero functionality change, but fairly large patch
> (basically the same places as where I added the 'local_ip' argument).
>
> Then, I can post a patch to enable packaging a src-addr in that
> container as well. That should be a much smaller patch and so
> may seem less scary.

I gave this a try and it started looking very ugly.

I'll work on breaking my previously posted patch smaller
chunks.

Thanks,
Ben
diff mbox

Patch

diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
index d50fe94..d8ef257 100644
--- a/support/include/nfsrpc.h
+++ b/support/include/nfsrpc.h
@@ -55,6 +55,8 @@ 
 #define NSMPROG		((rpcprog_t)100024)
 #endif
 
+struct local_bind_info;
+
 /**
  * nfs_clear_rpc_createerr - zap all error reporting fields
  *
@@ -75,7 +77,8 @@  extern rpcprog_t	nfs_getrpcbyname(const rpcprog_t, const char *table[]);
 extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
 				const socklen_t, const unsigned short,
 				const rpcprog_t, const rpcvers_t,
-				struct timeval *);
+				struct timeval *,
+				struct local_bind_info *);
 
 /*
  * Acquire an RPC CLIENT * with a privileged source port
@@ -83,7 +86,8 @@  extern CLIENT		*nfs_get_rpcclient(const struct sockaddr *,
 extern CLIENT		*nfs_get_priv_rpcclient( const struct sockaddr *,
 				const socklen_t, const unsigned short,
 				const rpcprog_t, const rpcvers_t,
-				struct timeval *);
+				struct timeval *,
+				struct local_bind_info *);
 
 /*
  * Convert a netid to a protocol number and protocol family
@@ -116,7 +120,8 @@  extern int		nfs_getport_ping(struct sockaddr *sap,
 				const socklen_t salen,
 				const rpcprog_t program,
 				const rpcvers_t version,
-				const unsigned short protocol);
+				const unsigned short protocol,
+				struct local_bind_info *local_ip);
 
 /*
  * Generic function that maps an RPC service tuple to an IP port
@@ -124,14 +129,16 @@  extern int		nfs_getport_ping(struct sockaddr *sap,
  */
 extern unsigned short	nfs_getport(const struct sockaddr *,
 				const socklen_t, const rpcprog_t,
-				const rpcvers_t, const unsigned short);
+				const rpcvers_t, const unsigned short,
+				struct local_bind_info *local_ip);
 
 /*
  * Generic function that maps an RPC service tuple to an IP port
  * number of the service on the local host
  */
 extern unsigned short	nfs_getlocalport(const rpcprot_t,
-				const rpcvers_t, const unsigned short);
+				const rpcvers_t, const unsigned short,
+				struct local_bind_info *local_ip);
 
 /*
  * Function to invoke an rpcbind v3/v4 GETADDR request
@@ -153,7 +160,8 @@  extern unsigned long	nfs_pmap_getport(const struct sockaddr_in *,
 				const unsigned long,
 				const unsigned long,
 				const unsigned long,
-				const struct timeval *);
+				const struct timeval *,
+				struct local_bind_info *local_ip);
 
 /*
  * Contact a remote RPC service to discover whether it is responding
@@ -164,7 +172,8 @@  extern int		nfs_rpc_ping(const struct sockaddr *sap,
 				const rpcprog_t program,
 				const rpcvers_t version,
 				const unsigned short protocol,
-				const struct timeval *timeout);
+				const struct timeval *timeout,
+				struct local_bind_info *local_ip);
 
 /* create AUTH_SYS handle with no supplemental groups */
 extern AUTH *			 nfs_authsys_create(void);
diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
index 9af2543..b1089ce 100644
--- a/support/include/sockaddr.h
+++ b/support/include/sockaddr.h
@@ -46,6 +46,12 @@  union nfs_sockaddr {
 	struct sockaddr_in6	s6;
 };
 
+struct local_bind_info {
+	union nfs_sockaddr addr;
+	socklen_t addrlen;
+	bool is_set;
+};
+
 #if SIZEOF_SOCKLEN_T - 0 == 0
 #define socklen_t unsigned int
 #endif
diff --git a/support/nfs/getport.c b/support/nfs/getport.c
index d74400b..e8256ff 100644
--- a/support/nfs/getport.c
+++ b/support/nfs/getport.c
@@ -181,7 +181,8 @@  static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
 				     const socklen_t salen,
 				     const unsigned short transport,
 				     const rpcvers_t version,
-				     struct timeval *timeout)
+				     struct timeval *timeout,
+				     struct local_bind_info *local_ip)
 {
 	static const char *rpcb_pgmtbl[] = {
 		"rpcbind",
@@ -195,7 +196,7 @@  static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
 
 	nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport)));
 	clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog,
-							version, timeout);
+				 version, timeout, local_ip);
 	nfs_gp_map_tcp_errorcodes(transport);
 	return clnt;
 }
@@ -729,7 +730,8 @@  static unsigned short nfs_gp_getport(CLIENT *client,
  */
 int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
 		 const rpcprog_t program, const rpcvers_t version,
-		 const unsigned short protocol, const struct timeval *timeout)
+		 const unsigned short protocol, const struct timeval *timeout,
+		 struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *saddr = &address.sa;
@@ -744,7 +746,7 @@  int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
 
 	memcpy(saddr, sap, (size_t)salen);
 	client = nfs_get_rpcclient(saddr, salen, protocol,
-						program, version, &tout);
+				   program, version, &tout, local_ip);
 	if (client != NULL) {
 		result = nfs_gp_ping(client, tout);
 		nfs_gp_map_tcp_errorcodes(protocol);
@@ -798,7 +800,8 @@  unsigned short nfs_getport(const struct sockaddr *sap,
 			   const socklen_t salen,
 			   const rpcprog_t program,
 			   const rpcvers_t version,
-			   const unsigned short protocol)
+			   const unsigned short protocol,
+			   struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *saddr = &address.sa;
@@ -810,7 +813,8 @@  unsigned short nfs_getport(const struct sockaddr *sap,
 
 	memcpy(saddr, sap, (size_t)salen);
 	client = nfs_gp_get_rpcbclient(saddr, salen, protocol,
-						default_rpcb_version, &timeout);
+				       default_rpcb_version, &timeout,
+				       local_ip);
 	if (client != NULL) {
 		port = nfs_gp_getport(client, saddr, program,
 					version, protocol, timeout);
@@ -840,7 +844,8 @@  unsigned short nfs_getport(const struct sockaddr *sap,
  */
 int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 		     const rpcprog_t program, const rpcvers_t version,
-		     const unsigned short protocol)
+		     const unsigned short protocol,
+		     struct local_bind_info *local_ip)
 {
 	struct timeval timeout = { -1, 0 };
 	unsigned short port = 0;
@@ -850,7 +855,8 @@  int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 	nfs_clear_rpc_createerr();
 
 	client = nfs_gp_get_rpcbclient(sap, salen, protocol,
-						default_rpcb_version, &timeout);
+				       default_rpcb_version, &timeout,
+				       local_ip);
 	if (client != NULL) {
 		port = nfs_gp_getport(client, sap, program,
 					version, protocol, timeout);
@@ -868,7 +874,8 @@  int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 		nfs_clear_rpc_createerr();
 
 		client = nfs_get_rpcclient(saddr, salen, protocol,
-						program, version, &timeout);
+					   program, version, &timeout,
+					   local_ip);
 		if (client != NULL) {
 			result = nfs_gp_ping(client, timeout);
 			nfs_gp_map_tcp_errorcodes(protocol);
@@ -909,7 +916,8 @@  int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
  */
 unsigned short nfs_getlocalport(const rpcprot_t program,
 				const rpcvers_t version,
-				const unsigned short protocol)
+				const unsigned short protocol,
+				struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *lb_addr = &address.sa;
@@ -946,7 +954,8 @@  unsigned short nfs_getlocalport(const rpcprot_t program,
 
 		if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
 			port = nfs_getport(lb_addr, lb_len,
-						program, version, protocol);
+					   program, version, protocol,
+					   local_ip);
 		} else
 			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
 	}
@@ -1074,7 +1083,8 @@  unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
 			       const unsigned long program,
 			       const unsigned long version,
 			       const unsigned long protocol,
-			       const struct timeval *timeout)
+			       const struct timeval *timeout,
+			       struct local_bind_info *local_ip)
 {
 	struct sockaddr_in address;
 	struct sockaddr *saddr = (struct sockaddr *)&address;
@@ -1094,7 +1104,8 @@  unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
 
 	memcpy(saddr, sin, sizeof(address));
 	client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin),
-					transport, PMAPVERS, &tout);
+				       transport, PMAPVERS, &tout,
+				       local_ip);
 	if (client != NULL) {
 		port = nfs_gp_pmap_getport(client, &parms, tout);
 		CLNT_DESTROY(client);
diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
index c14efe8..0a3fb7a 100644
--- a/support/nfs/rpc_socket.c
+++ b/support/nfs/rpc_socket.c
@@ -112,8 +112,12 @@  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(const int sock, const sa_family_t family,
+		    struct local_bind_info *local_ip)
 {
+	struct sockaddr *sa = NULL;
+	socklen_t salen = 0;
+
 	struct sockaddr_in sin = {
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_ANY),
@@ -123,15 +127,26 @@  static int nfs_bind(const int sock, const sa_family_t family)
 		.sin6_addr		= IN6ADDR_ANY_INIT,
 	};
 
-	switch (family) {
-	case AF_INET:
-		return bind(sock, (struct sockaddr *)(char *)&sin,
-					(socklen_t)sizeof(sin));
-	case AF_INET6:
-		return bind(sock, (struct sockaddr *)(char *)&sin6,
-					(socklen_t)sizeof(sin6));
+	if (local_ip && local_ip->is_set) {
+		sa = &local_ip->addr.sa;
+		salen = local_ip->addrlen;
+	} else {
+		switch (family) {
+		case AF_INET:
+			sa = (struct sockaddr *)&sin;
+			salen = sizeof(sin);
+			break;
+		case AF_INET6:
+			sa = (struct sockaddr *)&sin6;
+			salen = sizeof(sin6);
+		default:
+			break;
+		}
 	}
 
+	if (sa)
+		return bind(sock, sa, salen);
+
 	errno = EAFNOSUPPORT;
 	return -1;
 }
@@ -144,8 +159,10 @@  static int nfs_bind(const int sock, const sa_family_t family)
  * Returns zero on success, or returns -1 on error.  errno is
  * set to reflect the nature of the error.
  */
-static int nfs_bindresvport(const int sock, const sa_family_t family)
+static int nfs_bindresvport(const int sock, const sa_family_t family,
+			    struct local_bind_info *local_ip)
 {
+	struct sockaddr *sa = NULL;
 	struct sockaddr_in sin = {
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_ANY),
@@ -155,13 +172,23 @@  static int nfs_bindresvport(const int sock, const sa_family_t family)
 		.sin6_addr		= IN6ADDR_ANY_INIT,
 	};
 
-	switch (family) {
-	case AF_INET:
-		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
-	case AF_INET6:
-		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
+	if (local_ip && local_ip->is_set) {
+		sa = &local_ip->addr.sa;
+	} else {
+		switch (family) {
+		case AF_INET:
+			sa = (struct sockaddr *)&sin;
+			break;
+		case AF_INET6:
+			sa = (struct sockaddr *)&sin6;
+		default:
+			break;
+		}
 	}
 
+	if (sa)
+		return bindresvport_sa(sock, sa);
+
 	errno = EAFNOSUPPORT;
 	return -1;
 }
@@ -174,14 +201,24 @@  static int nfs_bindresvport(const int sock, const sa_family_t family)
  * Returns zero on success, or returns -1 on error.  errno is
  * set to reflect the nature of the error.
  */
-static int nfs_bindresvport(const int sock, const sa_family_t family)
+static int nfs_bindresvport(const int sock, const sa_family_t family,
+			    struct local_bind_info *local_ip)
 {
+	struct sockaddr_in laddr;
 	if (family != AF_INET) {
 		errno = EAFNOSUPPORT;
 		return -1;
 	}
 
-	return bindresvport(sock, NULL);
+	laddr.sin_family = family;
+	laddr.sin_port = 0;
+	if (local_ip && local_ip->is_set) {
+		struct sockaddr_in *si = &local_ip->addr.sa;
+		laddr.sin_addr.s_addr = si->sin_addr.s_addr;
+	} else {
+		laddr.sin_addr.s_addr = htonl(INADDR_ANY);
+	}
+	return bindresvport(sock, &laddr);
 }
 
 #endif	/* !HAVE_LIBTIRPC */
@@ -273,7 +310,8 @@  static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
 				 const rpcprog_t program,
 				 const rpcvers_t version,
 				 struct timeval *timeout,
-				 const int resvport)
+				 const int resvport,
+				 struct local_bind_info *local_ip)
 {
 	CLIENT *client;
 	int ret, sock;
@@ -301,9 +339,9 @@  static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
 	}
 
 	if (resvport)
-		ret = nfs_bindresvport(sock, sap->sa_family);
+		ret = nfs_bindresvport(sock, sap->sa_family, local_ip);
 	else
-		ret = nfs_bind(sock, sap->sa_family);
+		ret = nfs_bind(sock, sap->sa_family, local_ip);
 	if (ret < 0) {
 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 		rpc_createerr.cf_error.re_errno = errno;
@@ -355,7 +393,8 @@  static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
 				 const rpcprog_t program,
 				 const rpcvers_t version,
 				 struct timeval *timeout,
-				 const int resvport)
+				 const int resvport,
+				 struct local_bind_info *local_ip)
 {
 	CLIENT *client;
 	int ret, sock;
@@ -383,9 +422,9 @@  static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
 	}
 
 	if (resvport)
-		ret = nfs_bindresvport(sock, sap->sa_family);
+		ret = nfs_bindresvport(sock, sap->sa_family, local_ip);
 	else
-		ret = nfs_bind(sock, sap->sa_family);
+		ret = nfs_bind(sock, sap->sa_family, local_ip);
 	if (ret < 0) {
 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 		rpc_createerr.cf_error.re_errno = errno;
@@ -442,7 +481,8 @@  CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
 			  const unsigned short transport,
 			  const rpcprog_t program,
 			  const rpcvers_t version,
-			  struct timeval *timeout)
+			  struct timeval *timeout,
+			  struct local_bind_info *local_ip)
 {
 	nfs_clear_rpc_createerr();
 
@@ -465,11 +505,11 @@  CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
 	switch (transport) {
 	case IPPROTO_TCP:
 		return nfs_get_tcpclient(sap, salen, program, version,
-						timeout, 0);
+					 timeout, 0, local_ip);
 	case 0:
 	case IPPROTO_UDP:
 		return nfs_get_udpclient(sap, salen, program, version,
-						timeout, 0);
+					 timeout, 0, local_ip);
 	}
 
 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
@@ -499,7 +539,8 @@  CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
 			       const unsigned short transport,
 			       const rpcprog_t program,
 			       const rpcvers_t version,
-			       struct timeval *timeout)
+			       struct timeval *timeout,
+			       struct local_bind_info *local_ip)
 {
 	nfs_clear_rpc_createerr();
 
@@ -522,11 +563,11 @@  CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
 	switch (transport) {
 	case IPPROTO_TCP:
 		return nfs_get_tcpclient(sap, salen, program, version,
-						timeout, 1);
+					 timeout, 1, local_ip);
 	case 0:
 	case IPPROTO_UDP:
 		return nfs_get_udpclient(sap, salen, program, version,
-						timeout, 1);
+					 timeout, 1, local_ip);
 	}
 
 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index b1b5793..ccd44a2 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -34,6 +34,7 @@ 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <gssapi/gssapi.h>
+#include "sockaddr.h"
 
 #define MAX_FILE_NAMELEN	32
 #define FD_ALLOC_BLOCK		256
@@ -85,6 +86,7 @@  struct clnt_info {
 	int                     gssd_fd;
 	int                     gssd_poll_index;
 	struct sockaddr_storage addr;
+	struct local_bind_info  local_ip;
 };
 
 TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list;
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 41328c9..e584d20 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -726,7 +726,8 @@  out_err:
 static int
 populate_port(struct sockaddr *sa, const socklen_t salen,
 	      const rpcprog_t program, const rpcvers_t version,
-	      const unsigned short protocol)
+	      const unsigned short protocol,
+	      struct local_bind_info *local_ip)
 {
 	struct sockaddr_in	*s4 = (struct sockaddr_in *) sa;
 #ifdef IPV6_SUPPORTED
@@ -774,7 +775,7 @@  populate_port(struct sockaddr *sa, const socklen_t salen,
 		goto set_port;
 	}
 
-	port = nfs_getport(sa, salen, program, version, protocol);
+	port = nfs_getport(sa, salen, program, version, protocol, local_ip);
 	if (!port) {
 		printerr(0, "ERROR: unable to obtain port for prog %ld "
 			    "vers %ld\n", program, version);
@@ -807,7 +808,8 @@  int create_auth_rpc_client(struct clnt_info *clp,
 			   CLIENT **clnt_return,
 			   AUTH **auth_return,
 			   uid_t uid,
-			   int authtype)
+			   int authtype,
+			   struct local_bind_info *local_ip)
 {
 	CLIENT			*rpc_clnt = NULL;
 	struct rpc_gss_sec	sec;
@@ -899,11 +901,12 @@  int create_auth_rpc_client(struct clnt_info *clp,
 		goto out_fail;
 	}
 
-	if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
+	if (!populate_port(addr, salen, clp->prog, clp->vers,
+			   protocol, local_ip))
 		goto out_fail;
 
 	rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
-				     clp->vers, &timeout);
+				     clp->vers, &timeout, local_ip);
 	if (!rpc_clnt) {
 		snprintf(rpc_errmsg, sizeof(rpc_errmsg),
 			 "WARNING: can't create %s rpc_clnt to server %s for "
@@ -955,7 +958,7 @@  int create_auth_rpc_client(struct clnt_info *clp,
  */
 static void
 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
-		    char *service)
+		    char *service, struct local_bind_info *local_ip)
 {
 	CLIENT			*rpc_clnt = NULL;
 	AUTH			*auth = NULL;
@@ -1011,7 +1014,7 @@  process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
 				downcall_err = -EKEYEXPIRED;
 			else if (!err)
 				create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
-							     AUTHTYPE_KRB5);
+								     AUTHTYPE_KRB5, local_ip);
 			if (create_resp == 0)
 				break;
 		}
@@ -1038,7 +1041,8 @@  process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
 					gssd_setup_krb5_machine_gss_ccache(*ccname);
 					if ((create_auth_rpc_client(clp, &rpc_clnt,
 								    &auth, uid,
-								    AUTHTYPE_KRB5)) == 0) {
+								    AUTHTYPE_KRB5,
+								    local_ip)) == 0) {
 						/* Success! */
 						success++;
 						break;
@@ -1108,7 +1112,8 @@  out_return_error:
  * context on behalf of the kernel
  */
 static void
-process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
+process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd,
+		     struct local_bind_info *local_ip)
 {
 	CLIENT			*rpc_clnt = NULL;
 	AUTH			*auth = NULL;
@@ -1120,7 +1125,7 @@  process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
 	token.length = 0;
 	token.value = NULL;
 
-	if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
+	if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3, local_ip)) {
 		printerr(0, "WARNING: Failed to create spkm3 context for "
 			    "user with uid %d\n", uid);
 		goto out_return_error;
@@ -1167,7 +1172,7 @@  handle_krb5_upcall(struct clnt_info *clp)
 		return;
 	}
 
-	return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
+	process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL, &clp->local_ip);
 }
 
 void
@@ -1181,7 +1186,7 @@  handle_spkm3_upcall(struct clnt_info *clp)
 		return;
 	}
 
-	return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
+	process_spkm3_upcall(clp, uid, clp->spkm3_fd, &clp->local_ip);
 }
 
 void
@@ -1291,9 +1296,9 @@  handle_gssd_upcall(struct clnt_info *clp)
 	}
 
 	if (strcmp(mech, "krb5") == 0)
-		process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
+		process_krb5_upcall(clp, uid, clp->gssd_fd, target, service, &clp->local_ip);
 	else if (strcmp(mech, "spkm3") == 0)
-		process_spkm3_upcall(clp, uid, clp->gssd_fd);
+		process_spkm3_upcall(clp, uid, clp->gssd_fd, &clp->local_ip);
 	else
 		printerr(0, "WARNING: handle_gssd_upcall: "
 			    "received unknown gss mech '%s'\n", mech);
diff --git a/utils/mount/mount.c b/utils/mount/mount.c
index f3f0a83..4e08bc4 100644
--- a/utils/mount/mount.c
+++ b/utils/mount/mount.c
@@ -48,6 +48,7 @@ 
 #include "error.h"
 #include "stropts.h"
 #include "utils.h"
+#include "network.h"
 
 char *progname;
 int nfs_mount_data_version;
@@ -55,6 +56,7 @@  int nomtab;
 int verbose;
 int sloppy;
 int string;
+struct local_bind_info glb_local_ip;
 
 #define FOREGROUND	(0)
 #define BACKGROUND	(1)
@@ -305,6 +307,9 @@  static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
 
 	if ((len -= strlen(opt)) > 0)
 		strcat(extra_opts, opt);
+
+	if (strncmp(opt, "srcaddr=", strlen("srcaddr=")) == 0)
+		parse_local_bind(&glb_local_ip, opt + strlen("srcaddr="));
 }
 
 /*
@@ -345,21 +350,21 @@  static void parse_opts(const char *options, int *flags, char **extra_opts)
 }
 
 static int try_mount(char *spec, char *mount_point, int flags,
-			char *fs_type, char **extra_opts, char *mount_opts,
-			int fake, int bg)
+		     char *fs_type, char **extra_opts, char *mount_opts,
+		     int fake, int bg, struct local_bind_info *local_ip)
 {
 	int ret;
 
 	if (string)
 		ret = nfsmount_string(spec, mount_point, fs_type, flags,
-					extra_opts, fake, bg);
+				      extra_opts, fake, bg, local_ip);
 	else {
 		if (strcmp(fs_type, "nfs4") == 0)
 			ret = nfs4mount(spec, mount_point, flags,
-					extra_opts, fake, bg);
+					extra_opts, fake, bg, local_ip);
 		else
 			ret = nfsmount(spec, mount_point, flags,
-					extra_opts, fake, bg);
+				       extra_opts, fake, bg, local_ip);
 	}
 
 	if (ret)
@@ -515,7 +520,7 @@  int main(int argc, char *argv[])
 	}
 
 	mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts,
-				mount_opts, fake, FOREGROUND);
+			    mount_opts, fake, FOREGROUND, &glb_local_ip);
 	if (mnt_err == EX_BG) {
 		printf(_("%s: backgrounding \"%s\"\n"),
 			progname, spec);
@@ -535,8 +540,8 @@  int main(int argc, char *argv[])
 		}
 
 		mnt_err = try_mount(spec, mount_point, flags, fs_type,
-					&extra_opts, mount_opts, fake,
-					BACKGROUND);
+				    &extra_opts, mount_opts, fake,
+				    BACKGROUND, &glb_local_ip);
 		if (verbose && mnt_err)
 			printf(_("%s: giving up \"%s\"\n"),
 				progname, spec);
diff --git a/utils/mount/network.c b/utils/mount/network.c
index d1f91dc..fd194a5 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -404,30 +404,123 @@  out:
 	return 0;
 }
 
+
+void
+parse_local_bind(struct local_bind_info *laddr, const char* str) {
+	/* str is an IP address. */
+	int aiErr;
+	unsigned int i;
+	struct addrinfo *aiHead;
+	struct addrinfo hints;
+	char *node = NULL; /* ip addr */
+	char *service = NULL; /* port */
+	char *tmp = xstrdup(str);
+
+	laddr->is_set = 0;
+
+	memset(&hints, 0, sizeof(hints));
+
+	hints.ai_flags  = AI_NUMERICSERV;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+
+	if (str[0] == '[') {
+		/* IPv6 addr */
+		hints.ai_family = PF_INET6;
+		node = tmp + 1;
+		for (i = 0; i < strlen(node); i++) {
+			if (node[i] == ']') {
+				node[i] = 0;
+				service = &(node[i+1]);
+				break;
+			}
+		}
+	} else {
+		hints.ai_family = PF_INET;
+		node = tmp;
+		service = node;
+	}
+
+	if (service) {
+		int found_port = 0;
+		for (i = 0; i < strlen(service); i++) {
+			if (service[i] == ':') {
+				service += i+1;
+				found_port = 1;
+				break;
+			}
+		}
+		if (!found_port)
+			service = NULL;
+	}
+
+	aiErr = getaddrinfo(node, service, &hints, &aiHead);
+
+	/* If we tried PF_INET and it failed, try IPv6 instead
+	 * to see if it resolves properly.
+	 */
+	if ((aiErr != 0) && (hints.ai_family == PF_INET)) {
+		hints.ai_family = PF_INET6;
+		aiErr = getaddrinfo(node, service, &hints, &aiHead);
+	}
+
+	if (aiErr != 0) {
+		printf("node: %s service: %s  ai_family: %s  aiErr: %i %s\n",
+		       node, service,
+		       hints.ai_family == PF_INET6 ? "INET6" : "INET",
+		       aiErr, gai_strerror(aiErr));
+		perror("getaddrinfo");
+	} else {
+		if (aiHead) {
+			memcpy(&laddr->addr, aiHead->ai_addr, aiHead->ai_addrlen);
+			laddr->addrlen = aiHead->ai_addrlen;
+			laddr->is_set = true;
+			freeaddrinfo(aiHead);
+		}
+	}
+	free(tmp);
+}
+
 /*
  * Create a socket that is locally bound to a reserved or non-reserved port.
  *
  * 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)
+		      unsigned int timeout, int resvp, int conn,
+		      struct local_bind_info *local_ip)
 {
 	int so, cc, type;
 	struct sockaddr_in laddr;
 	socklen_t namelen = sizeof(laddr);
+	int f = AF_INET;
+
+	if (local_ip && local_ip->is_set)
+		f = local_ip->addr.sa.sa_family;
 
 	type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
-	if ((so = socket (AF_INET, type, p_prot)) < 0)
+
+	so = socket(f, type, p_prot);
+	if (so < 0)
 		goto err_socket;
 
-	laddr.sin_family = AF_INET;
+	laddr.sin_family = f;
 	laddr.sin_port = 0;
 	laddr.sin_addr.s_addr = htonl(INADDR_ANY);
 	if (resvp) {
+		/* TODO:  Support IPv6 */
+		if (local_ip && local_ip->is_set
+		    && local_ip->addr.sa.sa_family == AF_INET) {
+			struct sockaddr_in *si = &local_ip->addr.s4;
+			laddr.sin_addr.s_addr = si->sin_addr.s_addr;
+		}
 		if (bindresvport(so, &laddr) < 0)
 			goto err_bindresvport;
 	} else {
-		cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
+		if (local_ip && local_ip->is_set)
+			cc = bind(so, &local_ip->addr.sa, local_ip->addrlen);
+		else
+			cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
 		if (cc < 0)
 			goto err_bind;
 	}
@@ -537,7 +630,8 @@  static void nfs_pp_debug2(const char *str)
  */
 static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
 			  struct pmap *pmap, const unsigned long *versions,
-			  const unsigned int *protos)
+			  const unsigned int *protos,
+			  struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *saddr = &address.sa;
@@ -555,14 +649,16 @@  static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
 		if (verbose)
 			printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"),
 				progname, prog, *p_vers, *p_prot);
-		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
+		p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot,
+				     local_ip);
 		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,
-							*p_vers, *p_prot, NULL))
+						 *p_vers, *p_prot, NULL,
+						 local_ip))
 					goto out_ok;
 			} else
 				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
@@ -615,7 +711,8 @@  out_ok:
  * 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)
+			     struct pmap *pmap,
+			     struct local_bind_info *local_ip)
 {
 	if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
 		return 1;
@@ -626,10 +723,12 @@  static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
 		probe_proto = nfs_default_proto();
 
 		return nfs_probe_port(sap, salen, pmap,
-					probe_nfs3_first, probe_proto);
+				      probe_nfs3_first, probe_proto,
+				      local_ip);
 	} else
 		return nfs_probe_port(sap, salen, pmap,
-					probe_nfs2_only, probe_udp_only);
+				      probe_nfs2_only, probe_udp_only,
+				      local_ip);
 }
 
 /*
@@ -646,17 +745,20 @@  static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
  * 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,
-				struct pmap *pmap)
+			     struct pmap *pmap,
+			     struct local_bind_info *local_ip)
 {
 	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,
-					probe_mnt3_first, probe_udp_first);
+				      probe_mnt3_first, probe_udp_first,
+				      local_ip);
 	else
 		return nfs_probe_port(sap, salen, pmap,
-					probe_mnt1_first, probe_udp_only);
+				      probe_mnt1_first, probe_udp_only,
+				      local_ip);
 }
 
 /*
@@ -673,11 +775,12 @@  static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
 			struct pmap *mnt_pmap,
 			const struct sockaddr *nfs_saddr,
 			const socklen_t nfs_salen,
-			struct pmap *nfs_pmap)
+			struct pmap *nfs_pmap,
+			struct local_bind_info *local_ip)
 {
-	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
+	if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, local_ip))
 		return 0;
-	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+	return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap, local_ip);
 }
 
 /**
@@ -700,7 +803,8 @@  int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
 			struct pmap *mnt_pmap,
 			const struct sockaddr *nfs_saddr,
 			const socklen_t nfs_salen,
-			struct pmap *nfs_pmap)
+			struct pmap *nfs_pmap,
+			struct local_bind_info *local_ip)
 {
 	struct pmap save_nfs, save_mnt;
 	const unsigned long *probe_vers;
@@ -712,7 +816,8 @@  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_saddr, nfs_salen, nfs_pmap);
+					       nfs_saddr, nfs_salen, nfs_pmap,
+					       local_ip);
 
 	memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
 	memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
@@ -721,9 +826,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_saddr, nfs_salen, nfs_pmap, local_ip) != 0) {
 			mnt_pmap->pm_vers = *probe_vers;
-			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
+			if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap, local_ip) != 0)
 				return 1;
 			memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
 		}
@@ -753,7 +858,8 @@  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(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
+		    struct local_bind_info *local_ip)
 {
 	struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
 	struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
@@ -761,7 +867,7 @@  int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
 	return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
 					&mnt_server->pmap,
 					nfs_addr, sizeof(nfs_server->saddr),
-					&nfs_server->pmap);
+				   &nfs_server->pmap, local_ip);
 }
 
 static int nfs_probe_statd(void)
@@ -773,7 +879,8 @@  static int nfs_probe_statd(void)
 	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
 
 	return nfs_getport_ping(SAFE_SOCKADDR(&addr), sizeof(addr),
-				program, (rpcvers_t)1, IPPROTO_UDP);
+				program, (rpcvers_t)1, IPPROTO_UDP,
+				NULL);
 }
 
 /**
@@ -829,7 +936,8 @@  int start_statd(void)
  * We use a fast timeout since this call is advisory only.
  */
 int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
-		      const struct pmap *pmap, const dirpath *argp)
+		      const struct pmap *pmap, const dirpath *argp,
+		      struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *saddr = &address.sa;
@@ -841,7 +949,7 @@  int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
 	enum clnt_stat res = 0;
 
 	memcpy(saddr, sap, salen);
-	if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
+	if (nfs_probe_mntport(saddr, salen, &mnt_pmap, local_ip) == 0) {
 		if (verbose)
 			nfs_error(_("%s: Failed to discover mountd port%s"),
 				progname, clnt_spcreateerror(""));
@@ -851,7 +959,7 @@  int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
 
 	client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
 					mnt_pmap.pm_prog, mnt_pmap.pm_vers,
-					&timeout);
+					&timeout, local_ip);
 	if (client == NULL) {
 		if (verbose)
 			nfs_error(_("%s: Failed to create RPC client%s"),
@@ -899,7 +1007,8 @@  int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
  * 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(clnt_addr_t *mnt_server, dirpath *argp,
+		    struct local_bind_info *local_ip)
 {
 	struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
 	socklen_t salen = sizeof(mnt_server->saddr);
@@ -908,9 +1017,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(sap, salen, pmap, local_ip))
 		return 0;
-	clnt = mnt_openclnt(mnt_server, &msock);
+	clnt = mnt_openclnt(mnt_server, &msock, local_ip);
 	if (!clnt)
 		return 0;
 	res = clnt_call(clnt, MOUNTPROC_UMNT,
@@ -931,7 +1040,8 @@  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(clnt_addr_t *mnt_server, int *msock,
+		     struct local_bind_info *local_ip)
 {
 	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
 	struct pmap *mnt_pmap = &mnt_server->pmap;
@@ -939,7 +1049,7 @@  CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
 
 	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
 	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
-				TRUE, FALSE);
+			    TRUE, FALSE, local_ip);
 	if (*msock == RPC_ANYSOCK) {
 		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
 			/*
@@ -1008,7 +1118,7 @@  void mnt_closeclnt(CLIENT *clnt, int msock)
  */
 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 		const unsigned long vers, const unsigned int prot,
-		struct sockaddr_in *caddr)
+	      struct sockaddr_in *caddr, struct local_bind_info *local_ip)
 {
 	CLIENT *clnt = NULL;
 	int sock, status;
@@ -1016,7 +1126,7 @@  int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 	struct sockaddr dissolve;
 
 	rpc_createerr.cf_stat = status = 0;
-	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
+	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE, local_ip);
 	if (sock == RPC_ANYSOCK) {
 		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
 			/*
@@ -1659,7 +1769,8 @@  out:
  * parsed successfully; otherwise EX_FAIL.
  */
 int nfs_umount_do_umnt(struct mount_options *options,
-		       char **hostname, char **dirname)
+		       char **hostname, char **dirname,
+		       struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr address;
 	struct sockaddr *sap = &address.sa;
@@ -1686,7 +1797,7 @@  int nfs_umount_do_umnt(struct mount_options *options,
 		/* nfs_lookup reports any errors */
 		return EX_FAIL;
 
-	if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
+	if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname, local_ip) == 0)
 		/* nfs_advise_umount reports any errors */
 		return EX_FAIL;
 
diff --git a/utils/mount/network.h b/utils/mount/network.h
index 81c6f22..4af8fd1 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -25,6 +25,7 @@ 
 #define _NFS_UTILS_MOUNT_NETWORK_H
 
 #include <rpc/pmap_prot.h>
+#include "sockaddr.h"
 
 #define MNT_SENDBUFSIZE (2048U)
 #define MNT_RECVBUFSIZE (1024U)
@@ -35,14 +36,17 @@  typedef struct {
 	struct pmap pmap;
 } clnt_addr_t;
 
+void parse_local_bind(struct local_bind_info *laddr, const char* str);
+
 /* RPC call timeout values */
 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(clnt_addr_t *, clnt_addr_t *, struct local_bind_info *);
 int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
 			struct pmap *, const struct sockaddr *,
-			const socklen_t, struct pmap *);
+			const socklen_t, struct pmap *,
+			struct local_bind_info *);
 int nfs_gethostbyname(const char *, struct sockaddr_in *);
 int nfs_lookup(const char *hostname, const sa_family_t family,
 		struct sockaddr *sap, socklen_t *salen);
@@ -53,7 +57,7 @@  int nfs_callback_address(const struct sockaddr *, const socklen_t,
 		struct sockaddr *, socklen_t *);
 int clnt_ping(struct sockaddr_in *, const unsigned long,
 		const unsigned long, const unsigned int,
-		struct sockaddr_in *);
+	      struct sockaddr_in *, struct local_bind_info *);
 
 struct mount_options;
 
@@ -69,13 +73,15 @@  int start_statd(void);
 
 unsigned long nfsvers_to_mnt(const unsigned long);
 
-int nfs_call_umount(clnt_addr_t *, dirpath *);
+int nfs_call_umount(clnt_addr_t *, dirpath *, struct local_bind_info *);
 int nfs_advise_umount(const struct sockaddr *, const socklen_t,
-		      const struct pmap *, const dirpath *);
-CLIENT *mnt_openclnt(clnt_addr_t *, int *);
+		      const struct pmap *, const dirpath *,
+		      struct local_bind_info *);
+CLIENT *mnt_openclnt(clnt_addr_t *, int *, struct local_bind_info *);
 void mnt_closeclnt(CLIENT *, int);
 
 int nfs_umount_do_umnt(struct mount_options *options,
-		       char **hostname, char **dirname);
+		       char **hostname, char **dirname,
+		       struct local_bind_info *local_ip);
 
 #endif	/* _NFS_UTILS_MOUNT_NETWORK_H */
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index be91a25..bc957f3 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -463,6 +463,12 @@  by other clients, but can impact application and server performance.
 .IP
 The DATA AND METADATA COHERENCE section contains a
 detailed discussion of these trade-offs.
+.TP 1.5i
+.BI srcaddr= n.n.n.n
+Specifies a single IPv4 or IPv6 address
+to which the NFS client will bind its sockets.
+This can help force NFS to use a particular IP
+address on a multi-homed machine.
 .SS "Options for NFS versions 2 and 3 only"
 Use these options, along with the options in the above subsection,
 for NFS versions 2 and 3 only.
diff --git a/utils/mount/nfs4_mount.h b/utils/mount/nfs4_mount.h
index b03792e..8bf379c 100644
--- a/utils/mount/nfs4_mount.h
+++ b/utils/mount/nfs4_mount.h
@@ -68,6 +68,9 @@  struct nfs4_mount_data {
 #define NFS4_MOUNT_UNSHARED	0x8000	/* 5 */
 #define NFS4_MOUNT_FLAGMASK	0xFFFF
 
-int nfs4mount(const char *, const char *, int, char **, int, int);
+struct local_bind_info;
+
+int nfs4mount(const char *, const char *, int, char **, int, int,
+	      struct local_bind_info *);
 
 #endif
diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
index 028e7cd..432947d 100644
--- a/utils/mount/nfs4mount.c
+++ b/utils/mount/nfs4mount.c
@@ -172,7 +172,8 @@  static int get_my_ipv4addr(char *ip_addr, int len)
 }
 
 int nfs4mount(const char *spec, const char *node, int flags,
-	      char **extra_opts, int fake, int running_bg)
+	      char **extra_opts, int fake, int running_bg,
+	      struct local_bind_info *local_ip)
 {
 	static struct nfs4_mount_data data;
 	static char hostdir[1024];
@@ -425,7 +426,8 @@  int nfs4mount(const char *spec, const char *node, int flags,
 		}
 		client_addr.sin_family = 0;
 		client_addr.sin_addr.s_addr = 0;
-		clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
+		clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto,
+			  &client_addr, local_ip);
 		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..07b9cc3 100644
--- a/utils/mount/nfs_mount.h
+++ b/utils/mount/nfs_mount.h
@@ -80,7 +80,10 @@  struct nfs_mount_data {
 #define AUTH_GSS_SPKMP		390011
 #endif
 
-int	nfsmount(const char *, const char *, int , char **, int, int);
+struct local_bind_info;
+
+int	nfsmount(const char *, const char *, int , char **, int, int,
+		 struct local_bind_info *);
 int	nfsumount(int, char **);
 
 #endif	/* _NFS_UTILS_MOUNT_NFS_MOUNT_H */
diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
index 1298fe4..7d9a925 100644
--- a/utils/mount/nfsmount.c
+++ b/utils/mount/nfsmount.c
@@ -123,16 +123,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)
+	       mntarg_t *mntarg, mntres_t *mntres,
+	       struct local_bind_info *local_ip)
 {
 	CLIENT *clnt;
 	enum clnt_stat stat;
 	int msock;
 
-	if (!probe_bothports(mnt_server, nfs_server))
+	if (!probe_bothports(mnt_server, nfs_server, local_ip))
 		goto out_bad;
 
-	clnt = mnt_openclnt(mnt_server, &msock);
+	clnt = mnt_openclnt(mnt_server, &msock, local_ip);
 	if (!clnt)
 		goto out_bad;
 	/* make pointers in xdr_mountres3 NULL so
@@ -501,7 +502,8 @@  out_bad:
 
 int
 nfsmount(const char *spec, const char *node, int flags,
-	 char **extra_opts, int fake, int running_bg)
+	 char **extra_opts, int fake, int running_bg,
+	 struct local_bind_info *local_ip)
 {
 	char hostdir[1024];
 	char *hostname, *dirname, *old_opts, *mounthost = NULL;
@@ -681,7 +683,7 @@  nfsmount(const char *spec, const char *node, int flags,
 				sleep(30);
 
 			stat = nfs_call_mount(&mnt_server, &nfs_server,
-					      &dirname, &mntres);
+					      &dirname, &mntres, local_ip);
 			if (stat)
 				break;
 			memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
@@ -779,7 +781,7 @@  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(&mnt_server, &dirname, local_ip);
 			goto fail;
 		}
 noauth_flavors:
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
index 8cd2852..03ccf6d 100644
--- a/utils/mount/nfsumount.c
+++ b/utils/mount/nfsumount.c
@@ -57,7 +57,7 @@  extern int verbose;
 int force;
 int lazy;
 int remount;
-
+struct local_bind_info glb_local_ip;
 
 static int try_remount(const char *spec, const char *node)
 {
@@ -239,6 +239,7 @@  int nfsumount(int argc, char *argv[])
 	int c, ret;
 	char *spec;
 	struct mntentchn *mc;
+	char *opt;
 
 	if (argc < 2) {
 		umount_usage();
@@ -314,7 +315,7 @@  int nfsumount(int argc, char *argv[])
 			return EX_USAGE;
 		}
 		if (hasmntopt(&mc->m, "users") == NULL) {
-			char *opt = hasmntopt(&mc->m, "user");
+			opt = hasmntopt(&mc->m, "user");
 			struct passwd *pw;
 			char *comma;
 			size_t len;
@@ -334,6 +335,23 @@  int nfsumount(int argc, char *argv[])
 		}
 	}
 
+
+	if (mc) {
+		opt = hasmntopt(&mc->m, "srcaddr");
+		if ((opt != NULL) && strlen(opt) > strlen("srcaddr=")) {
+			char *sa = xstrdup(opt + strlen("srcaddr="));
+			int z;
+			for (z = 0; z < strlen(sa); z++) {
+				if (sa[z] == ',') {
+					sa[z] = 0;
+					break;
+				}
+			}
+			parse_local_bind(&glb_local_ip, sa);
+			free(sa);
+		}
+	}
+
 	ret = EX_SUCCESS;
 	if (mc) {
 		if (!lazy) {
@@ -344,7 +362,8 @@  int nfsumount(int argc, char *argv[])
 				 * we don't want to signal an error, as that
 				 * could cause /sbin/mount to retry!
 				 */
-				nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
+				nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts,
+					     &glb_local_ip);
 				break;
 			case 1:
 				break;
@@ -355,7 +374,7 @@  int nfsumount(int argc, char *argv[])
 		ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
 	} else if (*spec != '/') {
 		if (!lazy)
-			ret = nfs_umount23(spec, "tcp,v3");
+			ret = nfs_umount23(spec, "tcp,v3", &glb_local_ip);
 	} else
 		ret = del_mtab(NULL, spec);
 
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index f1aa503..31c41d6 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -92,6 +92,7 @@  struct nfsmount_info {
 	int			flags,		/* MS_ flags */
 				fake,		/* actually do the mount? */
 				child;		/* forked bg child? */
+	struct local_bind_info  *local_ip;      /* Local IP binding info */
 };
 
 #ifdef MOUNT_CONFIG
@@ -484,7 +485,8 @@  static int nfs_construct_new_options(struct mount_options *options,
  * FALSE is returned if some failure occurred.
  */
 static int
-nfs_rewrite_pmap_mount_options(struct mount_options *options)
+nfs_rewrite_pmap_mount_options(struct mount_options *options,
+			       struct local_bind_info *local_ip)
 {
 	union nfs_sockaddr nfs_address;
 	struct sockaddr *nfs_saddr = &nfs_address.sa;
@@ -534,7 +536,8 @@  nfs_rewrite_pmap_mount_options(struct mount_options *options)
 	 * negotiate.  Bail now if we can't contact it.
 	 */
 	if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
-				 nfs_saddr, nfs_salen, &nfs_pmap)) {
+				 nfs_saddr, nfs_salen, &nfs_pmap,
+				 local_ip)) {
 		errno = ESPIPE;
 		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
 			errno = EOPNOTSUPP;
@@ -589,7 +592,7 @@  static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
 }
 
 static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
-		struct sockaddr *sap, socklen_t salen)
+			     struct sockaddr *sap, socklen_t salen)
 {
 	struct mount_options *options = po_dup(mi->options);
 	int result = 0;
@@ -631,7 +634,7 @@  static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
 		printf(_("%s: trying text-based options '%s'\n"),
 			progname, *mi->extra_opts);
 
-	if (!nfs_rewrite_pmap_mount_options(options))
+	if (!nfs_rewrite_pmap_mount_options(options, mi->local_ip))
 		goto out_fail;
 
 	result = nfs_sys_mount(mi, options);
@@ -1028,7 +1031,8 @@  static int nfsmount_start(struct nfsmount_info *mi)
  * Returns a valid mount command exit code.
  */
 int nfsmount_string(const char *spec, const char *node, const char *type,
-		    int flags, char **extra_opts, int fake, int child)
+		    int flags, char **extra_opts, int fake, int child,
+		    struct local_bind_info *local_ip)
 {
 	struct nfsmount_info mi = {
 		.spec		= spec,
@@ -1039,6 +1043,7 @@  int nfsmount_string(const char *spec, const char *node, const char *type,
 		.flags		= flags,
 		.fake		= fake,
 		.child		= child,
+		.local_ip	= local_ip,
 	};
 	int retval = EX_FAIL;
 
diff --git a/utils/mount/stropts.h b/utils/mount/stropts.h
index b4fd888..68cebaa 100644
--- a/utils/mount/stropts.h
+++ b/utils/mount/stropts.h
@@ -25,6 +25,6 @@ 
 #define _NFS_UTILS_MOUNT_STROPTS_H
 
 int nfsmount_string(const char *, const char *, const char *, int,
-			char **, int, int);
+		    char **, int, int, struct local_bind_info *);
 
 #endif	/* _NFS_UTILS_MOUNT_STROPTS_H */
diff --git a/utils/mount/utils.c b/utils/mount/utils.c
index 298db39..c3d3467 100644
--- a/utils/mount/utils.c
+++ b/utils/mount/utils.c
@@ -153,7 +153,8 @@  int chk_mountpoint(const char *mount_point)
  * pmap tuple.  If the GETPORT call later fails to disambiguate them,
  * then we fail.
  */
-int nfs_umount23(const char *devname, char *string)
+int nfs_umount23(const char *devname, char *string,
+		 struct local_bind_info *local_ip)
 {
 	char *hostname = NULL, *dirname = NULL;
 	struct mount_options *options;
@@ -164,7 +165,8 @@  int nfs_umount23(const char *devname, char *string)
 
 	options = po_split(string);
 	if (options) {
-		result = nfs_umount_do_umnt(options, &hostname, &dirname);
+		result = nfs_umount_do_umnt(options, &hostname, &dirname,
+					    local_ip);
 		po_destroy(options);
 	} else
 		nfs_error(_("%s: option parsing error"), progname);
diff --git a/utils/mount/utils.h b/utils/mount/utils.h
index 3fcd504..8471458 100644
--- a/utils/mount/utils.h
+++ b/utils/mount/utils.h
@@ -31,6 +31,7 @@  void mount_usage(void);
 void umount_usage(void);
 int chk_mountpoint(const char *mount_point);
 
-int nfs_umount23(const char *devname, char *string);
+int nfs_umount23(const char *devname, char *string,
+		 struct local_bind_info *local_ip);
 
 #endif	/* !_NFS_UTILS_MOUNT_UTILS_H */