+++ /dev/null
-commit 0e94118815e8ff9c1142117764ee3e6cddba0395
-Author: Chuck Lever <chuck.lever@oracle.com>
-Date: Fri Oct 1 15:04:20 2010 -0400
-
- libnfs.a: Allow multiple RPC listeners to share listener port number
-
- Normally, when "-p" is not specified on the mountd command line, the
- TI-RPC library chooses random port numbers for each listener. If a
- port number _is_ specified on the command line, all the listeners
- will get the same port number, so SO_REUSEADDR needs to be set on
- each socket.
-
- Thus we can't let TI-RPC create the listener sockets for us in this
- case; we must create them ourselves and then set SO_REUSEADDR (and
- other socket options) by hand.
-
- Different versions of the same RPC program have to share the same
- listener and SVCXPRT, so we have to cache xprts we create, and re-use
- them when additional requests for registration come from the
- application.
-
- Though it doesn't look like it, this fix was "copied" from the legacy
- rpc_init() function. It's more complicated for TI-RPC, of course,
- since you can have an arbitrary number of listeners, not just two
- (one for AF_INET UDP and one for AF_INET TCP).
-
- Fix for:
-
- https://bugzilla.linux-nfs.org/show_bug.cgi?id=190
-
- There have been no reports of problems with specifying statd's
- listener port, but I expect this is a problem for statd too.
-
- Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
-
-diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
-index 59ba505..fdc4846 100644
---- a/support/nfs/svc_create.c
-+++ b/support/nfs/svc_create.c
-@@ -27,6 +27,7 @@
- #include <memory.h>
- #include <signal.h>
- #include <unistd.h>
-+#include <errno.h>
- #include <netdb.h>
-
- #include <netinet/in.h>
-@@ -41,11 +42,68 @@
- #include "tcpwrapper.h"
- #endif
-
-+#include "sockaddr.h"
- #include "rpcmisc.h"
- #include "xlog.h"
-
- #ifdef HAVE_LIBTIRPC
-
-+#define SVC_CREATE_XPRT_CACHE_SIZE (8)
-+static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
-+
-+/*
-+ * Cache an SVC xprt, in case there are more programs or versions to
-+ * register against it.
-+ */
-+static void
-+svc_create_cache_xprt(SVCXPRT *xprt)
-+{
-+ unsigned int i;
-+
-+ /* Check if we've already got this one... */
-+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
-+ if (svc_create_xprt_cache[i] == xprt)
-+ return;
-+
-+ /* No, we don't. Cache it. */
-+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
-+ if (svc_create_xprt_cache[i] == NULL) {
-+ svc_create_xprt_cache[i] = xprt;
-+ return;
-+ }
-+
-+ xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
-+}
-+
-+/*
-+ * Find a previously cached SVC xprt structure with the given bind address
-+ * and transport semantics.
-+ *
-+ * Returns pointer to a SVC xprt.
-+ *
-+ * If no matching SVC XPRT can be found, NULL is returned.
-+ */
-+static SVCXPRT *
-+svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
-+{
-+ unsigned int i;
-+
-+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
-+ SVCXPRT *xprt = svc_create_xprt_cache[i];
-+ struct sockaddr *sap;
-+
-+ if (xprt == NULL)
-+ continue;
-+ if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
-+ continue;
-+ sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
-+ if (!nfs_compare_sockaddr(bindaddr, sap))
-+ continue;
-+ return xprt;
-+ }
-+ return NULL;
-+}
-+
- /*
- * Set up an appropriate bind address, given @port and @nconf.
- *
-@@ -98,17 +156,112 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
- return ai;
- }
-
-+/*
-+ * Create a listener socket on a specific bindaddr, and set
-+ * special socket options to allow it to share the same port
-+ * as other listeners.
-+ *
-+ * Returns an open, bound, and possibly listening network
-+ * socket on success.
-+ *
-+ * Otherwise returns -1 if some error occurs.
-+ */
-+static int
-+svc_create_sock(const struct sockaddr *sap, socklen_t salen,
-+ struct netconfig *nconf)
-+{
-+ int fd, type, protocol;
-+ int one = 1;
-+
-+ switch(nconf->nc_semantics) {
-+ case NC_TPI_CLTS:
-+ type = SOCK_DGRAM;
-+ break;
-+ case NC_TPI_COTS_ORD:
-+ type = SOCK_STREAM;
-+ break;
-+ default:
-+ xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
-+ __func__, nconf->nc_semantics);
-+ return -1;
-+ }
-+
-+ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
-+ protocol = (int)IPPROTO_UDP;
-+ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
-+ protocol = (int)IPPROTO_TCP;
-+ else {
-+ xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
-+ __func__, nconf->nc_proto);
-+ return -1;
-+ }
-+
-+ fd = socket((int)sap->sa_family, type, protocol);
-+ if (fd == -1) {
-+ xlog(L_ERROR, "Could not make a socket: (%d) %m",
-+ errno);
-+ return -1;
-+ }
-+
-+#ifdef IPV6_SUPPORTED
-+ if (sap->sa_family == AF_INET6) {
-+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
-+ &one, sizeof(one)) == -1) {
-+ xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
-+ errno);
-+ (void)close(fd);
-+ return -1;
-+ }
-+ }
-+#endif /* IPV6_SUPPORTED */
-+
-+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
-+ &one, sizeof(one)) == -1) {
-+ xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
-+ errno);
-+ (void)close(fd);
-+ return -1;
-+ }
-+
-+ if (bind(fd, sap, salen) == -1) {
-+ xlog(L_ERROR, "Could not bind socket: (%d) %m",
-+ errno);
-+ (void)close(fd);
-+ return -1;
-+ }
-+
-+ if (nconf->nc_semantics == NC_TPI_COTS_ORD)
-+ if (listen(fd, SOMAXCONN) == -1) {
-+ xlog(L_ERROR, "Could not listen on socket: (%d) %m",
-+ errno);
-+ (void)close(fd);
-+ return -1;
-+ }
-+
-+ return fd;
-+}
-+
-+/*
-+ * The simple case is allowing the TI-RPC library to create a
-+ * transport itself, given just the bind address and transport
-+ * semantics.
-+ *
-+ * The port is chosen at random by the library; we don't know
-+ * what it is. So the new xprt cannot be cached here.
-+ *
-+ * Returns the count of started listeners (one or zero).
-+ */
- static unsigned int
--svc_create_nconf(const char *name, const rpcprog_t program,
-+svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
- const rpcvers_t version,
- void (*dispatch)(struct svc_req *, SVCXPRT *),
-- const uint16_t port, struct netconfig *nconf)
-+ struct netconfig *nconf)
- {
- struct t_bind bindaddr;
- struct addrinfo *ai;
- SVCXPRT *xprt;
-
-- ai = svc_create_bindaddr(nconf, port);
-+ ai = svc_create_bindaddr(nconf, 0);
- if (ai == NULL)
- return 0;
-
-@@ -119,7 +272,7 @@ svc_create_nconf(const char *name, const rpcprog_t program,
- freeaddrinfo(ai);
- if (xprt == NULL) {
- xlog(D_GENERAL, "Failed to create listener xprt "
-- "(%s, %u, %s)", name, version, nconf->nc_netid);
-+ "(%s, %u, %s)", name, version, nconf->nc_netid);
- return 0;
- }
-
-@@ -133,6 +286,81 @@ svc_create_nconf(const char *name, const rpcprog_t program,
- return 1;
- }
-
-+/*
-+ * If a port is specified on the command line, that port value will be
-+ * the same for all listeners created here. Create each listener socket
-+ * in advance and set SO_REUSEADDR, rather than allowing the RPC library
-+ * to create the listeners for us on a randomly chosen port (RPC_ANYFD).
-+ *
-+ * Also, to support multiple RPC versions on the same listener, register
-+ * any new versions on the same transport that is already handling other
-+ * versions on the same bindaddr and transport. To accomplish this,
-+ * cache previously created xprts on a list, and check that list before
-+ * creating a new socket for this [program, version].
-+ *
-+ * Returns the count of started listeners (one or zero).
-+ */
-+static unsigned int
-+svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
-+ const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port, struct netconfig *nconf)
-+{
-+ struct addrinfo *ai;
-+ SVCXPRT *xprt;
-+
-+ ai = svc_create_bindaddr(nconf, port);
-+ if (ai == NULL)
-+ return 0;
-+
-+ xprt = svc_create_find_xprt(ai->ai_addr, nconf);
-+ if (xprt == NULL) {
-+ int fd;
-+
-+ fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
-+ if (fd == -1)
-+ goto out_free;
-+
-+ xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
-+ if (xprt == NULL) {
-+ xlog(D_GENERAL, "Failed to create listener xprt "
-+ "(%s, %u, %s)", name, version, nconf->nc_netid);
-+ (void)close(fd);
-+ goto out_free;
-+ }
-+ }
-+
-+ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
-+ /* svc_reg(3) destroys @xprt in this case */
-+ xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
-+ name, version, nconf->nc_netid);
-+ goto out_free;
-+ }
-+
-+ svc_create_cache_xprt(xprt);
-+
-+ freeaddrinfo(ai);
-+ return 1;
-+
-+out_free:
-+ freeaddrinfo(ai);
-+ return 0;
-+}
-+
-+static unsigned int
-+svc_create_nconf(const char *name, const rpcprog_t program,
-+ const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port, struct netconfig *nconf)
-+{
-+ if (port != 0)
-+ return svc_create_nconf_fixed_port(name, program,
-+ version, dispatch, port, nconf);
-+
-+ return svc_create_nconf_rand_port(name, program,
-+ version, dispatch, nconf);
-+}
-+
- /**
- * nfs_svc_create - start up RPC svc listeners
- * @name: C string containing name of new service