svc: make sure mountd can get ports from /etc/services
diff mbox

Message ID 4DAD48C7.9090808@cn.fujitsu.com
State New, archived
Headers show

Commit Message

Mi Jinlong April 19, 2011, 8:33 a.m. UTC
At RHEL, if user set port for mountd at /etc/services as 
"mount   12345/tcp", mountd should be bind to 12345, but the 
latest nfs-utils, mountd get a rand port, not 12345.

This patch make sure mountd be bind to the port which was set
at /etc/service.

Signed-off-by: Mi Jinlong <mijinlong@cn.fujitsu.com>
---
 support/include/rpcmisc.h |    1 +
 support/nfs/svc_create.c  |    9 ++++-
 support/nfs/svc_socket.c  |   83 +++++++++++++++++++++-----------------------
 3 files changed, 48 insertions(+), 45 deletions(-)

Comments

Chuck Lever April 19, 2011, 1:28 p.m. UTC | #1
Hi MJ-

On Apr 19, 2011, at 4:33 AM, Mi Jinlong wrote:

> At RHEL, if user set port for mountd at /etc/services as 
> "mount   12345/tcp", mountd should be bind to 12345, but the 
> latest nfs-utils, mountd get a rand port, not 12345.
> 
> This patch make sure mountd be bind to the port which was set
> at /etc/service.

I don't think this is documented anywhere.  Is there a reason it should work this way?

The typical way to set mountd's port is to use a command line option.  That's the way it works for all the other RPC daemons.  By default the ports are set up at random and registered with rpcbind.  That's why clients use rpcbind, and not /etc/services, to find these services.

> Signed-off-by: Mi Jinlong <mijinlong@cn.fujitsu.com>
> ---
> support/include/rpcmisc.h |    1 +
> support/nfs/svc_create.c  |    9 ++++-
> support/nfs/svc_socket.c  |   83 +++++++++++++++++++++-----------------------
> 3 files changed, 48 insertions(+), 45 deletions(-)
> 
> diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
> index 0b06457..b806227 100644
> --- a/support/include/rpcmisc.h
> +++ b/support/include/rpcmisc.h
> @@ -53,6 +53,7 @@ void		rpc_init(char *name, int prog, int vers,
> void		rpc_dispatch(struct svc_req *rq, SVCXPRT *xprt,
> 				struct rpc_dtable *dtable, int nvers,
> 				void *argp, void *resp);
> +int		getservport(u_long number, const char *proto);
> 
> extern int	_rpcpmstart;
> extern int	_rpcfdtype;
> diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
> index b3f75ed..fd09902 100644
> --- a/support/nfs/svc_create.c
> +++ b/support/nfs/svc_create.c
> @@ -393,7 +393,7 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
> 	const struct sigaction create_sigaction = {
> 		.sa_handler	= SIG_IGN,
> 	};
> -	unsigned int visible, up;
> +	unsigned int visible, up, servport;
> 	struct netconfig *nconf;
> 	void *handlep;
> 
> @@ -417,8 +417,13 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
> 		if (!(nconf->nc_flag & NC_VISIBLE))
> 			continue;
> 		visible++;
> +		if (port == 0)
> +			servport = getservport(program, nconf->nc_proto);
> +		else
> +			servport = port;
> +		
> 		up += svc_create_nconf(name, program, version, dispatch,
> -						port, nconf);
> +						servport, nconf);
> 	}
> 
> 	if (visible == 0)
> diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
> index 03a5325..ec406a9 100644
> --- a/support/nfs/svc_socket.c
> +++ b/support/nfs/svc_socket.c
> @@ -35,14 +35,46 @@
> # define __close(f)		close ((f))
> #endif
> 
> +int getservport(u_long number, const char *proto)
> +{
> +	char rpcdata [1024], servdata [1024];
> +	struct rpcent rpcbuf, *rpcp;
> +	struct servent servbuf, *servp = NULL;
> +	int ret;
> +
> +	ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
> +				&rpcp);
> +	if (ret == 0 && rpcp != NULL)
> +	{
> +		/* First try name.  */
> +		ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
> +					sizeof servdata, &servp);
> +		if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
> +		{
> +			const char **a;
> +
> +			/* Then we try aliases.  */
> +			for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
> +			{
> +				ret = getservbyname_r (*a, proto, &servbuf, servdata,
> +							sizeof servdata, &servp);
> +				if (ret == 0 && servp != NULL)
> +					break;
> +			}
> +		}
> +	}
> +
> +	if (ret == 0 && servp != NULL)
> +		return  ntohs(servp->s_port);
> +
> +	return 0;
> +}
> +
> static int
> svc_socket (u_long number, int type, int protocol, int reuse)
> {
>   struct sockaddr_in addr;
>   socklen_t len = sizeof (struct sockaddr_in);
> -  char rpcdata [1024], servdata [1024];
> -  struct rpcent rpcbuf, *rpcp;
> -  struct servent servbuf, *servp = NULL;
>   int sock, ret;
>   const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
> 
> @@ -66,48 +98,13 @@ svc_socket (u_long number, int type, int protocol, int reuse)
> 
>   memset (&addr, 0, sizeof (addr));
>   addr.sin_family = AF_INET;
> +  addr.sin_port = htons(getservport(number, proto));
> 
> -  ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
> -			  &rpcp);
> -  if (ret == 0 && rpcp != NULL)
> +  if (bind (sock, (struct sockaddr *) &addr, len) < 0)
>     {
> -      /* First try name.  */
> -      ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
> -			     sizeof servdata, &servp);
> -      if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
> -	{
> -	  const char **a;
> -
> -	  /* Then we try aliases.  */
> -	  for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
> -	    {
> -	      ret = getservbyname_r (*a, proto, &servbuf, servdata,
> -				     sizeof servdata, &servp);
> -	      if (ret == 0 && servp != NULL)
> -		break;
> -	    }
> -	}
> -    }
> -
> -  if (ret == 0 && servp != NULL)
> -    {
> -      addr.sin_port = servp->s_port;
> -      if (bind (sock, (struct sockaddr *) &addr, len) < 0)
> -	{
> -	  perror (_("svc_socket: bind problem"));
> -	  (void) __close (sock);
> -	  sock = -1;
> -	}
> -    }
> -  else
> -    {
> -	  addr.sin_port = 0;
> -	  if (bind (sock, (struct sockaddr *) &addr, len) < 0)
> -	    {
> -	      perror (_("svc_socket: bind problem"));
> -	      (void) __close (sock);
> -	      sock = -1;
> -	    }
> +      perror (_("svc_socket: bind problem"));
> +      (void) __close (sock);
> +      sock = -1;
>     }
> 
>   if (sock >= 0)
> -- 
> 1.7.4.1
> 
> 
>
Mi Jinlong April 20, 2011, 9:29 a.m. UTC | #2
Chuck Lever:
> Hi MJ-
> 
> On Apr 19, 2011, at 4:33 AM, Mi Jinlong wrote:
> 
>> At RHEL, if user set port for mountd at /etc/services as 
>> "mount   12345/tcp", mountd should be bind to 12345, but the 
>> latest nfs-utils, mountd get a rand port, not 12345.
>>
>> This patch make sure mountd be bind to the port which was set
>> at /etc/service.
> 
> I don't think this is documented anywhere.  Is there a reason it should work this way?
> 
> The typical way to set mountd's port is to use a command line option.  That's the way it works for all the other RPC daemons.  By default the ports are set up at random and registered with rpcbind.  That's why clients use rpcbind, and not /etc/services, to find these services.

  I don't have a depth research, agree with you.
  But I got different result when I set port for mountd at /etc/services
  between nfs-utils-1.2.3 and nfs-utils-1.2.2.

  I just think we should get the same result at new nfs-utils as older.
Chuck Lever April 20, 2011, 3:08 p.m. UTC | #3
On Apr 20, 2011, at 5:29 AM, Mi Jinlong wrote:

> 
> 
> Chuck Lever:
>> Hi MJ-
>> 
>> On Apr 19, 2011, at 4:33 AM, Mi Jinlong wrote:
>> 
>>> At RHEL, if user set port for mountd at /etc/services as 
>>> "mount   12345/tcp", mountd should be bind to 12345, but the 
>>> latest nfs-utils, mountd get a rand port, not 12345.
>>> 
>>> This patch make sure mountd be bind to the port which was set
>>> at /etc/service.
>> 
>> I don't think this is documented anywhere.  Is there a reason it should work this way?
>> 
>> The typical way to set mountd's port is to use a command line option.  That's the way it works for all the other RPC daemons.  By default the ports are set up at random and registered with rpcbind.  That's why clients use rpcbind, and not /etc/services, to find these services.
> 
>  I don't have a depth research, agree with you.
>  But I got different result when I set port for mountd at /etc/services
>  between nfs-utils-1.2.3 and nfs-utils-1.2.2.

IMO that's unintentional behavior.  Could I ask a favor: would you bisect nfs-utils to find out exactly where this started and ended?

>  I just think we should get the same result at new nfs-utils as older.

Is there a real-world use case for this feature?  Why isn't the command line option adequate?
Mi Jinlong April 21, 2011, 3:42 a.m. UTC | #4
Chuck Lever:
> On Apr 20, 2011, at 5:29 AM, Mi Jinlong wrote:
> 
>>
>> Chuck Lever:
>>> Hi MJ-
>>>
>>> On Apr 19, 2011, at 4:33 AM, Mi Jinlong wrote:
>>>
>>>> At RHEL, if user set port for mountd at /etc/services as 
>>>> "mount   12345/tcp", mountd should be bind to 12345, but the 
>>>> latest nfs-utils, mountd get a rand port, not 12345.
>>>>
>>>> This patch make sure mountd be bind to the port which was set
>>>> at /etc/service.
>>> I don't think this is documented anywhere.  Is there a reason it should work this way?
>>>
>>> The typical way to set mountd's port is to use a command line option.  That's the way it works for all the other RPC daemons.  By default the ports are set up at random and registered with rpcbind.  That's why clients use rpcbind, and not /etc/services, to find these services.
>>  I don't have a depth research, agree with you.
>>  But I got different result when I set port for mountd at /etc/services
>>  between nfs-utils-1.2.3 and nfs-utils-1.2.2.
> 
> IMO that's unintentional behavior.  Could I ask a favor: would you bisect nfs-utils to find out exactly where this started and ended?

  Hi Chuck,

  It's after your patch "mountd: Support TI-RPC mountd listener" 
  (commit id:b551b1fd0052de9b8c674b30c39d9f2a1e9d79cc).

  Before this patch, mountd call rpc_init to create rpc socket,
  after it, if HAVE_LIBTIRPC is defined, mountd will call 
  nfs_svc_create to create rpc socket, that appears.

> 
>>  I just think we should get the same result at new nfs-utils as older.
> 
> Is there a real-world use case for this feature?  Why isn't the command line option adequate?
> 

  I have a test site which depend on this feature.
  If this feature indeed unnecessary, I will try to modify my test site.
Chuck Lever April 21, 2011, 2:11 p.m. UTC | #5
On Apr 20, 2011, at 11:42 PM, Mi Jinlong wrote:

> 
> 
> Chuck Lever:
>> On Apr 20, 2011, at 5:29 AM, Mi Jinlong wrote:
>> 
>>> 
>>> Chuck Lever:
>>>> Hi MJ-
>>>> 
>>>> On Apr 19, 2011, at 4:33 AM, Mi Jinlong wrote:
>>>> 
>>>>> At RHEL, if user set port for mountd at /etc/services as 
>>>>> "mount   12345/tcp", mountd should be bind to 12345, but the 
>>>>> latest nfs-utils, mountd get a rand port, not 12345.
>>>>> 
>>>>> This patch make sure mountd be bind to the port which was set
>>>>> at /etc/service.
>>>> I don't think this is documented anywhere.  Is there a reason it should work this way?
>>>> 
>>>> The typical way to set mountd's port is to use a command line option.  That's the way it works for all the other RPC daemons.  By default the ports are set up at random and registered with rpcbind.  That's why clients use rpcbind, and not /etc/services, to find these services.
>>> I don't have a depth research, agree with you.
>>> But I got different result when I set port for mountd at /etc/services
>>> between nfs-utils-1.2.3 and nfs-utils-1.2.2.
>> 
>> IMO that's unintentional behavior.  Could I ask a favor: would you bisect nfs-utils to find out exactly where this started and ended?
> 
>  Hi Chuck,
> 
>  It's after your patch "mountd: Support TI-RPC mountd listener" 
>  (commit id:b551b1fd0052de9b8c674b30c39d9f2a1e9d79cc).
> 
>  Before this patch, mountd call rpc_init to create rpc socket,
>  after it, if HAVE_LIBTIRPC is defined, mountd will call 
>  nfs_svc_create to create rpc socket, that appears.

Thanks.  I don't immediately see how that code path consults /etc/services.  There are no calls to getservby{name,port}(3) in either mountd or in libtirpc.  Did I miss something?  Any idea?

You said it stopped working after a while.  Can you bisect that as well?

Note that if nfs-utils is built without TI-RPC support, rpc_init() is still used instead of nfs_svc_create().  If nfs_svc_create() is indeed the proximal cause of this behavior, mountd won't consult /etc/services if it is built without TI-RPC support.  So it might be a bug if this works for some build configurations and doesn't work for others.  The question is should it always work, or should it never work; see below.

> 
>> 
>>> I just think we should get the same result at new nfs-utils as older.
>> 
>> Is there a real-world use case for this feature?  Why isn't the command line option adequate?
>> 
> 
>  I have a test site which depend on this feature.
>  If this feature indeed unnecessary, I will try to modify my test site.

Without knowing more about your test site, I claim it can and should use the "-p" option on rpc.mountd.  If your distribution of choice is Red Hat-based, you can set this in /etc/sysconfig/nfs (see MOUNTD_PORT=).

In other words, we have a documented and supported mechanism now, that should always work, for setting mountd's listener port.  Can you explain in detail why allowing /etc/services to have this effect is also necessary?
Jim Rees May 28, 2011, 1:29 p.m. UTC | #6
Mi Jinlong wrote:

  At RHEL, if user set port for mountd at /etc/services as 
  "mount   12345/tcp", mountd should be bind to 12345, but the 
  latest nfs-utils, mountd get a rand port, not 12345.
  
  This patch make sure mountd be bind to the port which was set
  at /etc/service.

Is this really such a good idea?  I would find this behavior surprising.  I
expect listeners to either use a well-known port, in which case they look in
/etc/services and fall back to a compiled-in constant (like telnet or ftp),
or use an ephemeral port, in which case they don't even look at
/etc/services.  This patch would change mountd so that its behavior
(well-known versus ephemeral) depends on /etc/services rather than a
run-time option.

The change in behavior would not be immediately obvious, either, because who
is going to notice that mountd is now on a well-known port?

You could argue that the admin would have to add a line to /etc/services for
anything to change, and I guess I could be convinced.  But are you sure some
distro packaging person isn't going to put that line in without
understanding the implications?

Yes, I know putting mountd on a random port isn't going to thwart a
determined hacker.  I'm thinking of the nuisance factor.  Consider ssh.
It's a secure protocol, so there isn't really a security risk with leaving
it on port 22, but sometimes you have to move it off to keep the log files
from filling up with crap.

Here's an alternate proposal.  Have the "-p" option take either a number or
a name.  If it's a name, look it up in /etc/services.
--
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 May 28, 2011, 4:01 p.m. UTC | #7
Hi Jim-

On May 28, 2011, at 9:29 AM, Jim Rees wrote:

> Mi Jinlong wrote:
> 
>  At RHEL, if user set port for mountd at /etc/services as 
>  "mount   12345/tcp", mountd should be bind to 12345, but the 
>  latest nfs-utils, mountd get a rand port, not 12345.
> 
>  This patch make sure mountd be bind to the port which was set
>  at /etc/service.
> 
> Is this really such a good idea?  I would find this behavior surprising.

I also find this behavior surprising.  However, this patch fixes a regression.  If you build mountd today with the legacy RPC library, it already consults /etc/services, and has done for a long while.  Mi is simply restoring this traditional behavior for newer TI-RPC builds.

> I expect listeners to either use a well-known port, in which case they look in
> /etc/services and fall back to a compiled-in constant (like telnet or ftp),
> or use an ephemeral port, in which case they don't even look at
> /etc/services.  This patch would change mountd so that its behavior
> (well-known versus ephemeral) depends on /etc/services rather than a
> run-time option.

As I read it: If -p is specified, mountd uses that value for the listener port.  Otherwise, if -p is not specified, mountd looks in /etc/services, and uses the port listed there.  If there is no "mountd" service listed in /etc/services, it uses an ephemeral port.

For any ONCRPC service other than NFS and rpcbind, there is typically no built-in fixed port.  In that sense, the rpc. daemons are not like telnet or ftp.  Ephemeral ports are used and registered with rpcbind, so well-known ports are not needed.

> The change in behavior would not be immediately obvious, either, because who
> is going to notice that mountd is now on a well-known port?

A problem certainly arises going the other way: if the port value was fixed in order to transit a firewall, and the port becomes ephemeral, then the problem becomes obvious.  This is exactly how mountd is broken today since, when built with TI-RPC, it no longer consults /etc/services and instead it uses an ephemeral port.  Admittedly, this is rare.

> You could argue that the admin would have to add a line to /etc/services for
> anything to change, and I guess I could be convinced.  But are you sure some
> distro packaging person isn't going to put that line in without
> understanding the implications?

/etc/services does not by default contain an entry for mountd (or statd, which is also affected by this change).  Without those entries, the daemons behave exactly as you expect.  This is probably why we never noticed mountd looked in /etc/services.  That, and this behavior isn't documented anywhere.

Since there is no IANA-listed port number assignment for the legacy mountd or statd services, I doubt /etc/services will be changed by a distro as you suggest.  However, distros will change the file to add new well-known ports for other services.  Installing a new copy will erase any local changes.  This is a good reason to prefer -p instead.

> Yes, I know putting mountd on a random port isn't going to thwart a
> determined hacker.  I'm thinking of the nuisance factor.  Consider ssh.
> It's a secure protocol, so there isn't really a security risk with leaving
> it on port 22, but sometimes you have to move it off to keep the log files
> from filling up with crap.
> 
> Here's an alternate proposal.  Have the "-p" option take either a number or
> a name.  If it's a name, look it up in /etc/services.

-p should be able to take a name anyway.  It would be a simple change.

I personally think -p is sufficient for setting the listener port as needed, and no-one has identified any hard requirement (like ancient HA clustering scripts that everyone uses and no-one wants to change) for having mountd read /etc/services.

I believe /etc/services is a well-known port registry, a local copy of IANA's registry.  Because mountd is an RPC service, fixing mountd's port is a local setting, and is not based on any standard.  Thus /etc/services is not an appropriate mechanism for setting mountd's listener port value.

So, we could also address this by getting rid of the legacy RPC behavior instead.  However, in cases like this, I typically choose to stick with legacy behavior, since that improves backwards compatibility.  It already works like this for legacy RPC builds, so in some sense we are stuck with it.

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



--
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
Jim Rees May 28, 2011, 4:45 p.m. UTC | #8
Chuck Lever wrote:

  So, we could also address this by getting rid of the legacy RPC behavior
  instead.  However, in cases like this, I typically choose to stick with
  legacy behavior, since that improves backwards compatibility.  It already
  works like this for legacy RPC builds, so in some sense we are stuck with
  it.

Ok.  I remember the earlier discussion but forgot that the patch simply
restores older behavior that was changed by tirpc.  In that case I'll go
along with Mi's patch.
--
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
Steve Dickson June 7, 2011, 8:17 p.m. UTC | #9
On 05/28/2011 12:45 PM, Jim Rees wrote:
> Chuck Lever wrote:
> 
>   So, we could also address this by getting rid of the legacy RPC behavior
>   instead.  However, in cases like this, I typically choose to stick with
>   legacy behavior, since that improves backwards compatibility.  It already
>   works like this for legacy RPC builds, so in some sense we are stuck with
>   it.
> 
> Ok.  I remember the earlier discussion but forgot that the patch simply
> restores older behavior that was changed by tirpc.  In that case I'll go
> along with Mi's patch.
I am all for restoring the older behavior, but unfortunately this patch
does not do it. 

I when back and took a look at how the  nfs-utils-1.2.2 code worked.
While its true both mountd and statd read ports from /etc/service, 
they did not fail when those ports were already taken. They just 
bound to random ephemeral ports, which is probably the reason none of 
us noticed they were reading ports out of /etc/services. With
Mi's patch, both daemons fail when the ports in /etc/service are
already taken. 

Mi, unfortunately I am not going to have any cycles to look
at this, due to the upcoming Bakeathon. So, could you please,
look into why the 1.2.2 recovered when ports in /etc/services
were taken and your patch does not.

tia,

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
Mi Jinlong June 10, 2011, 8:23 a.m. UTC | #10
Steve Dickson:
> On 05/28/2011 12:45 PM, Jim Rees wrote:
>> Chuck Lever wrote:
>>
>>   So, we could also address this by getting rid of the legacy RPC behavior
>>   instead.  However, in cases like this, I typically choose to stick with
>>   legacy behavior, since that improves backwards compatibility.  It already
>>   works like this for legacy RPC builds, so in some sense we are stuck with
>>   it.
>>
>> Ok.  I remember the earlier discussion but forgot that the patch simply
>> restores older behavior that was changed by tirpc.  In that case I'll go
>> along with Mi's patch.
> I am all for restoring the older behavior, but unfortunately this patch
> does not do it. 
> 
> I when back and took a look at how the  nfs-utils-1.2.2 code worked.
> While its true both mountd and statd read ports from /etc/service, 
> they did not fail when those ports were already taken. They just 
> bound to random ephemeral ports, which is probably the reason none of 
> us noticed they were reading ports out of /etc/services. With
> Mi's patch, both daemons fail when the ports in /etc/service are
> already taken. 

Hi steve,

  Do you mean the daemons can't run?

  I test nfs-utils-1.2.2 at fedora15, mountd can bind to the port 
  reading form /etc/service. 

  So, can you post some error or other message here?

thanks,
Mi Jinlong

--
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

Patch
diff mbox

diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
index 0b06457..b806227 100644
--- a/support/include/rpcmisc.h
+++ b/support/include/rpcmisc.h
@@ -53,6 +53,7 @@  void		rpc_init(char *name, int prog, int vers,
 void		rpc_dispatch(struct svc_req *rq, SVCXPRT *xprt,
 				struct rpc_dtable *dtable, int nvers,
 				void *argp, void *resp);
+int		getservport(u_long number, const char *proto);
 
 extern int	_rpcpmstart;
 extern int	_rpcfdtype;
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
index b3f75ed..fd09902 100644
--- a/support/nfs/svc_create.c
+++ b/support/nfs/svc_create.c
@@ -393,7 +393,7 @@  nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
 	const struct sigaction create_sigaction = {
 		.sa_handler	= SIG_IGN,
 	};
-	unsigned int visible, up;
+	unsigned int visible, up, servport;
 	struct netconfig *nconf;
 	void *handlep;
 
@@ -417,8 +417,13 @@  nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
 		if (!(nconf->nc_flag & NC_VISIBLE))
 			continue;
 		visible++;
+		if (port == 0)
+			servport = getservport(program, nconf->nc_proto);
+		else
+			servport = port;
+		
 		up += svc_create_nconf(name, program, version, dispatch,
-						port, nconf);
+						servport, nconf);
 	}
 
 	if (visible == 0)
diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
index 03a5325..ec406a9 100644
--- a/support/nfs/svc_socket.c
+++ b/support/nfs/svc_socket.c
@@ -35,14 +35,46 @@ 
 # define __close(f)		close ((f))
 #endif
 
+int getservport(u_long number, const char *proto)
+{
+	char rpcdata [1024], servdata [1024];
+	struct rpcent rpcbuf, *rpcp;
+	struct servent servbuf, *servp = NULL;
+	int ret;
+
+	ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
+				&rpcp);
+	if (ret == 0 && rpcp != NULL)
+	{
+		/* First try name.  */
+		ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
+					sizeof servdata, &servp);
+		if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
+		{
+			const char **a;
+
+			/* Then we try aliases.  */
+			for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
+			{
+				ret = getservbyname_r (*a, proto, &servbuf, servdata,
+							sizeof servdata, &servp);
+				if (ret == 0 && servp != NULL)
+					break;
+			}
+		}
+	}
+
+	if (ret == 0 && servp != NULL)
+		return  ntohs(servp->s_port);
+
+	return 0;
+}
+
 static int
 svc_socket (u_long number, int type, int protocol, int reuse)
 {
   struct sockaddr_in addr;
   socklen_t len = sizeof (struct sockaddr_in);
-  char rpcdata [1024], servdata [1024];
-  struct rpcent rpcbuf, *rpcp;
-  struct servent servbuf, *servp = NULL;
   int sock, ret;
   const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
 
@@ -66,48 +98,13 @@  svc_socket (u_long number, int type, int protocol, int reuse)
 
   memset (&addr, 0, sizeof (addr));
   addr.sin_family = AF_INET;
+  addr.sin_port = htons(getservport(number, proto));
 
-  ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
-			  &rpcp);
-  if (ret == 0 && rpcp != NULL)
+  if (bind (sock, (struct sockaddr *) &addr, len) < 0)
     {
-      /* First try name.  */
-      ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
-			     sizeof servdata, &servp);
-      if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
-	{
-	  const char **a;
-
-	  /* Then we try aliases.  */
-	  for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
-	    {
-	      ret = getservbyname_r (*a, proto, &servbuf, servdata,
-				     sizeof servdata, &servp);
-	      if (ret == 0 && servp != NULL)
-		break;
-	    }
-	}
-    }
-
-  if (ret == 0 && servp != NULL)
-    {
-      addr.sin_port = servp->s_port;
-      if (bind (sock, (struct sockaddr *) &addr, len) < 0)
-	{
-	  perror (_("svc_socket: bind problem"));
-	  (void) __close (sock);
-	  sock = -1;
-	}
-    }
-  else
-    {
-	  addr.sin_port = 0;
-	  if (bind (sock, (struct sockaddr *) &addr, len) < 0)
-	    {
-	      perror (_("svc_socket: bind problem"));
-	      (void) __close (sock);
-	      sock = -1;
-	    }
+      perror (_("svc_socket: bind problem"));
+      (void) __close (sock);
+      sock = -1;
     }
 
   if (sock >= 0)