Print this page
7577 mountd support to run on a fixed port
Portions contributed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sebastien Roy <sebastien.roy@delphix.com>

@@ -18,12 +18,13 @@
  *
  * CDDL HEADER END
  */
 
 /*
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
 /*        All Rights Reserved   */
 

@@ -115,14 +116,16 @@
 static void umount(struct svc_req *);
 static void umountall(struct svc_req *);
 static int newopts(char *);
 static tsol_tpent_t *get_client_template(struct sockaddr *);
 
+static int debug;
 static int verbose;
 static int rejecting;
 static int mount_vers_min = MOUNTVERS;
 static int mount_vers_max = MOUNTVERS3;
+static int mountd_port = 0;
 
 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
 
 thread_t        nfsauth_thread;
 thread_t        cmd_thread;

@@ -360,10 +363,86 @@
 
         *val = (int)lval;
         return (0);
 }
 
+/*
+ * This function is called for each configured network type to
+ * bind and register our RPC service programs.
+ *
+ * On TCP or UDP, we may want to bind MOUNTPROG on a specific port
+ * (when mountd_port is specified) in which case we'll use the
+ * variant of svc_tp_create() that lets us pass a bind address.
+ */
+static void
+md_svc_tp_create(struct netconfig *nconf)
+{
+        char port_str[8];
+        struct nd_hostserv hs;
+        struct nd_addrlist *al = NULL;
+        SVCXPRT *xprt = NULL;
+        rpcvers_t vers;
+
+        vers = mount_vers_max;
+
+        /*
+         * If mountd_port is set and this is an inet transport,
+         * bind this service on the specified port.  The TLI way
+         * to create such a bind address is netdir_getbyname()
+         * with the special "host" HOST_SELF_BIND.  This builds
+         * an all-zeros IP address with the specified port.
+         */
+        if (mountd_port != 0 &&
+            (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
+            strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
+                int err;
+
+                snprintf(port_str, sizeof (port_str), "%u",
+                    (unsigned short)mountd_port);
+
+                hs.h_host = HOST_SELF_BIND;
+                hs.h_serv = port_str;
+                err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
+                if (err == 0 && al != NULL) {
+                        xprt = svc_tp_create_addr(mnt, MOUNTPROG, vers,
+                            nconf, al->n_addrs);
+                        netdir_free(al, ND_ADDRLIST);
+                }
+                if (xprt == NULL) {
+                        syslog(LOG_ERR, "mountd: unable to create "
+                            "(MOUNTD,%d) on transport %s (port %d)",
+                            vers, nconf->nc_netid, mountd_port);
+                }
+                /* fall-back to default bind */
+        }
+        if (xprt == NULL) {
+                /*
+                 * Had mountd_port=0, or non-inet transport,
+                 * or the bind to a specific port failed.
+                 * Do a default bind.
+                 */
+                xprt = svc_tp_create(mnt, MOUNTPROG, vers, nconf);
+        }
+        if (xprt == NULL) {
+                syslog(LOG_ERR, "mountd: unable to create "
+                    "(MOUNTD,%d) on transport %s",
+                    vers, nconf->nc_netid);
+                return;
+        }
+
+        /*
+         * Register additional versions on this transport.
+         */
+        while (--vers >= mount_vers_min) {
+                if (!svc_reg(xprt, MOUNTPROG, vers, mnt, nconf)) {
+                        (void) syslog(LOG_ERR, "mountd: "
+                            "failed to register vers %d on %s",
+                            vers, nconf->nc_netid);
+                }
+        }
+}
+
 int
 main(int argc, char *argv[])
 {
         int     pid;
         int     c;

@@ -377,10 +456,12 @@
         int defvers, ret, bufsz;
         struct rlimit rl;
         int listen_backlog = 0;
         int max_threads = 0;
         int tmp;
+        struct netconfig *nconf;
+        NCONF_HANDLE *nc;
 
         int     pipe_fd = -1;
 
         /*
          * Mountd requires uid 0 for:

@@ -396,10 +477,11 @@
          *              MLP
          */
         can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
         if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
             PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
+            PRIV_NET_PRIVADDR,
             can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
                 (void) fprintf(stderr,
                     "%s: must be run with sufficient privileges\n",
                     argv[0]);
                 exit(1);

@@ -420,12 +502,22 @@
         if (ret != SA_OK) {
                 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
                     "failed, using default value");
         }
 
-        while ((c = getopt(argc, argv, "vrm:")) != EOF) {
+        ret = nfs_smf_get_iprop("mountd_port", &mountd_port,
+            DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
+        if (ret != SA_OK) {
+                syslog(LOG_ERR, "Reading of mountd_port from SMF "
+                    "failed, using default value");
+        }
+
+        while ((c = getopt(argc, argv, "dvrm:p:")) != EOF) {
                 switch (c) {
+                case 'd':
+                        debug++;
+                        break;
                 case 'v':
                         verbose++;
                         break;
                 case 'r':
                         rejecting = 1;

@@ -437,10 +529,19 @@
                                     argv[0]);
                                 break;
                         }
                         max_threads = tmp;
                         break;
+                case 'p':
+                        if (convert_int(&tmp, optarg) != 0 || tmp < 1 ||
+                            tmp > UINT16_MAX) {
+                                (void) fprintf(stderr, "%s: invalid port "
+                                    "number\n", argv[0]);
+                                break;
+                        }
+                        mountd_port = tmp;
+                        break;
                 default:
                         fprintf(stderr, "usage: mountd [-v] [-r]\n");
                         exit(1);
                 }
         }

@@ -486,10 +587,12 @@
         /*
          * Sanity check versions,
          * even though we may get versions > MOUNTVERS3, we still need
          * to start nfsauth service, so continue on regardless of values.
          */
+        if (mount_vers_max > MOUNTVERS3)
+                mount_vers_max = MOUNTVERS3;
         if (mount_vers_min > mount_vers_max) {
                 fprintf(stderr, "server_versmin > server_versmax\n");
                 mount_vers_max = mount_vers_min;
         }
         (void) setlocale(LC_ALL, "");

@@ -506,18 +609,20 @@
         (void) textdomain(TEXT_DOMAIN);
 
         /* Don't drop core if the NFS module isn't loaded. */
         (void) signal(SIGSYS, SIG_IGN);
 
+        if (!debug)
         pipe_fd = daemonize_init();
 
         /*
          * If we coredump it'll be in /core
          */
         if (chdir("/") < 0)
                 fprintf(stderr, "chdir /: %s\n", strerror(errno));
 
+        if (!debug)
         openlog("mountd", LOG_PID, LOG_DAEMON);
 
         /*
          * establish our lock on the lock file and write our pid to it.
          * exit if some other process holds the lock, or if there's any

@@ -599,10 +704,15 @@
         if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
                 fprintf(stderr, "unable to set max_threads\n");
                 exit(1);
         }
 
+        if (mountd_port < 0 || mountd_port > UINT16_MAX) {
+                fprintf(stderr, "unable to use specified port\n");
+                exit(1);
+        }
+
         /*
          * Make sure to unregister any previous versions in case the
          * user is reconfiguring the server in interesting ways.
          */
         svc_unreg(MOUNTPROG, MOUNTVERS);

@@ -643,53 +753,32 @@
                 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
                 exit(2);
         }
 
         /*
-         * Create datagram and connection oriented services
+         * Enumerate network transports and create service listeners
+         * as appropriate for each.
          */
-        if (mount_vers_max >= MOUNTVERS) {
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register datagram_v MOUNTVERS\n");
-                        exit(1);
+        if ((nc = setnetconfig()) == NULL) {
+                syslog(LOG_ERR, "setnetconfig failed: %m");
+                return (-1);
                 }
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register circuit_v MOUNTVERS\n");
-                        exit(1);
-                }
-        }
+        while ((nconf = getnetconfig(nc)) != NULL) {
+                /*
+                 * Skip things like tpi_raw, invisible...
+                 */
+                if ((nconf->nc_flag & NC_VISIBLE) == 0)
+                        continue;
+                if (nconf->nc_semantics != NC_TPI_CLTS &&
+                    nconf->nc_semantics != NC_TPI_COTS &&
+                    nconf->nc_semantics != NC_TPI_COTS_ORD)
+                        continue;
 
-        if (mount_vers_max >= MOUNTVERS_POSIX) {
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
-                    "datagram_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register datagram_v MOUNTVERS_POSIX\n");
-                        exit(1);
+                md_svc_tp_create(nconf);
                 }
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
-                    "circuit_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register circuit_v MOUNTVERS_POSIX\n");
-                        exit(1);
-                }
-        }
+        (void) endnetconfig(nc);
 
-        if (mount_vers_max >= MOUNTVERS3) {
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register datagram_v MOUNTVERS3\n");
-                        exit(1);
-                }
-                if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
-                        fprintf(stderr,
-                            "couldn't register circuit_v MOUNTVERS3\n");
-                        exit(1);
-                }
-        }
-
         /*
          * Start serving
          */
         rmtab_load();