Allow rpc.nfsd to suppress tcp or udp, and listen on a specific address.
authorSteve Dickson <SteveD@redhat.com>
Sun, 2 Jul 2006 23:52:00 +0000 (09:52 +1000)
committerNeil Brown <neilb@suse.de>
Sun, 2 Jul 2006 23:52:00 +0000 (09:52 +1000)
 -T       - will suppressing listening for TCP connection.
 -U       - will suppress UDP
 -H host  - will only listen on that local address
 -p port  - will listen on that port.

This requires kernel patches which will hopefully be in 2.6.19 and possibly some
earlier test and vendor kernels.

support/include/nfs/nfs.h
support/include/nfslib.h
support/nfs/nfssvc.c
utils/nfsd/nfsd.c
utils/nfsd/nfsd.man

index 2cf6857..f0286b2 100644 (file)
@@ -44,8 +44,12 @@ struct nfs_fh_old {
 #define LOCKDCTL_SVC           NFSCTL_LOCKD
 
 #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) 
 #define LOCKDCTL_SVC           NFSCTL_LOCKD
 
 #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) 
+#define NFSCTL_UDPUNSET(_cltbits)     ((_cltbits) &= ~(1 << (17 - 1))) 
+#define NFSCTL_TCPUNSET(_cltbits)     ((_cltbits) &= ~(1 << (18 - 1))) 
 
 #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) 
 
 #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) 
+#define NFSCTL_UDPISSET(_cltbits)     ((_cltbits) & (1 << (17 - 1))) 
+#define NFSCTL_TCPISSET(_cltbits)     ((_cltbits) & (1 << (18 - 1))) 
 
 #define NFSCTL_ALLBITS (~0)
 
 
 #define NFSCTL_ALLBITS (~0)
 
index 50892e2..3e25761 100644 (file)
@@ -120,7 +120,7 @@ int                 wildmat(char *text, char *pattern);
  * nfsd library functions.
  */
 int                    nfsctl(int, struct nfsctl_arg *, union nfsctl_res *);
  * nfsd library functions.
  */
 int                    nfsctl(int, struct nfsctl_arg *, union nfsctl_res *);
-int                    nfssvc(int port, int nrservs, unsigned int versbits);
+int                    nfssvc(int port, int nrservs, unsigned int versbits, unsigned int portbits, char *haddr);
 int                    nfsaddclient(struct nfsctl_client *clp);
 int                    nfsdelclient(struct nfsctl_client *clp);
 int                    nfsexport(struct nfsctl_export *exp);
 int                    nfsaddclient(struct nfsctl_client *clp);
 int                    nfsdelclient(struct nfsctl_client *clp);
 int                    nfsexport(struct nfsctl_export *exp);
index c51ace1..a6ea410 100644 (file)
 #include <config.h>
 #endif
 
 #include <config.h>
 #endif
 
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <syslog.h>
 
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <syslog.h>
 
+
 #include "nfslib.h"
 
 #include "nfslib.h"
 
+#define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
+#define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
+#define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
+
+static void
+nfssvc_setfds(int port, unsigned int ctlbits, char *haddr)
+{
+       int fd, on=1;
+       char buf[BUFSIZ];
+       int udpfd = -1, tcpfd = -1;
+       struct sockaddr_in sin;
+
+       fd = open(NFSD_PORTS_FILE, O_WRONLY);
+       if (fd < 0)
+               return;
+       sin.sin_family = AF_INET;
+       sin.sin_port   = htons(port);
+       sin.sin_addr.s_addr =  inet_addr(haddr);
+
+       if (NFSCTL_UDPISSET(ctlbits)) {
+               udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+               if (udpfd < 0) {
+                       syslog(LOG_ERR, "nfssvc: unable to create UPD socket: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+               if (bind(udpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
+                       syslog(LOG_ERR, "nfssvc: unable to bind UPD socket: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+       }
+
+       if (NFSCTL_TCPISSET(ctlbits)) {
+               tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+               if (tcpfd < 0) {
+                       syslog(LOG_ERR, "nfssvc: unable to createt tcp socket: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+               if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+                       syslog(LOG_ERR, "nfssvc: unable to set SO_REUSEADDR: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+               if (bind(tcpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
+                       syslog(LOG_ERR, "nfssvc: unable to bind TCP socket: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+               if (listen(tcpfd, 64) < 0){
+                       syslog(LOG_ERR, "nfssvc: unable to create listening socket: "
+                               "errno %d (%s)\n", errno, strerror(errno));
+                       exit(1);
+               }
+       }
+       if (udpfd >= 0) {
+               snprintf(buf, BUFSIZ,"%d\n", udpfd); 
+               if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+                       syslog(LOG_ERR, 
+                              "nfssvc: writting fds to kernel failed: errno %d (%s)", 
+                              errno, strerror(errno));
+               }
+               close(fd);
+               fd = -1;
+       }
+       if (tcpfd >= 0) {
+               if (fd < 0)
+                       fd = open(NFSD_PORTS_FILE, O_WRONLY);
+               snprintf(buf, BUFSIZ,"%d\n", tcpfd); 
+               if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+                       syslog(LOG_ERR, 
+                              "nfssvc: writting fds to kernel failed: errno %d (%s)", 
+                              errno, strerror(errno));
+               }
+       }
+       close(fd);
+
+       return;
+}
 static void
 nfssvc_versbits(unsigned int ctlbits)
 {
 static void
 nfssvc_versbits(unsigned int ctlbits)
 {
@@ -25,7 +109,7 @@ nfssvc_versbits(unsigned int ctlbits)
 
        ptr = buf;
        off = 0;
 
        ptr = buf;
        off = 0;
-       fd = open("/proc/fs/nfsd/versions", O_WRONLY);
+       fd = open(NFSD_VERS_FILE, O_WRONLY);
        if (fd < 0)
                return;
 
        if (fd < 0)
                return;
 
@@ -45,14 +129,17 @@ nfssvc_versbits(unsigned int ctlbits)
        return;
 }
 int
        return;
 }
 int
-nfssvc(int port, int nrservs, unsigned int versbits)
+nfssvc(int port, int nrservs, unsigned int versbits, unsigned protobits,
+       char *haddr)
 {
        struct nfsctl_arg       arg;
        int fd;
 
 {
        struct nfsctl_arg       arg;
        int fd;
 
+       nfssvc_setfds(port, protobits, haddr);
+
        nfssvc_versbits(versbits);
 
        nfssvc_versbits(versbits);
 
-       fd = open("/proc/fs/nfsd/threads", O_WRONLY);
+       fd = open(NFSD_THREAD_FILE, O_WRONLY);
        if (fd < 0)
                fd = open("/proc/fs/nfs/threads", O_WRONLY);
        if (fd >= 0) {
        if (fd < 0)
                fd = open("/proc/fs/nfs/threads", O_WRONLY);
        if (fd >= 0) {
index fa6ee71..d0bbfb3 100644 (file)
 #include <getopt.h>
 #include <syslog.h>
 #include <netdb.h>
 #include <getopt.h>
 #include <syslog.h>
 #include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include "nfslib.h"
 
 static void    usage(const char *);
 
 static struct option longopts[] =
 {
 #include "nfslib.h"
 
 static void    usage(const char *);
 
 static struct option longopts[] =
 {
+       { "host", 1, 0, 'H' },
        { "help", 0, 0, 'h' },
        { "no-nfs-version", 1, 0, 'N' },
        { "help", 0, 0, 'h' },
        { "no-nfs-version", 1, 0, 'N' },
+       { "no-tcp", 0, 0, 'T' },
+       { "no-udp", 0, 0, 'U' },
+       { "port", 1, 0, 'P' },
+       { "port", 1, 0, 'p' },
        { NULL, 0, 0, 0 }
 };
        { NULL, 0, 0, 0 }
 };
+unsigned int protobits = NFSCTL_ALLBITS;
 unsigned int versbits = NFSCTL_ALLBITS;
 unsigned int versbits = NFSCTL_ALLBITS;
+char *haddr = NULL;
 
 int
 main(int argc, char **argv)
 {
        int     count = 1, c, error, port, fd, found_one;
        struct servent *ent;
 
 int
 main(int argc, char **argv)
 {
        int     count = 1, c, error, port, fd, found_one;
        struct servent *ent;
+       struct hostent *hp;
 
        ent = getservbyname ("nfs", "udp");
        if (ent != NULL)
 
        ent = getservbyname ("nfs", "udp");
        if (ent != NULL)
@@ -44,8 +56,19 @@ main(int argc, char **argv)
        else
                port = 2049;
 
        else
                port = 2049;
 
-       while ((c = getopt_long(argc, argv, "hN:p:P:", longopts, NULL)) != EOF) {
+       while ((c = getopt_long(argc, argv, "H:hN:p:P:TU", longopts, NULL)) != EOF) {
                switch(c) {
                switch(c) {
+               case 'H':
+                       if (inet_addr(optarg) != INADDR_NONE) {
+                               haddr = strdup(optarg);
+                       } else if ((hp = gethostbyname(optarg)) != NULL) {
+                               haddr = inet_ntoa((*(struct in_addr*)(hp->h_addr_list[0])));
+                       } else {
+                               fprintf(stderr, "%s: Unknown hostname: %s\n",
+                                       argv[0], optarg);
+                               usage(argv [0]);
+                       }
+                       break;
                case 'P':       /* XXX for nfs-server compatibility */
                case 'p':
                        port = atoi(optarg);
                case 'P':       /* XXX for nfs-server compatibility */
                case 'p':
                        port = atoi(optarg);
@@ -67,6 +90,12 @@ main(int argc, char **argv)
                                exit(1);
                        }
                        break;
                                exit(1);
                        }
                        break;
+               case 'T':
+                               NFSCTL_TCPUNSET(protobits);
+                               break;
+               case 'U':
+                               NFSCTL_UDPUNSET(protobits);
+                               break;
                default:
                        fprintf(stderr, "Invalid argument: '%c'\n", c);
                case 'h':
                default:
                        fprintf(stderr, "Invalid argument: '%c'\n", c);
                case 'h':
@@ -76,6 +105,10 @@ main(int argc, char **argv)
        /*
         * Do some sanity checking, if the ctlbits are set
         */
        /*
         * Do some sanity checking, if the ctlbits are set
         */
+       if (!NFSCTL_UDPISSET(protobits) && !NFSCTL_TCPISSET(protobits)) {
+               fprintf(stderr, "invalid protocol specified\n");
+               exit(1);
+       }
        found_one = 0;
        for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) {
                if (NFSCTL_VERISSET(versbits, c))
        found_one = 0;
        for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) {
                if (NFSCTL_VERISSET(versbits, c))
@@ -85,6 +118,14 @@ main(int argc, char **argv)
                fprintf(stderr, "no version specified\n");
                exit(1);
        }                       
                fprintf(stderr, "no version specified\n");
                exit(1);
        }                       
+       if (NFSCTL_VERISSET(versbits, 4) && !NFSCTL_TCPISSET(versbits)) {
+               fprintf(stderr, "version 4 requires the TCP protocol\n");
+               exit(1);
+       }
+       if (haddr == NULL) {
+               struct in_addr in = {INADDR_ANY}; 
+               haddr = strdup(inet_ntoa(in));
+       }
 
        if (chdir(NFS_STATEDIR)) {
                fprintf(stderr, "%s: chdir(%s) failed: %s\n",
 
        if (chdir(NFS_STATEDIR)) {
                fprintf(stderr, "%s: chdir(%s) failed: %s\n",
@@ -116,7 +157,7 @@ main(int argc, char **argv)
        closeall(3);
 
        openlog("nfsd", LOG_PID, LOG_DAEMON);
        closeall(3);
 
        openlog("nfsd", LOG_PID, LOG_DAEMON);
-       if ((error = nfssvc(port, count, versbits)) < 0) {
+       if ((error = nfssvc(port, count, versbits, protobits, haddr)) < 0) {
                int e = errno;
                syslog(LOG_ERR, "nfssvc: %s", strerror(e));
                closelog();
                int e = errno;
                syslog(LOG_ERR, "nfssvc: %s", strerror(e));
                closelog();
@@ -129,7 +170,7 @@ static void
 usage(const char *prog)
 {
        fprintf(stderr, "Usage:\n"
 usage(const char *prog)
 {
        fprintf(stderr, "Usage:\n"
-               "%s [-p|-P|--port port] [-N|--no-nfs-version version ] nrservs\n", 
+               "%s [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-T|--no-tcp] [-U|--no-udp] nrservs\n", 
                prog);
        exit(2);
 }
                prog);
        exit(2);
 }
index d175d11..4ac709c 100644 (file)
@@ -22,6 +22,17 @@ server provides an ancillary service needed to satisfy mount requests
 by NFS clients.
 .SH OPTIONS
 .TP
 by NFS clients.
 .SH OPTIONS
 .TP
+.B \-H " or " \-\-host  hostname
+specify a particular hostname (or address) that NFS requests will
+be accepted on. By default,
+.B rpc.nfsd
+will accept NFS requests on all known network addresses.
+Note that
+.B lockd
+(which performs file locking services for NFS) may still accept
+request on all known network addresses.  This may change in future
+releases of the Linux Kernel.
+.TP
 .B \-p " or " \-\-port  port
 specify a diferent port to listen on for NFS requests. By default,
 .B rpc.nfsd
 .B \-p " or " \-\-port  port
 specify a diferent port to listen on for NFS requests. By default,
 .B rpc.nfsd
@@ -34,6 +45,16 @@ does not offer certain versions of NFS. The current version of
 .B rpc.nfsd
 can support both NFS version 2,3 and the newer version 4.
 .TP
 .B rpc.nfsd
 can support both NFS version 2,3 and the newer version 4.
 .TP
+.B \-T " or " \-\-no-tcp
+Disable 
+.B rpc.nfsd 
+from accepting TCP connections from clients.
+.TP
+.B \-U " or " \-\-no-udp
+Disable
+.B rpc.nfsd
+from accepting UDP connections from clients.
+.TP
 .I nproc
 specify the number of NFS server threads. By default, just one
 thread is started. However, for optimum performance several threads
 .I nproc
 specify the number of NFS server threads. By default, just one
 thread is started. However, for optimum performance several threads