auth: Allow subnet ranges in NetInfo and NetRestrict 13/11313/7
authorChas Williams (CONTRACTOR) <chas@cmf.nrl.navy.mil>
Mon, 7 Jul 2014 13:55:44 +0000 (09:55 -0400)
committerBenjamin Kaduk <kaduk@mit.edu>
Fri, 6 May 2016 02:50:23 +0000 (22:50 -0400)
Add the ability to specify a range of addresses in both NetInfo and
NetRestrict.

Change-Id: Iecdcca8587aa2e6e7cd56cbbebb63eb41b5d6f40
Reviewed-on: https://gerrit.openafs.org/11313
Reviewed-by: Daria Phoebe Brashear <shadow@your-file-system.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>

doc/man-pages/pod5/NetInfo.pod
doc/man-pages/pod5/NetRestrict.pod
doc/xml/AdminGuide/auagd008.xml
doc/xml/AdminGuide/auagd015.xml
src/auth/netrestrict.c
src/auth/test/Makefile.in
src/auth/test/testnetrestrict.c [new file with mode: 0644]

index 58e9ae9..1d78907 100644 (file)
@@ -30,10 +30,11 @@ File Server initiates RPCs: when it breaks callbacks and when it pings the
 client machine to verify that the Cache Manager is still accessible.
 
 The F<NetInfo> file is in ASCII format. One of the machine's IP addresses
-appears on each line, in dotted decimal format. The File Server initially
-uses the address that appears first in the list. The order of the
-remaining addresses is not significant: if an RPC to the first interface
-fails, the File Server simultaneously sends RPCs to all of the other
+appears on each line, in dotted decimal format.  To match a network instead
+of an individual address, use a slash (C</>) followed a subnet length.  The
+File Server initially uses the address that matches first in the list. The
+order of the remaining addresses is not significant: if an RPC to the first
+interface fails, the File Server simultaneously sends RPCs to all of the other
 interfaces in the list.  Whichever interface replies first is the one to
 which the File Server then sends pings and RPCs to break callbacks.
 
@@ -77,8 +78,9 @@ to use for communication with the peer processes on other database
 machines in the cell.
 
 The F<NetInfo> file is in ASCII format. One of the machine's IP addresses
-appears on each line, in dotted decimal format. The order of the addresses
-is not significant.
+appears on each line, in dotted decimal format.  To match a network
+instead of an individual address, use a slash (C</>) followed a subnet
+length.  The order of the addresses is not significant.
 
 Optionally, the File Server can be forced to use an IP address that does
 not belong to one of the server interfaces. To do this, add a line to the
index 471b025..f5d76d9 100644 (file)
@@ -14,9 +14,9 @@ to talk to other database servers.
 =head2 FORMAT
 
 The F<NetRestrict> file is in ASCII format. One IP address appears on each
-line, in dotted decimal format. The order of the addresses is not
-significant. There is currently no mechanism to specify a range of
-addresses or a wildcard; each IP address must be listed individually.
+line, in dotted decimal format.  To specify a network instead, use a
+slash (C</>) followed by a subnet length.  The order of the addresses is
+not significant.
 
 =head2 Client NetRestrict
 
@@ -70,6 +70,19 @@ the peer processes on other database machines in the cell.
 To display the File Server interface addresses registered in the VLDB, use
 the B<vos listaddrs> command.
 
+=head1 EXAMPLES
+
+If the File Server should not use the IP address 192.168.1.1 on one of
+its private interfaces, then the F<NetRestrict> file should contain
+the following:
+
+   196.168.1.1
+
+In order to prevent the usage of any 192.168/16 addresses on its local
+interfaces, the F<NetRestrict> file should contain:
+
+   196.168.0.0/16
+
 =head1 SEE ALSO
 
 L<NetInfo(5)>,
index 2f490e3..9e4f119 100644 (file)
 
         <listitem>
           <para>Using a text editor, open the <emphasis role="bold">/usr/afs/local/NetRestrict</emphasis> file. Place one IP address
-          in dotted decimal format on each line. The order of the addresses is not significant. Use the value <emphasis
-          role="bold">255</emphasis> as a wildcard that represents all possible addresses in that field. For example, the entry
-          <computeroutput>192.12.105.255</computeroutput> indicates that the Cache Manager does not register any of the addresses in
+          in dotted decimal format on each line. The order of the addresses is not significant. Use a slash (<emphasis
+          role="bold">/</emphasis>) followed by a subnet length to represent all possible addresses in a range. For example, the entry
+          <computeroutput>192.12.105.0/24</computeroutput> indicates that the Cache Manager does not register any of the addresses in
           the 192.12.105 subnet.</para>
         </listitem>
 
index 4ffe0cc..47a51be 100644 (file)
 
         <listitem>
           <para>Using a text editor, open the <emphasis role="bold">/usr/vice/etc/NetRestrict</emphasis> file. Place one IP address
-          in dotted decimal format on each line. The order of the addresses is not significant. Use the value <emphasis
-          role="bold">255</emphasis> as a wildcard that represents all possible addresses in that field. For example, the entry
-          <computeroutput>192.12.105.255</computeroutput> indicates that the Cache Manager does not register any of the addresses in
+          in dotted decimal format on each line. The order of the addresses is not significant. Use a slash (<emphasis
+          role="bold">/</emphasis>) followed by a subnet length to represent all possible addresses in a range. For example, the entry
+          <computeroutput>192.12.105.0/24</computeroutput> indicates that the Cache Manager does not register any of the addresses in
           the 192.12.105 subnet.</para>
         </listitem>
 
index a5a048e..38bff94 100644 (file)
 
 #include "cellconfig.h"
 
-#define AFS_IPINVALID        0xffffffff        /* invalid IP address */
-#define AFS_IPINVALIDIGNORE  0xfffffffe        /* no input given to extractAddr */
-#define MAX_NETFILE_LINE       2048    /* length of a line in the netrestrict file */
+#define AFS_IPINVALID           -1     /* invalid IP address */
+#define AFS_IPINVALIDIGNORE     -2     /* no input given to extractAddr */
+#define MAX_NETFILE_LINE       2048    /* length of a line in the NetRestrict file */
 #define MAXIPADDRS             1024    /* from afsd.c */
 
 static int ParseNetInfoFile_int(afs_uint32 *, afs_uint32 *, afs_uint32 *,
                          int, char reason[], const char *,
                          int);
-/*
+
+/**
  * The line parameter is a pointer to a buffer containing a string of
- * bytes of the form
-** w.x.y.z     # machineName
- * returns the network interface IP Address in NBO
+ * bytes of the form:
+ *
+ * w.x.y.z[/n]         # machineName
+ *
+ * Returns an IPv4 address and mask in network byte order.  Optionally,
+ * a '/' may be used to specify a subnet mask length.
+ *
+ * @param[in] line
+ *     Pointer to a string of bytes
+ * @param[out] maxSize
+ *     Length to search in line for addresses
+ * @param[out] addr
+ *     IPv4 address in network byte order
+ * @param[out] mask
+ *     IPv4 subnet mask in network byte order, default to 0xffffffff
+ *
+ * @return
+ *      @retval 0 success
+ *      @retval AFS_IPINVALID the address is invalid or parsing failed
+ *      @retval AFS_IPINVALIDIGNORE blank line that can be ignored
  */
-afs_uint32
-extract_Addr(char *line, int maxSize)
+static int
+extract_Addr(char *line, int maxSize, afs_uint32 *addr, afs_uint32 *mask)
 {
     char bytes[4][32];
     int i = 0, n = 0;
     char *endPtr;
     afs_uint32 val[4];
-    afs_uint32 retval = 0;
+    int subnet_len = 32;
 
     /* skip empty spaces */
     while (isspace(*line) && maxSize) {
@@ -57,8 +75,15 @@ extract_Addr(char *line, int maxSize)
     if (!maxSize || !*line)
        return AFS_IPINVALIDIGNORE;
 
+    /* init to 0.0.0.0 for strtol() */
+    for (n = 0; n < 4; n++) {
+       bytes[n][0] = '0';
+       bytes[n][1] = '\0';
+    }
+
     for (n = 0; n < 4; n++) {
-       while ((*line != '.') && !isspace(*line) && maxSize) {  /* extract nth byte */
+       while ((*line != '.') && !isspace(*line)
+              && (*line != '/') && maxSize) {  /* extract nth byte */
            if (!isdigit(*line))
                return AFS_IPINVALID;
            if (i > 31)
@@ -68,16 +93,47 @@ extract_Addr(char *line, int maxSize)
        }                       /* while */
        if (!maxSize)
            return AFS_IPINVALID;
-       bytes[n][i] = 0;
-       i = 0, line++;
+       bytes[n][i] = '\0';
+       if (*line == '/')
+           break;
+       i = 0;
+       line++;
+    }
+
+    if (*line == '.')
+       ++line;                 /* single trailing . allowed */
+
+    if (*line == '/') {                /* have a subnet length */
+       line++;
+        subnet_len = 0;
+       while (isdigit(*line)) {
+           subnet_len = subnet_len * 10 + (*line - '0');
+           if (subnet_len > 32)
+                   return AFS_IPINVALID;       /* subnet length too long */
+           ++line;
+       }
+       if (subnet_len == 0)
+           return AFS_IPINVALID;       /* subnet length too short */
+    }
+
+    if (!isspace(*line) && (*line != '\0'))
+           return AFS_IPINVALID;       /* improperly formed comment */
+
+    for (n = 0; n < 4; n++) {
        errno = 0;
        val[n] = strtol(bytes[n], &endPtr, 10);
-       if ((val[n] == 0) && (errno != 0 || bytes[n] == endPtr))        /* no conversion */
+       if ((val[n] == 0) && (errno != 0 || bytes[n] == endPtr)) /* no conversion */
            return AFS_IPINVALID;
-    }                          /* for */
+    }
+
+    *mask = 0;
+    while (subnet_len--) {
+       *mask = (*mask >> 1) | 0x80000000;
+    }
 
-    retval = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3];
-    return htonl(retval);
+    *mask = htonl(*mask);
+    *addr = htonl((val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3]);
+    return 0;
 }
 
 /**
@@ -124,7 +180,8 @@ parseNetRestrictFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[],
     char line[MAX_NETFILE_LINE];
     int lineNo, usedfile = 0;
     afs_uint32 i, neaddrs, nOutaddrs;
-    afs_uint32 addr, eAddrs[MAXIPADDRS], eMask[MAXIPADDRS], eMtu[MAXIPADDRS];
+    afs_uint32 addr, mask, eAddrs[MAXIPADDRS], eMask[MAXIPADDRS], eMtu[MAXIPADDRS];
+    int retval;
 
     opr_Assert(outAddrs);
     opr_Assert(reason);
@@ -165,13 +222,13 @@ parseNetRestrictFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[],
     usedfile = 0;
     while (fgets(line, MAX_NETFILE_LINE, fp) != NULL) {
        lineNo++;               /* input line number */
-       addr = extract_Addr(line, strlen(line));
-       if (addr == AFS_IPINVALID) {    /* syntactically invalid */
+       retval = extract_Addr(line, strlen(line), &addr, &mask);
+       if (retval == AFS_IPINVALID) {  /* syntactically invalid */
            fprintf(stderr, "%s : line %d : parse error - invalid IP\n",
                    fileName, lineNo);
            continue;
        }
-       if (addr == AFS_IPINVALIDIGNORE) {      /* ignore error */
+       if (retval == AFS_IPINVALIDIGNORE) {    /* ignore error */
            fprintf(stderr, "%s : line %d : invalid address ... ignoring\n",
                    fileName, lineNo);
            continue;
@@ -180,7 +237,7 @@ parseNetRestrictFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[],
 
        /* Check if we need to exclude this address */
        for (i = 0; i < neaddrs; i++) {
-           if (eAddrs[i] && (eAddrs[i] == addr)) {
+           if (eAddrs[i] && ((eAddrs[i] & mask) == (addr & mask))) {
                eAddrs[i] = 0;  /* Yes - exclude it by zeroing it for now */
            }
        }
@@ -261,9 +318,10 @@ ParseNetInfoFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[], afs_uint32 out
     char line[MAX_NETFILE_LINE];
     FILE *fp;
     int i, existNu, count = 0;
-    afs_uint32 addr;
+    afs_uint32 addr, mask;
     int lineNo = 0;
     int l;
+    int retval;
 
     opr_Assert(fileName);
     opr_Assert(outAddrs);
@@ -310,20 +368,25 @@ ParseNetInfoFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[], afs_uint32 out
        }
 
        lineNo++;               /* input line number */
-       addr = extract_Addr(&line[fake], strlen(&line[fake]));
+       retval = extract_Addr(&line[fake], strlen(&line[fake]), &addr, &mask);
 
-       if (addr == AFS_IPINVALID) {    /* syntactically invalid */
+       if (retval == AFS_IPINVALID) {  /* syntactically invalid */
            fprintf(stderr, "afs:%s : line %d : parse error\n", fileName,
                    lineNo);
            continue;
        }
-       if (addr == AFS_IPINVALIDIGNORE) {      /* ignore error */
+       if (fake && ntohl(mask) != 0xffffffff) {
+           fprintf(stderr, "afs:%s : line %d : bad fake address\n", fileName,
+                   lineNo);
+           continue;
+       }
+       if (retval == AFS_IPINVALIDIGNORE) {    /* ignore error */
            continue;
        }
 
        /* See if it is an address that really exists */
        for (i = 0; i < existNu; i++) {
-           if (existingAddr[i] == addr)
+           if ((existingAddr[i] & mask) == (addr & mask))
                break;
        }
        if ((i >= existNu) && (!fake))
@@ -331,12 +394,12 @@ ParseNetInfoFile_int(afs_uint32 outAddrs[], afs_uint32 outMask[], afs_uint32 out
 
        /* Check if it is a duplicate address we alread have */
        for (l = 0; l < count; l++) {
-           if (outAddrs[l] == addr)
+           if ((outAddrs[l] & mask) == (addr & mask))
                break;
        }
        if (l < count) {
-           fprintf(stderr, "afs:%x specified twice in NetInfo file\n",
-                   ntohl(addr));
+           fprintf(stderr, "afs:%x matched more than once in NetInfo file\n",
+                   ntohl(outAddrs[l]));
            continue;           /* duplicate addr - ignore */
        }
 
index f45f530..ee8ebde 100644 (file)
@@ -17,7 +17,7 @@ LT_deps = \
        $(TOP_OBJDIR)/src/opr/liboafs_opr.la
 LT_libs = $(LDFLAGS_roken) $(LIB_roken)
 
-tests all: testcellconf ktctest
+tests all: testcellconf ktctest testnetrestrict
 
 testcellconf: testcellconf.lo
        $(LT_LDRULE_static) testcellconf.lo $(LT_deps) $(LT_libs)
@@ -25,6 +25,9 @@ testcellconf: testcellconf.lo
 ktctest: ktctest.lo
        $(LT_LDRULE_static) ktctest.lo $(LT_deps) $(LT_libs)
 
+testnetrestrict: testnetrestrict.o
+       $(AFS_LDRULE) testnetrestrict.o $(TOP_LIBDIR)/libauth.a $(TOP_LIBDIR)/libopr.a
+
 clean:
        $(LT_CLEAN)
        $(RM) -f copyauth testcellconf ktctest setkey auth.h cellconfig.h acfg_errors.c ktc_errors.c core
diff --git a/src/auth/test/testnetrestrict.c b/src/auth/test/testnetrestrict.c
new file mode 100644 (file)
index 0000000..26ff957
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * testnetstrict.c
+ *
+ * Utility to test NetInfo and NetRestrict parsing
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+#include <afs/cellconfig.h>
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <arpa/inet.h>
+
+char *interfaceList, *filenameNetInfo, *filenameNetRestrict;
+
+int
+rx_getAllAddrMaskMtu(afs_uint32 addrBuffer[],
+                    afs_uint32 maskBuffer[],
+                    afs_uint32 mtuBuffer[],
+                    int maxSize)
+{
+    FILE *fp;
+    int nInterfaces = 0, lineNo = 1;
+    int a[4], m[4], mtu;
+    char line[80];
+
+    fp = fopen(interfaceList, "r");
+    if (fp == NULL) {
+       perror("fopen");
+       exit(1);
+    }
+
+    while (nInterfaces < maxSize) {
+       ++lineNo;
+       if (fgets(line, 80, fp) == NULL)
+               break;
+       if (sscanf(line, "%d.%d.%d.%d %d.%d.%d.%d %d\n",
+                 &a[0], &a[1], &a[2], &a[3],
+                 &m[0], &m[1], &m[2], &m[3], &mtu) == 9) {
+
+           addrBuffer[nInterfaces] = htonl(a[0] << 24 | a[1] << 16
+                                           | a[2] << 8 | a[3]);
+           maskBuffer[nInterfaces] = htonl(m[0] << 24 | m[1] << 16
+                                           | m[2] << 8 | m[3]);
+           mtuBuffer[nInterfaces] = htonl(mtu);
+           ++nInterfaces;
+       } else {
+           fprintf(stderr, "failed to parse line %d in %s\n", lineNo, interfaceList);
+       }
+    }
+    fclose(fp);
+
+    return nInterfaces;
+}
+
+#define MAXADDRS 16
+
+int
+main(int argc, char *argv[])
+{
+    afs_uint32 outAddrs[MAXADDRS], outMask[MAXADDRS], outMtu[MAXADDRS], nAddrs;
+    char reason[1024];
+    int i, retval;
+
+    if (argc < 4) {
+       fprintf(stderr, "usage: testnetrestrict <interface list> <netinfo> <netstrict>\n");
+       exit(1);
+    }
+
+    interfaceList = argv[1];
+    filenameNetInfo = argv[2];
+    filenameNetRestrict = argv[3];
+
+    reason[0] = '\0';
+    retval = afsconf_ParseNetInfoFile(outAddrs, outMask, outMtu, MAXADDRS,
+                                     reason, filenameNetInfo);
+
+    printf("afsconf_ParseNetInfoFile() returned %d\n", retval);
+    if (reason[0] != '\0')
+       printf("reason: %s\n", reason);
+
+    printf("final address list:\n");
+    if (retval > 0) {
+       for(i = 0; i < retval; ++i) {
+           printf("\taddress 0x%x  mask 0x%x  mtu %d\n",
+                  ntohl(outAddrs[i]), ntohl(outMask[i]), ntohl(outMtu[i]));
+       }
+    }
+
+    reason[0] = '\0';
+    retval = afsconf_ParseNetRestrictFile(outAddrs, outMask, outMtu, MAXADDRS,
+                                         &nAddrs, reason, filenameNetRestrict);
+
+    printf("\nafsconf_ParseNetRestrictFile() returned %d\n", retval);
+    if (reason[0] != '\0')
+       printf("reason: %s\n", reason);
+    if (nAddrs > 0) {
+       printf("final address list:\n");
+       for(i = 0; i < nAddrs; ++i) {
+           printf("\taddress 0x%x  mask 0x%x  mtu %d\n",
+                  ntohl(outAddrs[i]), ntohl(outMask[i]), ntohl(outMtu[i]));
+       }
+    }
+
+    reason[0] = '\0';
+    retval = afsconf_ParseNetFiles(outAddrs, outMask, outMtu, MAXADDRS,
+                                  reason, filenameNetInfo, filenameNetRestrict);
+
+    printf("\nafsconf_ParseNetFiles() returned %d\n", retval);
+    if (reason[0] != '\0')
+       printf("reason: %s\n", reason);
+    if (retval > 0) {
+       printf("final address list:\n");
+       for(i = 0; i < retval; ++i) {
+           printf("\taddress 0x%x  mask 0x%x  mtu %d\n",
+                  ntohl(outAddrs[i]), ntohl(outMask[i]), ntohl(outMtu[i]));
+       }
+    }
+
+    exit(0);
+}