Skip to contentSkip to conversation

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

PATCH In review Mi Jinlong 4 participants 15 messages Apr 19, 2011 – Aug 3, 2011

Subsystems (2)
lore
Share & tools
lore

Mi Jinlong

author
#0 lore raw

PATCH 1/1 svc: make sure mountd can get ports from /etc/services

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.

---
 support/include/rpcmisc.h |    1 +
 support/nfs/svc_create.c  |    9 ++++-
 support/nfs/svc_socket.c  |   83 +++++++++++++++++++++-----------------------
 3 files changed, 48 insertions(+), 45 deletions(-)
support/include/rpcmisc.h +1 -0
index 0b06457..b806227 100644
@@ -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;
support/nfs/svc_create.c +7 -2
index b3f75ed..fd09902 100644
@@ -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)
support/nfs/svc_socket.c +40 -43

102 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#1 lore raw

Hi MJ-

Quoted 6 lines · Mi Jinlong

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.

Quoted 160 lines

---

support/include/rpcmisc.h |    1 +
support/nfs/svc_create.c  |    9 ++++-
support/nfs/svc_socket.c  |   83 +++++++++++++++++++++-----------------------
3 files changed, 48 insertions(+), 45 deletions(-)
support/include/rpcmisc.h +1 -0
index 0b06457..b806227 100644
@@ -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;
support/nfs/svc_create.c +7 -2
index b3f75ed..fd09902 100644
@@ -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)
support/nfs/svc_socket.c +35 -0
index 03a5325..ec406a9 100644
@@ -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);

support/nfs/svc_socket.c +5 -43
-  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)
Signature · 1 line
1.7.4.1
Signature · 2 lines
Chuck Lever
chuck[dot]lever[at]oracle[dot]com
author
#2 lore raw

Chuck Lever:

Quoted 8 lines

Hi MJ-

Quoted 4 lines · Mi Jinlong

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.

Quoted 6 lines
Quoted 2 lines

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.

Signature · 3 lines
----
thanks
Mi Jinlong
Quoted 162 lines
Quoted 161 lines

---

support/include/rpcmisc.h |    1 +
support/nfs/svc_create.c  |    9 ++++-
support/nfs/svc_socket.c  |   83 +++++++++++++++++++++-----------------------
3 files changed, 48 insertions(+), 45 deletions(-)
support/include/rpcmisc.h +1 -0
index 0b06457..b806227 100644
@@ -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;
support/nfs/svc_create.c +7 -2
index b3f75ed..fd09902 100644
@@ -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)
support/nfs/svc_socket.c +35 -0
index 03a5325..ec406a9 100644
@@ -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);

support/nfs/svc_socket.c +5 -43
-  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)
Signature · 1 line
1.7.4.1
#3 lore raw
Quoted 14 lines · Mi Jinlong

Chuck Lever:

Quoted 11 lines

Hi MJ-

Quoted 6 lines · Mi Jinlong

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.

Quoted 7 lines
Quoted 3 lines

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?

Quoted 1 line

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?

Signature · 2 lines
Chuck Lever
chuck[dot]lever[at]oracle[dot]com
author
#4 lore raw

Chuck Lever:

Quoted 14 lines · Mi Jinlong
Quoted 13 lines

Chuck Lever:

Quoted 10 lines

Hi MJ-

Quoted 6 lines · Mi Jinlong

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.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

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.

Quoted 5 lines
Quoted 2 lines

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.

Signature · 3 lines
----
thanks
Mi Jinlong
#5 lore raw
Quoted 26 lines · Mi Jinlong

Chuck Lever:

Quoted 14 lines · Mi Jinlong
Quoted 13 lines

Chuck Lever:

Quoted 10 lines

Hi MJ-

Quoted 6 lines · Mi Jinlong

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.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

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?

Quoted 8 lines

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.

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

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?

Signature · 2 lines
Chuck Lever
chuck[dot]lever[at]oracle[dot]com
author
#6 lore raw

Chuck Lever :

Quoted 26 lines · Mi Jinlong
Quoted 25 lines

Chuck Lever:

Quoted 13 lines · Mi Jinlong
Quoted 12 lines

Chuck Lever:

Quoted 10 lines

Hi MJ-

Quoted 6 lines · Mi Jinlong

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.

Quoted 7 lines
Quoted 6 lines
Quoted 3 lines

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,

Quoted 9 lines
Quoted 6 lines

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?

mountd main()

     --> rpc_init()
      --> svcudp_socket()
       --> svc_socket()
        --> getservbyname_r()       ***
Quoted 4 lines

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.

If building without TI-RPC support, mountd will get ports from /etc/service. With TI-RPC support, mountd will not get ports from /etc/service. That's the problem.

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line

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=).

Got it, thanks!

Quoted 2 lines

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?

I just think the new nfs-utils should do the same thing as older one. Maybe called backward compatibility.

Signature · 3 lines
----
thanks
Mi Jinlong
#7 lore raw
Quoted 6 lines · Mi Jinlong

Chuck Lever :

Quoted 1 line

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?

I just think the new nfs-utils should do the same thing as older one. Maybe called backward compatibility.

I was confused by your original report.

Before, I read that with the commit mentioned above, /etc/services started working, then it stopped at some later point. Now you seem to be saying that /etc/services always worked this way, but with the commit above, it stops working. I've filed a bugzilla to track the issue:

https://bugzilla.linux-nfs.org/show_bug.cgi?id=200

If you can find documentation that describes this feature (ie makes it a formal API, or describes any other details about its operation we should know of), or know of a widely deployed application of mountd or statd that requires it, please do let us know, or modify the bugzilla report.

Signature · 2 lines
Chuck Lever
chuck[dot]lever[at]oracle[dot]com
author
#8 lore raw

PATCH v2 1/1 rpc.mountd: let mountd consult /etc/services for port

In reply to Mi Jinlong

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.

---
 support/include/rpcmisc.h |    1 +
 support/nfs/svc_create.c  |    9 ++++-
 support/nfs/svc_socket.c  |   80 +++++++++++++++++++++------------------------
 utils/mountd/mountd.man   |    5 ++-
 utils/statd/statd.man     |    5 ++-
 5 files changed, 53 insertions(+), 47 deletions(-)
support/include/rpcmisc.h +1 -0
index 0b06457..b806227 100644
@@ -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;
support/nfs/svc_create.c +7 -2
index b3f75ed..c159fc8 100644
@@ -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)
support/nfs/svc_socket.c +37 -43

99 lines not on this page · raw message

utils/mountd/mountd.man +4 -1
index 016a357..b322437 100644
@@ -122,7 +122,10 @@ Ignored (compatibility with unfsd??).
 Specifies the port number used for RPC listener sockets.
 If this option is not specified,
 .B rpc.mountd
-chooses a random ephemeral port for each listener socket.
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
 .IP
 This option can be used to fix the port value of
 .BR rpc.mountd 's
utils/statd/statd.man +4 -1
index b72236c..203f8c9 100644
@@ -219,7 +219,10 @@ for details.
 Specifies the port number used for RPC listener sockets.
 If this option is not specified,
 .B rpc.statd
-chooses a random ephemeral port for each listener socket.
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
 .IP
 This option can be used to fix the port value of its listeners when
 SM_NOTIFY requests must traverse a firewall between clients and servers.
Signature · 1 line
1.7.5.1
#9 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

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.

#10 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

Hi Jim-

Quoted 10 lines · Jim Rees

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.

Quoted 6 lines

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.

Quoted 2 lines

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.

Quoted 4 lines

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.

Quoted 8 lines

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

#11 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

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.

#12 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

Quoted 11 lines · Jim Rees

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.

author
#13 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

Steve Dickson:

Quoted 15 lines · Jim Rees
Quoted 11 lines

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.

Quoted 7 lines

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

#14 lore raw

Re: [PATCH v2] rpc.mountd: let mountd consult /etc/services for port

In reply to Mi Jinlong

Hello Mi,

My apologizes for taking so long on this... It did fall off my radar... but after some unit testing, it appears things
work as expected....

Committed!

steved.

Quoted 193 lines · Mi Jinlong

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.

---
 support/include/rpcmisc.h |    1 +
 support/nfs/svc_create.c  |    9 ++++-
 support/nfs/svc_socket.c  |   80 +++++++++++++++++++++------------------------
 utils/mountd/mountd.man   |    5 ++-
 utils/statd/statd.man     |    5 ++-
 5 files changed, 53 insertions(+), 47 deletions(-)
support/include/rpcmisc.h +1 -0
index 0b06457..b806227 100644
@@ -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;
support/nfs/svc_create.c +7 -2
index b3f75ed..c159fc8 100644
@@ -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)
support/nfs/svc_socket.c +37 -43
index 03a5325..c252f53 100644
@@ -35,14 +35,43 @@
 # 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 +95,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)
-    {
-      /* 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)
+  if (bind(sock, (struct sockaddr *) &addr, len) < 0)
     {
-      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)
utils/mountd/mountd.man +4 -1
index 016a357..b322437 100644
@@ -122,7 +122,10 @@ Ignored (compatibility with unfsd??).
 Specifies the port number used for RPC listener sockets.
 If this option is not specified,
 .B rpc.mountd
-chooses a random ephemeral port for each listener socket.
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
 .IP
 This option can be used to fix the port value of
 .BR rpc.mountd 's
utils/statd/statd.man +4 -1
index b72236c..203f8c9 100644
@@ -219,7 +219,10 @@ for details.
 Specifies the port number used for RPC listener sockets.
 If this option is not specified,
 .B rpc.statd
-chooses a random ephemeral port for each listener socket.
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
 .IP
 This option can be used to fix the port value of its listeners when
 SM_NOTIFY requests must traverse a firewall between clients and servers.