diff mbox

nfs-utils: Support binding to source address.

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

Commit Message

Ben Greear June 8, 2011, 5:39 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>
---
:100644 100644 d50fe94... d8ef257... M	support/include/nfsrpc.h
:100644 100644 9af2543... 3822b4b... M	support/include/sockaddr.h
:100644 100644 d74400b... e8256ff... M	support/nfs/getport.c
:100644 100644 c14efe8... 9cd8538... 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... 0c57ea4... M	utils/mount/mount.c
:100644 100644 d1f91dc... 1081144... M	utils/mount/network.c
:100644 100644 81c6f22... 4af8fd1... M	utils/mount/network.h
: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... 053a91f... 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... dfad580... 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   |   98 ++++++++++++++++++-------
 utils/gssd/gssd.h          |    2 +
 utils/gssd/gssd_proc.c     |   33 +++++----
 utils/mount/mount.c        |   34 ++++++--
 utils/mount/network.c      |  174 +++++++++++++++++++++++++++++++++++---------
 utils/mount/network.h      |   20 +++--
 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    |   20 ++++-
 utils/mount/stropts.c      |   15 +++-
 utils/mount/stropts.h      |    2 +-
 utils/mount/utils.c        |    8 ++-
 utils/mount/utils.h        |    3 +-
 18 files changed, 368 insertions(+), 137 deletions(-)

Comments

Jim Rees June 8, 2011, 6:36 p.m. UTC | #1
greearb@candelatech.com wrote:

  +	if (local_ip && local_ip->is_set) {
  +		sa = (struct sockaddr *)(char *)(&local_ip->addr);
  +		salen = local_ip->addrlen;
  +	} else {
  +		switch (family) {
  +		case AF_INET:
  +			sa = (struct sockaddr *)(char *)&sin;
  +			salen = sizeof(sin);
  +			break;
  +		case AF_INET6:
  +			sa = (struct sockaddr *)(char *)&sin6;
  +			salen = sizeof(sin6);
  +		default:
  +			break;
  +		}

Are the double casts actually necessary?  And if so, shouldn't the inner one
be (void *)?
--
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 8, 2011, 6:39 p.m. UTC | #2
On 06/08/2011 11:36 AM, Jim Rees wrote:
> greearb@candelatech.com wrote:
>
>    +	if (local_ip&&  local_ip->is_set) {
>    +		sa = (struct sockaddr *)(char *)(&local_ip->addr);
>    +		salen = local_ip->addrlen;
>    +	} else {
>    +		switch (family) {
>    +		case AF_INET:
>    +			sa = (struct sockaddr *)(char *)&sin;
>    +			salen = sizeof(sin);
>    +			break;
>    +		case AF_INET6:
>    +			sa = (struct sockaddr *)(char *)&sin6;
>    +			salen = sizeof(sin6);
>    +		default:
>    +			break;
>    +		}
>
> Are the double casts actually necessary?  And if so, shouldn't the inner one
> be (void *)?

They are not, as far as I know..I was copying some
similar code I found elsewhere in nfs-utils.

Unless someone knows a reason to keep this, I'll just
remove the inner cast.

Thanks,
Ben
J. Bruce Fields June 8, 2011, 7:11 p.m. UTC | #3
On Wed, Jun 08, 2011 at 10:39:08AM -0700, 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.

Possibly dumb question: there's an ongoing effort (I think--haven't
heard much from them recently?) to make the nfs and rpc code aware of
which network namespace it's running in.  Would that do what you need?

Does statd need changes too?

--b.
--
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
Chuck Lever June 8, 2011, 9:16 p.m. UTC | #4
On Jun 8, 2011, at 4:54 PM, Ben Greear wrote:

> On 06/08/2011 01:41 PM, Chuck Lever wrote:
>> 
>> On Jun 8, 2011, at 1:39 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.
>> 
>>   [ ... snipped ... ]
>> 
>>> diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
>>> index 9af2543..3822b4b 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 {
>>> +	struct sockaddr_storage addr;
>> 
>> For storing socket addresses, nfs_utils has "union nfs_sockaddr" which safely allows the equivalent of type-casting.  You should use that here.
>> 
>>> +	int addrlen;
>> 
>> socklen_t or size_t is preferred for sockaddr lengths.
>> 
>>> +	int is_set;
>> 
>> This should probably be _Bool or bool.
>> 
>> But why is this structure needed?  Why can't you pass a bind address via a struct sockaddr * just as bind(2) does?
> 
> Someone might one day want to bind to an interface with SO_BINDTODEVICE, so I thought
> it would be nice to pass a container around so that it would be easy to add a dev-name
> argument without touching all of the method signatures again.

My preference would be to go with something simple for now.  If we ever need more, it's easy to change.  These are not a published formal API.

Are you thinking of allowing users to specify either an IP address or a hostname as a value for this new option?

>> When you specify a source address for mountd and statd, wouldn't they need to register that address with rpcbind? That won't work for non-TI-RPC builds, and neither would it work for the kernel, which, according to Trond, will never support listening on specific addresses (he NACKed a patch to the kernel's rpcbind client to support this). Are you sure you need server-side changes too?
> 
> Much of the changes are just required to make things compile with the changes to the
> core socket building methods.  I don't notice any changes to mountd or statd in
> the patch I posted.  What sections are you speaking of?
> 
>> You can't pass a bind address to the mount command using a command line option, since command line options aren't stored in /etc/fstab or /etc/mtab.  How would umount.nfs learn of the bind address if it can't find it in /etc/mtab?
>> 
>> It should not read from an environment variable either... how would that work during system boot?  How would such a variable be set during system shutdown?  If we were to do this, it really should use a new mount option.
> 
> Ok, I've implemented it with a mount-option for my testing.  I'll remove the
> cmd-line-arg and environ variable logic.

I have to admit that parse_local_bind() doesn't make sense to me.  For example, I don't think there is any need for bind addresses to use the square bracket convention that is used for server addresses; that is there for the NFS special device simply because a colon is already the separator between the server IP address and the export path.  We don't have that problem for a bind address, since it always stands by itself.

I also imagine that the source address will need to be passed to the kernel via mount(2), yes?  That can only be done via a string mount option.  Anyway, point made.

And such a mount option would need to be documented in nfs(5) (see utils/mount/nfs.man), and changes included with your patches.

>> If you repost, I would do mountd, statd, and mount's getport implementation all in separate patches.
> 
> I'm happy to work on this, but plz point me to what parts of my patch are messing with
> mountd and statd.

I'm not sure why I had that impression, but I now see that there are no mountd or statd hunks.  If you are happy without server-side changes, then great!
Ben Greear June 8, 2011, 9:30 p.m. UTC | #5
On 06/08/2011 02:16 PM, Chuck Lever wrote:
>
> On Jun 8, 2011, at 4:54 PM, Ben Greear wrote:
>
>> On 06/08/2011 01:41 PM, Chuck Lever wrote:

>>> But why is this structure needed?  Why can't you pass a bind address via a struct sockaddr * just as bind(2) does?
>>
>> Someone might one day want to bind to an interface with SO_BINDTODEVICE, so I thought
>> it would be nice to pass a container around so that it would be easy to add a dev-name
>> argument without touching all of the method signatures again.
>
> My preference would be to go with something simple for now.  If we ever need more, it's easy to change.  These are not a published formal API.
>
> Are you thinking of allowing users to specify either an IP address or a hostname as a value for this new option?

I was thinking "eth5", for use with SO_BINDTODEVICE.

But, host-names might also be useful.

It's not that difficult to change the method signatures again, but it sure makes
the patch much bigger than it would otherwise need to be...

>>> When you specify a source address for mountd and statd, wouldn't they need to register that address with rpcbind? That won't work for non-TI-RPC builds, and neither would it work for the kernel, which, according to Trond, will never support listening on specific addresses (he NACKed a patch to the kernel's rpcbind client to support this). Are you sure you need server-side changes too?
>>
>> Much of the changes are just required to make things compile with the changes to the
>> core socket building methods.  I don't notice any changes to mountd or statd in
>> the patch I posted.  What sections are you speaking of?
>>
>>> You can't pass a bind address to the mount command using a command line option, since command line options aren't stored in /etc/fstab or /etc/mtab.  How would umount.nfs learn of the bind address if it can't find it in /etc/mtab?
>>>
>>> It should not read from an environment variable either... how would that work during system boot?  How would such a variable be set during system shutdown?  If we were to do this, it really should use a new mount option.
>>
>> Ok, I've implemented it with a mount-option for my testing.  I'll remove the
>> cmd-line-arg and environ variable logic.
>
> I have to admit that parse_local_bind() doesn't make sense to me.  For example, I don't think there is any need for bind addresses to use the square bracket convention that is used for server addresses; that is there for the NFS special device simply because a colon is already the separator between the server IP address and the export path.  We don't have that problem for a bind address, since it always stands by itself.
>
> I also imagine that the source address will need to be passed to the kernel via mount(2), yes?  That can only be done via a string mount option.  Anyway, point made.

I thought it might be nice to carry on the [] convention of v6 addresses, but it could
be optional.  I'll work on that as well.

>
> And such a mount option would need to be documented in nfs(5) (see utils/mount/nfs.man), and changes included with your patches.

I'm using "srcaddr=foo" currently.  I can update the man page if that option name looks like a good
choice to you (it is used by cifs currently for the same purpose).


Thanks,
Ben
NeilBrown June 9, 2011, 5:47 a.m. UTC | #6
On Wed,  8 Jun 2011 10:39:08 -0700 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.
> 

I gotta say I think this is rather horrible.....

As I understand it, the problem is bindresvport.
It binds to a port number before making a connection, so the local address
that is bound to is the 'default' rather than the best one to reach the given
target.  And in some network configs this can be bad, because e.g. the target
may not be able to reply to that 'default' address.

So you want to be able to specify the local endpoint fully when you bind, so
you require/allow the user to specify the local endpoint.

Wouldn't it be soooo much nicer if the tools could just figure out the
'correct' local endpoint and just use that?  Obviously "yes" but maybe that
isn't straight forward.  Have you looked into that at all?

Worst case (which may be so incredibly bad it isn't worth considering) is
that we could extract the routing table from the kernel and "figure it out".

But I suspect there is an easier way...  What if you create a UDP socket,
'connect' to some arbitrary port on the target machine, and then use
getsockname to get the local endpoint address of that socket.  That wouldn't
generate any network traffic, but should give you the preferred local
endpoint for talking to that peer??

For the in-kernel code I wouldn't accept a trick like that, but there is
presumably some way to find the preferred local endpoint for some address
more directly ... certainly worth asking on net-dev if we cannot figure one
out.


So I'm thinking:  yes, there is a real need, but I think there must be a
better solution.

What think you?

Thanks,
NeilBrown
--
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, 6:12 a.m. UTC | #7
On 06/08/2011 10:47 PM, NeilBrown wrote:
> On Wed,  8 Jun 2011 10:39:08 -0700 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.
>>
>
> I gotta say I think this is rather horrible.....

And you haven't even seen the kernel side yet ;)

> As I understand it, the problem is bindresvport.
> It binds to a port number before making a connection, so the local address
> that is bound to is the 'default' rather than the best one to reach the given
> target.  And in some network configs this can be bad, because e.g. the target
> may not be able to reply to that 'default' address.
>
> So you want to be able to specify the local endpoint fully when you bind, so
> you require/allow the user to specify the local endpoint.
>
> Wouldn't it be soooo much nicer if the tools could just figure out the
> 'correct' local endpoint and just use that?  Obviously "yes" but maybe that
> isn't straight forward.  Have you looked into that at all?

Not nicer in all cases.

Sometimes a user knows best, and may want to use non-obvious
routing setups, such as rules that send pkts to different gateways
if they are from different source addresses.

> Worst case (which may be so incredibly bad it isn't worth considering) is
> that we could extract the routing table from the kernel and "figure it out".

There are potentially multiple routing tables.

> But I suspect there is an easier way...  What if you create a UDP socket,
> 'connect' to some arbitrary port on the target machine, and then use
> getsockname to get the local endpoint address of that socket.  That wouldn't
> generate any network traffic, but should give you the preferred local
> endpoint for talking to that peer??
>
> For the in-kernel code I wouldn't accept a trick like that, but there is
> presumably some way to find the preferred local endpoint for some address
> more directly ... certainly worth asking on net-dev if we cannot figure one
> out.
>
>
> So I'm thinking:  yes, there is a real need, but I think there must be a
> better solution.
>
> What think you?

I think I really do want to specify the local IP address.  There
are lots of other tools that allow this (ping, ssh, cifs, etc),
and there is a good reason for allowing it, primarily when used
in conjunction with routing rules that are based on source
IP.

Thanks,
Ben
NeilBrown June 9, 2011, 6:38 a.m. UTC | #8
On Wed, 08 Jun 2011 23:12:00 -0700 Ben Greear <greearb@candelatech.com> wrote:

> On 06/08/2011 10:47 PM, NeilBrown wrote:
> > On Wed,  8 Jun 2011 10:39:08 -0700 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.
> >>
> >
> > I gotta say I think this is rather horrible.....
> 
> And you haven't even seen the kernel side yet ;)
> 
> > As I understand it, the problem is bindresvport.
> > It binds to a port number before making a connection, so the local address
> > that is bound to is the 'default' rather than the best one to reach the given
> > target.  And in some network configs this can be bad, because e.g. the target
> > may not be able to reply to that 'default' address.
> >
> > So you want to be able to specify the local endpoint fully when you bind, so
> > you require/allow the user to specify the local endpoint.
> >
> > Wouldn't it be soooo much nicer if the tools could just figure out the
> > 'correct' local endpoint and just use that?  Obviously "yes" but maybe that
> > isn't straight forward.  Have you looked into that at all?
> 
> Not nicer in all cases.
> 
> Sometimes a user knows best, and may want to use non-obvious
> routing setups, such as rules that send pkts to different gateways
> if they are from different source addresses.

I cannot say that I am convinced.  Such rules sound like they are asking for
trouble and I'm not surprised when they get it.

> >
> > What think you?
> 
> I think I really do want to specify the local IP address.  There
> are lots of other tools that allow this (ping, ssh, cifs, etc),
> and there is a good reason for allowing it, primarily when used
> in conjunction with routing rules that are based on source
> IP.

ping is a debugging tool and clearly a special case.  ssh is a bit of a
special case too.  cifs less so.   But there are far more tools that don't
allow you to specify a local endpoint address and I feel we should be strongly
discouraging configurations which need them.

So while I'm still not convinced your solution is a good idea, I admit I am
somewhat less convinced that it is a bad idea and I won't try to road-block.
But if some day someone points to existing use in nfs-utils as a
justification for adding source-address setting in firefox, I think I might
just scream.

Thanks,
NeilBrown

--
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
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..3822b4b 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 {
+	struct sockaddr_storage addr;
+	int addrlen;
+	int 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..9cd8538 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 = (struct sockaddr *)(char *)(&local_ip->addr);
+		salen = local_ip->addrlen;
+	} else {
+		switch (family) {
+		case AF_INET:
+			sa = (struct sockaddr *)(char *)&sin;
+			salen = sizeof(sin);
+			break;
+		case AF_INET6:
+			sa = (struct sockaddr *)(char *)&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 = (struct sockaddr *)(char *)(&local_ip->addr);
+	} else {
+		switch (family) {
+		case AF_INET:
+			sa = (struct sockaddr *)(char *)&sin;
+			break;
+		case AF_INET6:
+			sa = (struct sockaddr *)(char *)&sin6;
+		default:
+			break;
+		}
 	}
 
+	if (sa)
+		return bindresvport_sa(sock, sa);
+
 	errno = EAFNOSUPPORT;
 	return -1;
 }
@@ -174,14 +201,25 @@  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;
+		si = (struct sockaddr_in *)(&local_ip->addr);
+		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 +311,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 +340,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 +394,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 +423,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 +482,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 +506,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 +540,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 +564,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..0c57ea4 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)
@@ -377,6 +382,7 @@  int main(int argc, char *argv[])
 	char *spec, *mount_point, *fs_type = "nfs";
 	char *extra_opts = NULL, *mount_opts = NULL;
 	uid_t uid = getuid();
+	char *env;
 
 	progname = basename(argv[0]);
 
@@ -404,7 +410,7 @@  int main(int argc, char *argv[])
 	mount_config_init(progname);
 
 	argv[2] = argv[0]; /* so that getopt error messages are correct */
-	while ((c = getopt_long(argc - 2, argv + 2, "rvVwfno:hs",
+	while ((c = getopt_long(argc - 2, argv + 2, "rvVwfno:hsI:",
 				longopts, NULL)) != -1) {
 		switch (c) {
 		case 'r':
@@ -434,6 +440,9 @@  int main(int argc, char *argv[])
 		case 's':
 			++sloppy;
 			break;
+		case 'I':
+			parse_local_bind(&glb_local_ip, optarg);
+			break;
 		case 'h':
 		default:
 			mount_usage();
@@ -495,6 +504,13 @@  int main(int argc, char *argv[])
 
 	parse_opts(mount_opts, &flags, &extra_opts);
 
+	/* It is hard to pass new cmd line args to this, so allow env to
+	 * set local binding as well.
+	 */
+	env = getenv("MNT_SRCADDR");
+	if (env)
+		parse_local_bind(&glb_local_ip, env);
+
 	if (uid != 0) {
 		if (!(flags & (MS_USERS|MS_USER))) {
 			nfs_error(_("%s: permission denied"), progname);
@@ -515,7 +531,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 +551,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..1081144 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -404,30 +404,116 @@  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 (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 = 1;
+			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.ss_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.ss_family == AF_INET) {
+			struct sockaddr_in *si;
+			si = (struct sockaddr_in *)(&local_ip->addr);
+			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, (struct sockaddr *)&(local_ip->addr),
+				  local_ip->addrlen);
+		else
+			cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
 		if (cc < 0)
 			goto err_bind;
 	}
@@ -537,7 +623,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 +642,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 +704,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 +716,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 +738,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 +768,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 +796,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 +809,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 +819,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 +851,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 +860,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 +872,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 +929,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 +942,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 +952,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 +1000,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 +1010,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 +1033,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 +1042,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 +1111,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 +1119,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 +1762,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 +1790,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/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..053a91f 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)
 {
@@ -231,6 +231,7 @@  static struct option umount_longopts[] =
   { "no-mtab", 0, 0, 'n' },
   { "verbose", 0, 0, 'v' },
   { "read-only", 0, 0, 'r' },
+  { "local-ip", 0, 0, 'I' },
   { NULL, 0, 0, 0 }
 };
 
@@ -239,6 +240,7 @@  int nfsumount(int argc, char *argv[])
 	int c, ret;
 	char *spec;
 	struct mntentchn *mc;
+	char *env;
 
 	if (argc < 2) {
 		umount_usage();
@@ -251,13 +253,16 @@  int nfsumount(int argc, char *argv[])
 	argc -= 1;
 
 	argv[0] = argv[-1]; /* So that getopt error messages are correct */
-	while ((c = getopt_long (argc, argv, "fvnrlh",
+	while ((c = getopt_long (argc, argv, "fvnrlhI:",
 				umount_longopts, NULL)) != -1) {
 
 		switch (c) {
 		case 'f':
 			++force;
 			break;
+		case 'I':              /* bind to local IP */
+			parse_local_bind(&glb_local_ip, optarg);
+			break;
 		case 'v':
 			++verbose;
 			break;
@@ -280,7 +285,11 @@  int nfsumount(int argc, char *argv[])
 		umount_usage();
 		return EX_USAGE;
 	}
-	
+
+	env = getenv("MNT_BIND_ADDR");
+	if (env)
+		parse_local_bind(&glb_local_ip, env);
+
 	if (spec == NULL || (*spec != '/' && strchr(spec,':') == NULL)) {
 		nfs_error(_("%s: %s: not found\n"), progname, spec);
 		return EX_USAGE;
@@ -344,7 +353,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 +365,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..dfad580 100644
--- a/utils/mount/utils.c
+++ b/utils/mount/utils.c
@@ -104,6 +104,7 @@  void mount_usage(void)
 	printf(_("\t-n\t\tDo not update /etc/mtab\n"));
 	printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n"));
 	printf(_("\t-h\t\tPrint this help\n"));
+	printf(_("\t-I\t\tBind to local IP address\n"));
 	printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n"));
 }
 
@@ -115,6 +116,7 @@  void umount_usage(void)
 	printf(_("\t-n\tDo not update /etc/mtab\n"));
 	printf(_("\t-r\tremount\n"));
 	printf(_("\t-l\tlazy unmount\n"));
+	printf(_("\t-I\t\tBind to local IP address\n"));
 	printf(_("\t-h\tprint this help\n\n"));
 }
 
@@ -153,7 +155,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 +167,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 */