Whamcloud - gitweb
LU-14288 lnet: Introduce nidmasks 22/55922/13
authorChris Horn <chris.horn@hpe.com>
Fri, 26 Jul 2024 20:36:51 +0000 (14:36 -0600)
committerOleg Drokin <green@whamcloud.com>
Sun, 24 Nov 2024 06:02:03 +0000 (06:02 +0000)
A nidmask is like a netmask, except it applies to IPv4 or IPv6 LNet
NIDs.

Nidmasks use the existing nidlist infrastructure so any caller of
cfs_parse_nidlist() can include a nidmask in the argument and match
NIDs against it using cfs_match_nid(), or convert it back to a string
with cfs_print_nidlist().

For example, "192.168.1.1@tcp/24" is equivalent to the nidrange
"192.168.1.[1-254]@tcp", and "2001::1@tcp/126" is equivalent to
"2001::@tcp 2001::1@tcp 2001::2@tcp 2001::3@tcp".

cfs_parse_nidrange() is modified to treat an IPv6 address as
equivalent to a netmask with prefix length of /128. Thus,
cfs_parse_nidlist() can now be used with lists of IPv6 addresses.

The user and kernel space implementations of cfs_parse_nidlist(),
et. al. have been modified to more closely match each other. Namely,
char * is used instead of the struct cfs_lstr and a length argument
is added to the kernel space cfs_parse_nidlist. Callers are adjusted
accordingly.

conf-sanity.sh/test_43a is modified to generate nidmasks that contain
the client's NID and verify that this is handled correctly when
nosquash_nids is set to the nidmask.

lnetctl debug nidlist command is added to facilitate testing of the
userspace code.

Test-Parameters: trivial testlist=conf-sanity env=ONLY=43a
Test-Parameters: testlist=conf-sanity env=ONLY=43a,FORCE_LARGE_NID=true,LOAD_MODULES_REMOTE=true
Signed-off-by: Chris Horn <chris.horn@hpe.com>
Change-Id: Id9d0bc6f4f8b977591f0b6f88bda46ae03cb58d5
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55922
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Neil Brown <neilb@suse.de>
Reviewed-by: Serguei Smirnov <ssmirnov@whamcloud.com>
14 files changed:
libcfs/include/libcfs/util/string.h
libcfs/libcfs/util/nidstrings.c
libcfs/libcfs/util/string.c
lnet/include/lnet/lib-types.h
lnet/include/uapi/linux/lnet/nidstr.h
lnet/lnet/nidstrings.c
lnet/utils/lnetconfig/liblnetconfig.c
lnet/utils/lnetconfig/liblnetconfig.h
lnet/utils/lnetctl.c
lustre/obdclass/lprocfs_status.c
lustre/obdclass/obd_mount.c
lustre/ptlrpc/nrs_tbf.c
lustre/tests/conf-sanity.sh
lustre/utils/obd.c

index 97edf59..d484d10 100644 (file)
@@ -90,6 +90,9 @@ struct netstrfns {
        int     (*nf_print_addrlist)(char *buffer, int count,
                                     struct list_head *list);
        int     (*nf_match_addr)(__u32 addr, struct list_head *list);
+       int     (*nf_match_netmask)(const __be32 *addr, size_t asize,
+                                   const __be32 *netmask,
+                                   const __be32 *netaddr);
        int     (*nf_min_max)(struct list_head *nidlist, __u32 *min_nid,
                              __u32 *max_nid);
        int     (*nf_expand_addrrange)(struct list_head *addrranges,
@@ -137,10 +140,14 @@ int cfs_ip_addr_parse(char *str, int len, struct list_head *list);
 int cfs_ip_addr_range_gen(__u32 *ip_list, int count,
                          struct list_head *ip_addr_expr);
 int cfs_ip_addr_match(__u32 addr, struct list_head *list);
+int libcfs_ip_in_netmask(const __be32 *addr, size_t asize,
+                        const __be32 *netmask,
+                        const __be32 *netaddr);
 int cfs_expand_nidlist(struct list_head *nidlist, lnet_nid_t *lnet_nidlist,
                       int max_nids);
 int cfs_parse_nid_parts(char *str, struct list_head *addr,
                        struct list_head *net_num, __u32 *net_type);
 int cfs_abs_path(const char *request_path, char **resolved_path);
+char *strim(char *s);
 
 #endif
index 2b11313..d892b8b 100644 (file)
@@ -42,6 +42,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 
 #include <libcfs/util/string.h>
 #include <linux/lnet/lnet-types.h>
@@ -533,6 +534,43 @@ cfs_ip_addr_match(__u32 addr, struct list_head *list)
        return i == 4;
 }
 
+/**
+ * Matches address (\a addr) against the netmask encoded in \a netmask and
+ * \a netaddr.
+ *
+ * \retval 1 if \a addr matches
+ * \retval 0 otherwise
+ */
+int
+libcfs_ip_in_netmask(const __be32 *addr, size_t asize, const __be32 *netmask,
+                    const __be32 *netaddr)
+{
+       if (asize == 4) {
+               struct in_addr nid_addr, masked_addr;
+
+               memcpy(&nid_addr.s_addr, addr, asize);
+
+               masked_addr.s_addr = nid_addr.s_addr &
+                                    (*(struct in_addr *)netmask).s_addr;
+
+               return memcmp(&masked_addr, netaddr, sizeof(masked_addr)) == 0;
+       } else if (asize == 16) {
+               struct in6_addr nid_addr, masked_addr;
+               int i;
+
+               memcpy(&nid_addr.s6_addr, addr, asize);
+
+               for (i = 0; i < 16; i++)
+                       masked_addr.s6_addr[i] =
+                               nid_addr.s6_addr[i] &
+                               (*(struct in6_addr *)netmask).s6_addr[i];
+
+               return memcmp(&masked_addr, netaddr, sizeof(masked_addr)) == 0;
+       }
+
+       return 0;
+}
+
 static void
 libcfs_decnum_addr2str(__u32 addr, char *str, size_t size)
 {
@@ -638,7 +676,8 @@ static struct netstrfns libcfs_netstrfns[] = {
                .nf_print_addrlist      = libcfs_ip_addr_range_print,
                .nf_match_addr          = cfs_ip_addr_match,
                .nf_min_max             = cfs_ip_min_max,
-               .nf_expand_addrrange    = libcfs_ip_addr_range_expand
+               .nf_expand_addrrange    = libcfs_ip_addr_range_expand,
+               .nf_match_netmask       = libcfs_ip_in_netmask
        },
        {
                .nf_type                = O2IBLND,
@@ -650,7 +689,8 @@ static struct netstrfns libcfs_netstrfns[] = {
                .nf_print_addrlist      = libcfs_ip_addr_range_print,
                .nf_match_addr          = cfs_ip_addr_match,
                .nf_min_max             = cfs_ip_min_max,
-               .nf_expand_addrrange    = libcfs_ip_addr_range_expand
+               .nf_expand_addrrange    = libcfs_ip_addr_range_expand,
+               .nf_match_netmask       = libcfs_ip_in_netmask
        },
        {
                .nf_type                = GNILND,
@@ -1043,26 +1083,29 @@ libcfs_stranynid(struct lnet_nid *nid, const char *str)
        return !LNET_NID_IS_ANY(nid);
 }
 
-/**
- * Nid range list syntax.
+/* NID range list syntax.
  * \verbatim
  *
- * <nidlist>         :== <nidrange> [ ' ' <nidrange> ]
- * <nidrange>        :== <addrrange> '@' <net>
- * <addrrange>       :== '*' |
- *                       <ipaddr_range> |
- *                      <cfs_expr_list>
- * <ipaddr_range>    :== <cfs_expr_list>.<cfs_expr_list>.<cfs_expr_list>.
- *                      <cfs_expr_list>
- * <cfs_expr_list>   :== <number> |
- *                       <expr_list>
- * <expr_list>       :== '[' <range_expr> [ ',' <range_expr>] ']'
- * <range_expr>      :== <number> |
- *                       <number> '-' <number> |
- *                       <number> '-' <number> '/' <number>
- * <net>             :== <netname> | <netname><number>
- * <netname>         :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
- *                       "vib" | "ra" | "elan" | "mx" | "ptl"
+ * <nidlist>        :== <nidrange> [ ' ' <nidrange> ]
+ * <nidrange>       :== <addrrange> '@' <net>
+ * <addrrange>      :== '*' |
+ *                      <netmask> |
+ *                      <ipv6_addr> |
+ *                      <ipv4_addr_range> |
+ *                      <numaddr_range>
+ * <netmask>        :== An IPv4 or IPv6 network mask in CIDR notation.
+ *                      e.g. 192.168.1.0/24 or 2001:0db8::/32
+ * <ipv6_addr>      :== A single IPv6 address
+ * <ipv4_addr_range> :==
+ *     <numaddr_range>.<numaddr_range>.<numaddr_range>.<numaddr_range>
+ * <numaddr_range>   :== <number> |
+ *                      <expr_list>
+ * <expr_list>      :== '[' <range_expr> [ ',' <range_expr>] ']'
+ * <range_expr>             :== <number> |
+ *                      <number> '-' <number> |
+ *                      <number> '-' <number> '/' <number>
+ * <net>            :== <netname> | <netname><number>
+ * <netname>        :== "lo" | "tcp" | "o2ib" | "gni" | "gip" | "ptlf" | "kfi"
  * \endverbatim
  */
 
@@ -1082,6 +1125,10 @@ struct nidrange {
         */
        struct list_head nr_addrranges;
        /**
+        * List head for nidmask::nm_link.
+        */
+       struct list_head nr_nidmasks;
+       /**
         * Flag indicating that *@<net> is found.
         */
        int nr_all;
@@ -1096,6 +1143,38 @@ struct nidrange {
 };
 
 /**
+ * Structure to represent \<netmask\> token of the syntax
+ */
+struct nidmask {
+       /* Link to nidrange::nr_nidmasks */
+       struct list_head nm_link;
+
+       /* This is the base address that was parsed */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_addr;
+
+       /* Netmask derived from the prefix length */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_netmask;
+
+       /* Network address derived from the base address and the netmask */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_netaddr;
+
+       /* Address family */
+       sa_family_t nm_family;
+
+       /* Prefix length */
+       __u8 nm_prefix_len;
+};
+
+/**
  * Structure to represent \<addrrange\> token of the syntax.
  */
 struct addrrange {
@@ -1119,26 +1198,135 @@ struct addrrange {
  * \retval -errno otherwise
  */
 static int
-parse_addrange(const struct cfs_lstr *src, struct nidrange *nidrange)
+parse_addrange(char *str, struct nidrange *nidrange)
 {
        struct addrrange *addrrange;
 
-       if (src->ls_len == 1 && src->ls_str[0] == '*') {
+       if (strcmp(str, "*") == 0) {
                nidrange->nr_all = 1;
                return 0;
        }
 
        addrrange = calloc(1, sizeof(struct addrrange));
-       if (addrrange == NULL)
+       if (!addrrange)
                return -ENOMEM;
        list_add_tail(&addrrange->ar_link, &nidrange->nr_addrranges);
        INIT_LIST_HEAD(&addrrange->ar_numaddr_ranges);
 
-       return nidrange->nr_netstrfns->nf_parse_addrlist(src->ls_str,
-                                               src->ls_len,
+       return nidrange->nr_netstrfns->nf_parse_addrlist(str, strlen(str),
                                                &addrrange->ar_numaddr_ranges);
 }
 
+static void
+init_ipv4_nidmask(struct in_addr *ipv4, struct nidmask *nm)
+{
+       memcpy(&nm->nm_addr.ipv4, &ipv4->s_addr, sizeof(struct in_addr));
+
+       nm->nm_netmask.ipv4.s_addr =
+               htonl(~((1U << (32 - nm->nm_prefix_len)) - 1));
+       nm->nm_netaddr.ipv4.s_addr = ipv4->s_addr & nm->nm_netmask.ipv4.s_addr;
+}
+
+/* Note: The memory of struct nidmask is allocated via calloc, so it has been
+ * set to zero as required by this function.
+ */
+static void
+init_ipv6_nidmask(struct in6_addr *ipv6, struct nidmask *nm)
+{
+       int i, j;
+
+       memcpy(&nm->nm_addr.ipv6, &ipv6->s6_addr, sizeof(struct in6_addr));
+
+       for (i = nm->nm_prefix_len, j = 0; i > 0; i -= 8, j++) {
+               if (i >= 8)
+                       nm->nm_netmask.ipv6.s6_addr[j] = 0xff;
+               else
+                       nm->nm_netmask.ipv6.s6_addr[j] =
+                                       (unsigned long)(0xffU << (8 - i));
+       }
+
+       for (i = 0; i < sizeof(struct in6_addr); i++)
+               nm->nm_netaddr.ipv6.s6_addr[i] = ipv6->s6_addr[i] &
+                                                nm->nm_netmask.ipv6.s6_addr[i];
+}
+
+static __u8
+parse_prefix_len(char *str)
+{
+       unsigned int max;
+       unsigned int prefix_len;
+       char *slash = strchr(str, '/');
+
+       /* IPv4 netmask must include an explicit prefix length */
+       if (!(slash || strchr(str, ':')))
+               return 0;
+
+       /* We treat an IPv6 address without a prefix length as having /128 */
+       if (!slash)
+               return 128;
+
+       if (strchr(str, ':'))
+               max = 128;
+       else
+               max = 32;
+
+       slash++;
+       if (!cfs_str2num_check(slash, strlen(slash), &prefix_len, 1, max))
+               return 0;
+
+       return (__u8)prefix_len;
+}
+
+static int
+parse_nidmask(char *str, struct nidrange *nr)
+{
+       struct nidmask *nm;
+       char *addrstr;
+       struct netstrfns *nf = nr->nr_netstrfns;
+       size_t asize;
+       __u32 addr[4];
+
+       nm = calloc(1, sizeof(struct nidmask));
+       if (!nm) {
+               fprintf(stderr, "Failed to allocate memory for nidmask\n");
+               return -ENOMEM;
+       }
+
+       /* Add to nr_nidmasks so that our caller can free us on error */
+       list_add_tail(&nm->nm_link, &nr->nr_nidmasks);
+
+       nm->nm_prefix_len = parse_prefix_len(str);
+       if (!nm->nm_prefix_len) {
+               fprintf(stderr, "Failed to parse prefix length from \"%s\"\n",
+                       str);
+               return -EINVAL;
+       }
+
+       if (!nf->nf_str2addr_size) {
+               fprintf(stderr, "Network type doesn't support nidmasks\n");
+               return -EINVAL;
+       }
+
+       addrstr = strsep(&str, "/");
+       if (!nf->nf_str2addr_size(addrstr, strlen(addrstr), addr, &asize)) {
+               fprintf(stderr, "Failed to convert \"%s\" to address\n",
+                       addrstr);
+               return -EINVAL;
+       }
+
+       if (asize == 4) {
+               nm->nm_family = AF_INET;
+               init_ipv4_nidmask((struct in_addr *)&addr, nm);
+       } else if (asize == 16) {
+               nm->nm_family = AF_INET6;
+               init_ipv6_nidmask((struct in6_addr *)&addr, nm);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /**
  * Finds or creates struct nidrange.
  *
@@ -1150,30 +1338,27 @@ parse_addrange(const struct cfs_lstr *src, struct nidrange *nidrange)
  * \retval NULL if \a src does not match any network
  */
 static struct nidrange *
-add_nidrange(const struct cfs_lstr *src,
-            struct list_head *nidlist)
+add_nidrange(char *str, struct list_head *nidlist)
 {
        struct netstrfns *nf;
        struct nidrange *nr;
-       int endlen;
-       unsigned netnum;
+       char *end;
+       unsigned int netnum;
 
-       if (src->ls_len >= LNET_NIDSTR_SIZE)
+       nf = libcfs_namenum2netstrfns(str);
+       if (!nf)
                return NULL;
 
-       nf = libcfs_namenum2netstrfns(src->ls_str);
-       if (nf == NULL)
-               return NULL;
-       endlen = src->ls_len - strlen(nf->nf_name);
-       if (endlen == 0)
+       end = str + strlen(nf->nf_name);
+       if (!*end) {
                /* network name only, e.g. "elan" or "tcp" */
                netnum = 0;
-       else {
+       else {
                /* e.g. "elan25" or "tcp23", refuse to parse if
                 * network name is not appended with decimal or
                 * hexadecimal number */
-               if (!cfs_str2num_check(src->ls_str + strlen(nf->nf_name),
-                                      endlen, &netnum, 0, MAX_NUMERIC_VALUE))
+               if (!cfs_str2num_check(end, strlen(end), &netnum, 0,
+                                      MAX_NUMERIC_VALUE))
                        return NULL;
        }
 
@@ -1186,10 +1371,11 @@ add_nidrange(const struct cfs_lstr *src,
        }
 
        nr = calloc(1, sizeof(struct nidrange));
-       if (nr == NULL)
+       if (!nr)
                return NULL;
        list_add_tail(&nr->nr_link, nidlist);
        INIT_LIST_HEAD(&nr->nr_addrranges);
+       INIT_LIST_HEAD(&nr->nr_nidmasks);
        nr->nr_netstrfns = nf;
        nr->nr_all = 0;
        nr->nr_netnum = netnum;
@@ -1200,36 +1386,49 @@ add_nidrange(const struct cfs_lstr *src,
 /**
  * Parses \<nidrange\> token of the syntax.
  *
- * \retval 1 if \a src parses to \<addrrange\> '@' \<net\>
- * \retval 0 otherwise
+ * \retval 0 if \a src parses to \<addrrange\> '@' \<net\>
+ * \retval -errno otherwise
  */
 static int
-parse_nidrange(struct cfs_lstr *src, struct list_head *nidlist)
+parse_nidrange(char *str, struct list_head *nidlist)
 {
-       struct cfs_lstr addrrange;
-       struct cfs_lstr net;
-       struct cfs_lstr tmp;
+       char *addrrange;
+       char *net;
+       char *slash;
        struct nidrange *nr;
+       int rc;
 
-       tmp = *src;
-       if (cfs_gettok(src, '@', &addrrange) == 0)
-               goto failed;
+       addrrange = strsep(&str, "@");
+       if (!str) {
+               fprintf(stderr, "nidrange \"%s\" doesn't contain '@'\n",
+                       addrrange);
+               return -EINVAL;
+       }
 
-       if (cfs_gettok(src, '@', &net) == 0 || src->ls_str != NULL)
-               goto failed;
+       net = strim(str);
+       if (strchr(net, '@') || !*net) {
+               fprintf(stderr, "net \"%s\" is malformed\n", net);
+               return -EINVAL;
+       }
 
-       nr = add_nidrange(&net, nidlist);
-       if (nr == NULL)
-               goto failed;
+       nr = add_nidrange(net, nidlist);
+       if (!nr) {
+               fprintf(stderr, "failed to add nidrange for network \"%s\"\n",
+                       net);
+               return -EINVAL;
+       }
 
-       if (parse_addrange(&addrrange, nr) != 0)
-               goto failed;
+       /* Check for an IPv6 address or a '/' outside of '[]' */
+       slash = strchr(addrrange, '/');
+       if (strchr(addrrange, ':') || (slash && !strchr(slash, ']')))
+               rc = parse_nidmask(addrrange, nr);
+       else
+               rc = parse_addrange(addrrange, nr);
 
-       return 1;
- failed:
-       fprintf(stderr, "can't parse nidrange: \"%.*s\"\n",
-               tmp.ls_len, tmp.ls_str);
-       return 0;
+       if (rc)
+               fprintf(stderr, "Failed to parse addrrange \"%s\" rc = %d\n",
+                       addrrange, rc);
+       return rc;
 }
 
 static __u32
@@ -1386,6 +1585,18 @@ free_addrranges(struct list_head *list)
        }
 }
 
+static void
+free_nidmasks(struct list_head *list)
+{
+       struct nidmask *nm;
+
+       while (!list_empty(list)) {
+               nm = list_first_entry(list, struct nidmask, nm_link);
+               list_del(&nm->nm_link);
+               free(nm);
+       }
+}
+
 /**
  * Frees nidrange strutures of \a list.
  *
@@ -1403,6 +1614,7 @@ cfs_free_nidlist(struct list_head *list)
        list_for_each_safe(pos, next, list) {
                nr = list_entry(pos, struct nidrange, nr_link);
                free_addrranges(&nr->nr_addrranges);
+               free_nidmasks(&nr->nr_nidmasks);
                list_del(pos);
                free(nr);
        }
@@ -1422,28 +1634,54 @@ cfs_free_nidlist(struct list_head *list)
  * \retval 0 otherwise
  */
 int
-cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
+cfs_parse_nidlist(char *orig, int len, struct list_head *nidlist)
 {
-       struct cfs_lstr src;
-       struct cfs_lstr res;
-       int rc;
+       int rc = 0;
+       char *str;
+
+       orig = strndup(orig, len);
+       if (!orig)
+               return 0;
 
-       src.ls_str = str;
-       src.ls_len = len;
        INIT_LIST_HEAD(nidlist);
-       while (src.ls_str) {
-               rc = cfs_gettok(&src, ' ', &res);
-               if (rc == 0) {
-                       cfs_free_nidlist(nidlist);
-                       return 0;
-               }
-               rc = parse_nidrange(&res, nidlist);
-               if (rc == 0) {
-                       cfs_free_nidlist(nidlist);
-                       return 0;
-               }
+       str = orig;
+       while (rc == 0 && str) {
+               char *tok = strsep(&str, " ");
+
+               if (*tok)
+                       rc = parse_nidrange(tok, nidlist);
        }
-       return 1;
+       free(orig);
+       if (rc)
+               cfs_free_nidlist(nidlist);
+       else if (list_empty(nidlist))
+               rc = -EINVAL;
+
+       return rc ? 0 : 1;
+}
+
+static int
+match_nidmask(struct lnet_nid *nid, struct nidmask *nm, struct netstrfns *nf)
+{
+       __be32 addr[4] = { nid->nid_addr[0], nid->nid_addr[1],
+                          nid->nid_addr[2], nid->nid_addr[3] };
+       __be32 *netmask, *netaddr;
+
+       if (!nf->nf_match_netmask)
+               return 0;
+
+       if (nid_is_nid4(nid) && nm->nm_family == AF_INET) {
+               netmask = (__be32 *)&nm->nm_netmask.ipv4.s_addr;
+               netaddr = (__be32 *)&nm->nm_netaddr.ipv4.s_addr;
+       } else if (!nid_is_nid4(nid) && nm->nm_family == AF_INET6) {
+               netmask = (__be32 *)&nm->nm_netmask.ipv6.s6_addr;
+               netaddr = (__be32 *)&nm->nm_netaddr.ipv6.s6_addr;
+       } else {
+               return 0;
+       }
+
+       return nf->nf_match_netmask(addr, NID_ADDR_BYTES(nid), netmask,
+                                   netaddr);
 }
 
 /**
@@ -1457,10 +1695,9 @@ cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
 int cfs_match_nid(struct lnet_nid *nid, struct list_head *nidlist)
 {
        struct nidrange *nr;
+       struct nidmask *nm;
        struct addrrange *ar;
 
-       if (!nid_is_nid4(nid))
-               return 0;
        list_for_each_entry(nr, nidlist, nr_link) {
                if (nr->nr_netstrfns->nf_type != nid->nid_type)
                        continue;
@@ -1468,6 +1705,11 @@ int cfs_match_nid(struct lnet_nid *nid, struct list_head *nidlist)
                        continue;
                if (nr->nr_all)
                        return 1;
+
+               list_for_each_entry(nm, &nr->nr_nidmasks, nm_link)
+                       if (match_nidmask(nid, nm, nr->nr_netstrfns))
+                               return 1;
+
                list_for_each_entry(ar, &nr->nr_addrranges, ar_link)
                        if (nr->nr_netstrfns->nf_match_addr(
                                    __be32_to_cpu(nid->nid_addr[0]),
@@ -1547,6 +1789,40 @@ cfs_print_addrranges(char *buffer, int count, struct list_head *addrranges,
        return i;
 }
 
+static int
+cfs_print_nidmasks(char *buffer, int count, struct list_head *nidmasks,
+                  struct nidrange *nr)
+{
+       int i = 0;
+       struct nidmask *nm;
+       __u8 max;
+
+       list_for_each_entry(nm, nidmasks, nm_link) {
+               if (i != 0)
+                       i += scnprintf(buffer + i, count - i, " ");
+
+               /* parse_nidmask() ensures nm_family is set to either AF_INET
+                * or AF_INET6
+                */
+               if (nm->nm_family == AF_INET) {
+                       inet_ntop(nm->nm_family, &nm->nm_addr.ipv4.s_addr,
+                                 buffer + i, count - i);
+                       max = 32;
+               } else {
+                       inet_ntop(nm->nm_family, &nm->nm_addr.ipv6.s6_addr,
+                                 buffer + i, count - i);
+                       max = 128;
+               }
+               i += strlen(buffer + i);
+               if (nm->nm_prefix_len < max)
+                       i += scnprintf(buffer + i, count - i, "/%u",
+                                      nm->nm_prefix_len);
+               i += cfs_print_network(buffer + i, count - i, nr);
+       }
+
+       return i;
+}
+
 /**
  * Print a list of nidranges (\a nidlist) into the specified \a buffer.
  * At max \a count characters can be printed into \a buffer.
@@ -1558,6 +1834,7 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
 {
        int i = 0;
        struct nidrange *nr;
+       bool need_space = false;
 
        if (count <= 0)
                return 0;
@@ -1568,12 +1845,26 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
 
                if (nr->nr_all != 0) {
                        assert(list_empty(&nr->nr_addrranges));
+                       assert(list_empty(&nr->nr_nidmasks));
                        i += scnprintf(buffer + i, count - i, "*");
                        i += cfs_print_network(buffer + i, count - i, nr);
-               } else {
+                       continue;
+               }
+
+               if (!list_empty(&nr->nr_nidmasks)) {
+                       i += cfs_print_nidmasks(buffer + i, count - i,
+                                               &nr->nr_nidmasks, nr);
+                       need_space = true;
+               }
+
+               if (!list_empty(&nr->nr_addrranges)) {
+                       if (need_space)
+                               i += scnprintf(buffer + i, count - i, " ");
+
                        i += cfs_print_addrranges(buffer + i, count - i,
                                                  &nr->nr_addrranges, nr);
                }
+               need_space = false;
        }
        return i;
 }
index b3e3209..3f3aae6 100644 (file)
@@ -495,3 +495,27 @@ out:
        }
        return rc;
 }
+
+static char *skip_spaces(const char *str)
+{
+       while (isspace(*str))
+               ++str;
+       return (char *)str;
+}
+
+char *strim(char *s)
+{
+       size_t size;
+       char *end;
+
+       size = strlen(s);
+       if (!size)
+               return s;
+
+       end = s + size - 1;
+       while (end >= s && isspace(*end))
+               end--;
+       *(end + 1) = '\0';
+
+       return skip_spaces(s);
+}
index 9396b88..6e8d5b9 100644 (file)
@@ -248,6 +248,9 @@ struct netstrfns {
        int     (*nf_print_addrlist)(char *buffer, int count,
                                     struct list_head *list);
        int     (*nf_match_addr)(u32 addr, struct list_head *list);
+       int     (*nf_match_netmask)(const __be32 *addr, size_t asize,
+                                   const __be32 *netmask,
+                                   const __be32 *netaddr);
        int     (*nf_min_max)(struct list_head *nidlist, u32 *min_nid,
                              u32 *max_nid);
 };
index 23e57e9..f35a4b5 100644 (file)
@@ -103,11 +103,7 @@ int libcfs_stranynid(struct lnet_nid *nid, const char *str);
 int libcfs_num_parse(char *str, int len, struct list_head *list);
 char *libcfs_id2str(struct lnet_process_id id);
 void cfs_free_nidlist(struct list_head *list);
-#ifdef __KERNEL__
-int cfs_parse_nidlist(char *str, struct list_head *list);
-#else
 int cfs_parse_nidlist(char *str, int len, struct list_head *list);
-#endif
 int cfs_print_nidlist(char *buffer, int count, struct list_head *list);
 int cfs_match_nid(struct lnet_nid *nid, struct list_head *list);
 int cfs_match_net(__u32 net_id, __u32 net_type,
@@ -115,6 +111,9 @@ int cfs_match_net(__u32 net_id, __u32 net_type,
 
 int cfs_ip_addr_parse(char *str, int len, struct list_head *list);
 int cfs_ip_addr_match(__u32 addr, struct list_head *list);
+int libcfs_ip_in_netmask(const __be32 *addr, size_t asize,
+                        const __be32 *netmask,
+                        const __be32 *netaddr);
 int cfs_nidrange_find_min_max(struct list_head *nidlist, char *min_nid,
                               char *max_nid, __kernel_size_t nidstr_length);
 void cfs_expr_list_free_list(struct list_head *list);
index 6e409ad..b960725 100644 (file)
@@ -17,6 +17,9 @@
 #include <libcfs/libcfs.h>
 #include <uapi/linux/lnet/nidstr.h>
 #include <lnet/lib-types.h>
+#include <linux/ctype.h>
+#include <linux/inet.h>
+#include <linux/inetdevice.h>
 
 /* max value for numeric network address */
 #define MAX_NUMERIC_VALUE 0xffffffff
@@ -59,26 +62,29 @@ libcfs_next_nidstring(void)
 }
 EXPORT_SYMBOL(libcfs_next_nidstring);
 
-/**
- * Nid range list syntax.
+/* NID range list syntax.
  * \verbatim
  *
  * <nidlist>        :== <nidrange> [ ' ' <nidrange> ]
  * <nidrange>       :== <addrrange> '@' <net>
  * <addrrange>      :== '*' |
- *                      <ipaddr_range> |
- *                      <cfs_expr_list>
- * <ipaddr_range>    :== <cfs_expr_list>.<cfs_expr_list>.<cfs_expr_list>.
- *                      <cfs_expr_list>
- * <cfs_expr_list>   :== <number> |
+ *                      <netmask> |
+ *                      <ipv6_addr> |
+ *                      <ipv4_addr_range> |
+ *                      <numaddr_range>
+ * <netmask>        :== An IPv4 or IPv6 network mask in CIDR notation.
+ *                      e.g. 192.168.1.0/24 or 2001:0db8::/32
+ * <ipv6_addr>      :== A single IPv6 address
+ * <ipv4_addr_range> :==
+ *     <numaddr_range>.<numaddr_range>.<numaddr_range>.<numaddr_range>
+ * <numaddr_range>   :== <number> |
  *                      <expr_list>
  * <expr_list>      :== '[' <range_expr> [ ',' <range_expr>] ']'
- * <range_expr>      :== <number> |
+ * <range_expr>             :== <number> |
  *                      <number> '-' <number> |
  *                      <number> '-' <number> '/' <number>
  * <net>            :== <netname> | <netname><number>
- * <netname>        :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
- *                      "vib" | "ra" | "elan" | "mx" | "ptl"
+ * <netname>        :== "lo" | "tcp" | "o2ib" | "gni" | "gip" | "ptlf" | "kfi"
  * \endverbatim
  */
 
@@ -98,6 +104,10 @@ struct nidrange {
         */
        struct list_head nr_addrranges;
        /**
+        * List head for nidmask::nm_link.
+        */
+       struct list_head nr_nidmasks;
+       /**
         * Flag indicating that *@<net> is found.
         */
        int nr_all;
@@ -111,6 +121,35 @@ struct nidrange {
        int nr_netnum;
 };
 
+struct nidmask {
+       /* Link to nidrange::nr_nidmasks */
+       struct list_head nm_link;
+
+       /* This is the base address that was parsed */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_addr;
+
+       /* Netmask derived from the prefix length */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_netmask;
+
+       /* Network address derived from the base address and the netmask */
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } nm_netaddr;
+
+       /* Address family */
+       sa_family_t nm_family;
+
+       /* Prefix length */
+       u8 nm_prefix_len;
+};
+
 /**
  * Structure to represent \<addrrange\> token of the syntax.
  */
@@ -415,6 +454,102 @@ parse_addrange(char *str, struct nidrange *nidrange)
                                                &addrrange->ar_numaddr_ranges);
 }
 
+static void
+init_ipv4_nidmask(struct sockaddr_in *sa, struct nidmask *nm)
+{
+       memcpy(&nm->nm_addr.ipv4, &sa->sin_addr.s_addr, sizeof(struct in_addr));
+       nm->nm_netmask.ipv4.s_addr = inet_make_mask(nm->nm_prefix_len);
+       nm->nm_netaddr.ipv4.s_addr = sa->sin_addr.s_addr &
+                                    nm->nm_netmask.ipv4.s_addr;
+}
+
+/* Note: The memory of struct nidmask is allocated via CFS_ALLOC_PTR, so it has
+ * been set to zero as required by this function.
+ */
+static void
+init_ipv6_nidmask(struct sockaddr_in6 *sa, struct nidmask *nm)
+{
+       int i, j;
+
+       memcpy(&nm->nm_addr.ipv6, &sa->sin6_addr.s6_addr,
+              sizeof(struct in6_addr));
+
+       for (i = nm->nm_prefix_len, j = 0; i > 0; i -= 8, j++) {
+               if (i >= 8)
+                       nm->nm_netmask.ipv6.s6_addr[j] = 0xff;
+               else
+                       nm->nm_netmask.ipv6.s6_addr[j] =
+                                       (unsigned long)(0xffU << (8 - i));
+       }
+
+       for (i = 0; i < sizeof(struct in6_addr); i++)
+               nm->nm_netaddr.ipv6.s6_addr[i] = sa->sin6_addr.s6_addr[i] &
+                                                nm->nm_netmask.ipv6.s6_addr[i];
+}
+
+static u8
+parse_prefix_len(char *str)
+{
+       unsigned int max;
+       unsigned int prefix_len;
+       char *slash = strchr(str, '/');
+
+       /* IPv4 netmask must include an explicit prefix length */
+       if (!(slash || strchr(str, ':')))
+               return 0;
+
+       /* We treat an IPv6 address without a prefix length as having /128 */
+       if (!slash)
+               return 128;
+
+       if (strchr(str, ':'))
+               max = 128;
+       else
+               max = 32;
+
+       if (kstrtouint(slash + 1, 10, &prefix_len))
+               return 0;
+
+       if (prefix_len < 1 || prefix_len > max)
+               return 0;
+
+       return (u8)prefix_len;
+}
+
+static int
+parse_nidmask(char *str, struct nidrange *nr)
+{
+       struct nidmask *nm;
+       struct sockaddr_storage sa;
+       char *addrstr;
+
+       CFS_ALLOC_PTR(nm);
+       if (!nm)
+               return -ENOMEM;
+
+       /* Add to nr_nidmasks so that our caller can free us on error */
+       list_add_tail(&nm->nm_link, &nr->nr_nidmasks);
+
+       nm->nm_prefix_len = parse_prefix_len(str);
+       if (!nm->nm_prefix_len)
+               return -EINVAL;
+
+       addrstr = strsep(&str, "/");
+       if (rpc_pton(&init_net, addrstr, strlen(addrstr),
+                    (struct sockaddr *)&sa, sizeof(sa)) == 0)
+               return -EINVAL;
+
+       nm->nm_family = sa.ss_family;
+       if (nm->nm_family == AF_INET6)
+               init_ipv6_nidmask((struct sockaddr_in6 *)&sa, nm);
+       else if (nm->nm_family == AF_INET)
+               init_ipv4_nidmask((struct sockaddr_in *)&sa, nm);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
 /**
  * Finds or creates struct nidrange.
  *
@@ -462,6 +597,7 @@ add_nidrange(char *str, struct list_head *nidlist)
                return NULL;
        list_add_tail(&nr->nr_link, nidlist);
        INIT_LIST_HEAD(&nr->nr_addrranges);
+       INIT_LIST_HEAD(&nr->nr_nidmasks);
        nr->nr_netstrfns = nf;
        nr->nr_all = 0;
        nr->nr_netnum = netnum;
@@ -480,26 +616,30 @@ parse_nidrange(char *str, struct list_head *nidlist)
 {
        char *addrrange;
        char *net;
+       char *slash;
        struct nidrange *nr;
+       int rc;
 
        addrrange = strim(strsep(&str, "@"));
        if (!str)
-               goto failed;
+               return -EINVAL;
 
        net = strim(str);
-       if (strchr(net, '@') != NULL || !*net)
-               goto failed;
+       if (strchr(net, '@') || !*net)
+               return -EINVAL;
 
        nr = add_nidrange(net, nidlist);
-       if (nr == NULL)
-               goto failed;
+       if (!nr)
+               return -EINVAL;
 
-       if (parse_addrange(addrrange, nr) != 0)
-               goto failed;
+       /* Check for a '/' that does not appear inside '[]' */
+       slash = strchr(addrrange, '/');
+       if (strchr(addrrange, ':') || (slash && !strchr(slash, ']')))
+               rc = parse_nidmask(addrrange, nr);
+       else
+               rc = parse_addrange(addrrange, nr);
 
-       return 0;
-failed:
-       return -EINVAL;
+       return rc;
 }
 
 /**
@@ -524,6 +664,19 @@ free_addrranges(struct list_head *list)
        }
 }
 
+static void
+free_nidmasks(struct list_head *list)
+{
+       struct nidmask *nm;
+
+       while ((nm = list_first_entry_or_null(list,
+                                             struct nidmask,
+                                             nm_link)) != NULL) {
+               list_del(&nm->nm_link);
+               CFS_FREE_PTR(nm);
+       }
+}
+
 /**
  * Frees nidrange strutures of \a list.
  *
@@ -541,6 +694,7 @@ cfs_free_nidlist(struct list_head *list)
        list_for_each_safe(pos, next, list) {
                nr = list_entry(pos, struct nidrange, nr_link);
                free_addrranges(&nr->nr_addrranges);
+               free_nidmasks(&nr->nr_nidmasks);
                list_del(pos);
                CFS_FREE_PTR(nr);
        }
@@ -561,12 +715,12 @@ EXPORT_SYMBOL(cfs_free_nidlist);
  * \retval -errno otherwise (-ENOMEM or -EINVAL)
  */
 int
-cfs_parse_nidlist(char *orig, struct list_head *nidlist)
+cfs_parse_nidlist(char *orig, int len, struct list_head *nidlist)
 {
        int rc = 0;
        char *str;
 
-       orig = kstrdup(orig, GFP_KERNEL);
+       orig = kstrndup(orig, len, GFP_KERNEL);
        if (!orig)
                return -ENOMEM;
 
@@ -587,6 +741,30 @@ cfs_parse_nidlist(char *orig, struct list_head *nidlist)
 }
 EXPORT_SYMBOL(cfs_parse_nidlist);
 
+static int
+match_nidmask(struct lnet_nid *nid, struct nidmask *nm, struct netstrfns *nf)
+{
+       __be32 addr[4] = { nid->nid_addr[0], nid->nid_addr[1],
+                          nid->nid_addr[2], nid->nid_addr[3] };
+       __be32 *netmask, *netaddr;
+
+       if (!nf->nf_match_netmask)
+               return 0;
+
+       if (nid_is_nid4(nid) && nm->nm_family == AF_INET) {
+               netmask = (__be32 *)&nm->nm_netmask.ipv4.s_addr;
+               netaddr = (__be32 *)&nm->nm_netaddr.ipv4.s_addr;
+       } else if (!nid_is_nid4(nid) && nm->nm_family == AF_INET6) {
+               netmask = (__be32 *)&nm->nm_netmask.ipv6.s6_addr;
+               netaddr = (__be32 *)&nm->nm_netaddr.ipv6.s6_addr;
+       } else {
+               return 0;
+       }
+
+       return nf->nf_match_netmask(addr, NID_ADDR_BYTES(nid), netmask,
+                                   netaddr);
+}
+
 /**
  * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
  *
@@ -598,10 +776,9 @@ EXPORT_SYMBOL(cfs_parse_nidlist);
 int cfs_match_nid(struct lnet_nid *nid, struct list_head *nidlist)
 {
        struct nidrange *nr;
+       struct nidmask *nm;
        struct addrrange *ar;
 
-       if (!nid_is_nid4(nid))
-               return 0;
        list_for_each_entry(nr, nidlist, nr_link) {
                if (nr->nr_netstrfns->nf_type != nid->nid_type)
                        continue;
@@ -609,6 +786,11 @@ int cfs_match_nid(struct lnet_nid *nid, struct list_head *nidlist)
                        continue;
                if (nr->nr_all)
                        return 1;
+
+               list_for_each_entry(nm, &nr->nr_nidmasks, nm_link)
+                       if (match_nidmask(nid, nm, nr->nr_netstrfns))
+                               return 1;
+
                list_for_each_entry(ar, &nr->nr_addrranges, ar_link)
                        if (nr->nr_netstrfns->nf_match_addr(
                                    be32_to_cpu(nid->nid_addr[0]),
@@ -660,6 +842,42 @@ cfs_print_addrranges(char *buffer, int count, struct list_head *addrranges,
        return i;
 }
 
+static int
+cfs_print_nidmasks(char *buffer, int count, struct list_head *nidmasks,
+                  struct nidrange *nr)
+{
+       int i = 0;
+       struct nidmask *nm;
+       struct sockaddr_storage sa = {};
+       u8 max;
+
+       list_for_each_entry(nm, nidmasks, nm_link) {
+               if (i != 0)
+                       i += scnprintf(buffer + i, count - i, " ");
+
+               /* parse_nidmask() ensures nm_family is set to either AF_INET
+                * or AF_INET6
+                */
+               sa.ss_family = nm->nm_family;
+               if (nm->nm_family == AF_INET) {
+                       memcpy(&((struct sockaddr_in *)(&sa))->sin_addr.s_addr,
+                              &nm->nm_addr.ipv4, sizeof(struct in_addr));
+                       max = 32;
+               } else {
+                       memcpy(&((struct sockaddr_in6 *)(&sa))->sin6_addr.s6_addr,
+                              &nm->nm_addr.ipv6, sizeof(struct in6_addr));
+                       max = 128;
+               }
+               i += rpc_ntop((struct sockaddr *)&sa, buffer + i, count - i);
+               if (nm->nm_prefix_len < max)
+                       i += scnprintf(buffer + i, count - i, "/%u",
+                                      nm->nm_prefix_len);
+               i += cfs_print_network(buffer + i, count - i, nr);
+       }
+
+       return i;
+}
+
 /**
  * Print a list of nidranges (\a nidlist) into the specified \a buffer.
  * At max \a count characters can be printed into \a buffer.
@@ -671,6 +889,7 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
 {
        int i = 0;
        struct nidrange *nr;
+       bool need_space = false;
 
        if (count <= 0)
                return 0;
@@ -679,14 +898,26 @@ int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
                if (i != 0)
                        i += scnprintf(buffer + i, count - i, " ");
 
-               if (nr->nr_all != 0) {
+               if (nr->nr_all) {
                        LASSERT(list_empty(&nr->nr_addrranges));
+                       LASSERT(list_empty(&nr->nr_nidmasks));
                        i += scnprintf(buffer + i, count - i, "*");
                        i += cfs_print_network(buffer + i, count - i, nr);
-               } else {
+                       continue;
+               }
+
+               if (!list_empty(&nr->nr_nidmasks)) {
+                       i += cfs_print_nidmasks(buffer + i, count - i,
+                                               &nr->nr_nidmasks, nr);
+                       need_space = true;
+               }
+               if (!list_empty(&nr->nr_addrranges)) {
+                       if (need_space)
+                               i += scnprintf(buffer + i, count - i, " ");
                        i += cfs_print_addrranges(buffer + i, count - i,
                                                  &nr->nr_addrranges, nr);
                }
+               need_space = false;
        }
        return i;
 }
@@ -923,6 +1154,43 @@ cfs_ip_addr_match(__u32 addr, struct list_head *list)
 }
 
 /**
+ * Matches address (\a addr) against the netmask encoded in \a netmask and
+ * \a netaddr.
+ *
+ * \retval 1 if \a addr matches
+ * \retval 0 otherwise
+ */
+int
+libcfs_ip_in_netmask(const __be32 *addr, size_t asize, const __be32 *netmask,
+                    const __be32 *netaddr)
+{
+       if (asize == 4) {
+               struct in_addr nid_addr, masked_addr;
+
+               memcpy(&nid_addr.s_addr, addr, asize);
+
+               masked_addr.s_addr = nid_addr.s_addr &
+                                    (*(struct in_addr *)netmask).s_addr;
+
+               return memcmp(&masked_addr, netaddr, sizeof(masked_addr)) == 0;
+       } else if (asize == 16) {
+               struct in6_addr nid_addr, masked_addr;
+               int i;
+
+               memcpy(&nid_addr.s6_addr, addr, asize);
+
+               for (i = 0; i < 16; i++)
+                       masked_addr.s6_addr[i] =
+                               nid_addr.s6_addr[i] &
+                               (*(struct in6_addr *)netmask).s6_addr[i];
+
+               return memcmp(&masked_addr, netaddr, sizeof(masked_addr)) == 0;
+       }
+
+       return 0;
+}
+
+/**
  * Print the network part of the nidrange \a nr into the specified \a buffer.
  *
  * \retval number of characters written
@@ -1023,7 +1291,8 @@ static struct netstrfns libcfs_netstrfns[] = {
          .nf_str2addr_size     = libcfs_ip_str2addr_size,
          .nf_parse_addrlist    = cfs_ip_addr_parse,
          .nf_print_addrlist    = libcfs_ip_addr_range_print,
-         .nf_match_addr        = cfs_ip_addr_match
+         .nf_match_addr        = cfs_ip_addr_match,
+         .nf_match_netmask     = libcfs_ip_in_netmask
        },
        { .nf_type              = O2IBLND,
          .nf_name              = "o2ib",
@@ -1032,7 +1301,8 @@ static struct netstrfns libcfs_netstrfns[] = {
          .nf_str2addr          = libcfs_ip_str2addr,
          .nf_parse_addrlist    = cfs_ip_addr_parse,
          .nf_print_addrlist    = libcfs_ip_addr_range_print,
-         .nf_match_addr        = cfs_ip_addr_match
+         .nf_match_addr        = cfs_ip_addr_match,
+         .nf_match_netmask     = libcfs_ip_in_netmask
        },
        { .nf_type              = GNILND,
          .nf_name              = "gni",
index 4151446..025903e 100644 (file)
@@ -230,10 +230,62 @@ void lustre_lnet_init_nw_descr(struct lnet_dlc_network_descr *nw_descr)
        }
 }
 
+int lustre_lnet_debug_nidlist(char *nidstr, char *match_nid)
+{
+       struct list_head nidlist;
+       int rc;
+       char *buf = NULL;
+       size_t bsize = strlen(nidstr) + 1;
+
+       buf = malloc(bsize);
+       if (!buf) {
+               fprintf(stderr, "Cannot allocate memory for buffer\n");
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&nidlist);
+       rc = cfs_parse_nidlist(nidstr, strlen(nidstr), &nidlist);
+       if (!rc) {
+               fprintf(stderr, "Unable to parse nidlist from: %s\n", nidstr);
+               goto failed;
+       }
+
+       if (cfs_print_nidlist(buf, bsize, &nidlist))
+               printf("nidlist parsed to: %s\n", buf);
+
+       if (match_nid) {
+               struct lnet_nid nid;
+
+               rc = libcfs_strnid(&nid, match_nid);
+               if (rc < 0) {
+                       fprintf(stderr, "Invalid nid for matching %s\n",
+                               match_nid);
+                       goto failed;
+               }
+
+               rc = cfs_match_nid(&nid, &nidlist);
+               if (rc)
+                       printf("%s matches nidlist %s\n", match_nid, nidstr);
+               else
+                       printf("%s does not match nidlist %s\n", match_nid,
+                              nidstr);
+       }
+
+       cfs_free_nidlist(&nidlist);
+       free(buf);
+
+       return 0;
+failed:
+       cfs_free_nidlist(&nidlist);
+       if (buf)
+               free(buf);
+       return LUSTRE_CFG_RC_BAD_PARAM;
+}
+
 int lustre_lnet_parse_nidstr(char *nidstr, lnet_nid_t *lnet_nidlist,
                             int max_nids, char *err_str)
 {
-       int rc, num_nids = 0;
+       int num_nids = 0;
        struct list_head nidlist;
 
        if (!nidstr) {
@@ -248,19 +300,12 @@ int lustre_lnet_parse_nidstr(char *nidstr, lnet_nid_t *lnet_nidlist,
        }
 
        INIT_LIST_HEAD(&nidlist);
-       rc = cfs_parse_nidlist(nidstr, strlen(nidstr), &nidlist);
-       if (rc == 0) {
+       if (!cfs_parse_nidlist(nidstr, strlen(nidstr), &nidlist)) {
                snprintf(err_str, LNET_MAX_STR_LEN,
                         "Unable to parse nidlist from: %s\n", nidstr);
                return LUSTRE_CFG_RC_BAD_PARAM;
        }
 
-       if (list_empty(&nidlist)) {
-               snprintf(err_str, LNET_MAX_STR_LEN,
-                        "\"%s\" does not specify any valid nid lists", nidstr);
-               return LUSTRE_CFG_RC_BAD_PARAM;
-       }
-
        num_nids = cfs_expand_nidlist(&nidlist, lnet_nidlist, max_nids);
        cfs_free_nidlist(&nidlist);
 
index ca47f30..7d00dc5 100644 (file)
@@ -967,6 +967,7 @@ int lustre_lnet_parse_interfaces(char *intf_str,
 int lustre_lnet_parse_nidstr(char *nidstr, lnet_nid_t *lnet_nidlist,
                             int max_nids, char *err_str);
 
+int lustre_lnet_debug_nidlist(char *nidstr, char *match_nid);
 /* lustre_lnet_add_udsp
  *     Add a selection policy.
  *     src - source NID descriptor
index 0120a47..701696d 100644 (file)
@@ -43,6 +43,7 @@ static int jt_show_routing(int argc, char **argv);
 static int jt_show_stats(int argc, char **argv);
 static int jt_show_peer(int argc, char **argv);
 static int jt_show_recovery(int argc, char **argv);
+static int jt_debug_nidlist(int argc, char **argv);
 static int jt_show_global(int argc, char **argv);
 static int jt_show_udsp(int argc, char **argv);
 static int jt_show_fault(int argc, char **argv);
@@ -199,6 +200,7 @@ command_t debug_cmds[] = {
                "\t--peer : list peer recovery queue\n"},
        {"peer", jt_show_peer_debug_info, 0, "show peer debug info\n"
                "\t--nid: peer's NID\n"},
+       {"nidlist", jt_debug_nidlist, 0, "debug nidlist parsing\n"},
        { 0, 0, 0, NULL }
 };
 
@@ -3662,6 +3664,25 @@ old_api:
        return rc;
 }
 
+static int jt_debug_nidlist(int argc, char **argv)
+{
+       int rc;
+
+       if (argc < 2) {
+               fprintf(stderr, "Missing nidlist argument\n");
+               return -EINVAL;
+       } else if (argc == 2) {
+               rc = lustre_lnet_debug_nidlist(argv[1], NULL);
+       } else if (argc == 3) {
+               rc = lustre_lnet_debug_nidlist(argv[1], argv[2]);
+       } else {
+               fprintf(stderr, "Too many arguments\n");
+               return -EINVAL;
+       }
+
+       return rc;
+}
+
 static int jt_show_peer_debug_info(int argc, char **argv)
 {
        int rc, opt;
index 18174af..ad79eef 100644 (file)
@@ -2344,7 +2344,7 @@ int lprocfs_wr_nosquash_nids(const char __user *buffer, unsigned long count,
                RETURN(count);
        }
 
-       if (cfs_parse_nidlist(kernbuf, &tmp) < 0) {
+       if (cfs_parse_nidlist(kernbuf, strlen(kernbuf), &tmp)) {
                errmsg = "can't parse";
                GOTO(failed, rc = -EINVAL);
        }
index d117eaa..975d0fc 100644 (file)
@@ -1444,21 +1444,11 @@ static bool lmd_parse_nidlist(char *buf)
                end += pos;
                c = end[0];
                end[0] = '\0';
-               /* FIXME !!! Add IPv6 support to cfs_parse_nidlist */
-               if (strchr(buf, ':')) {
-                       struct lnet_nid nid;
-
-                       if (libcfs_strnid(&nid, buf) < 0) {
-                               invalid = true;
-                               goto failed;
-                       }
+               if (cfs_parse_nidlist(buf, strlen(buf), &nidlist)) {
+                       invalid = true;
+                       goto failed;
                } else {
-                       if (cfs_parse_nidlist(buf, &nidlist) < 0) {
-                               invalid = true;
-                               goto failed;
-                       } else {
-                               cfs_free_nidlist(&nidlist);
-                       }
+                       cfs_free_nidlist(&nidlist);
                }
                end[0] = c;
                end++;
index ccbcdf7..aba3be4 100644 (file)
@@ -1195,24 +1195,21 @@ static int nrs_tbf_nid_rule_init(struct ptlrpc_nrs_policy *policy,
                                 struct nrs_tbf_rule *rule,
                                 struct nrs_tbf_cmd *start)
 {
+       size_t len = strlen(start->u.tc_start.ts_nids_str);
+
        LASSERT(start->u.tc_start.ts_nids_str);
-       OBD_ALLOC(rule->tr_nids_str,
-                 strlen(start->u.tc_start.ts_nids_str) + 1);
-       if (rule->tr_nids_str == NULL)
-               return -ENOMEM;
 
-       memcpy(rule->tr_nids_str,
-              start->u.tc_start.ts_nids_str,
-              strlen(start->u.tc_start.ts_nids_str));
+       rule->tr_nids_str = kstrndup(start->u.tc_start.ts_nids_str,
+                                    len, GFP_KERNEL);
+       if (!rule->tr_nids_str)
+               return -ENOMEM;
 
        INIT_LIST_HEAD(&rule->tr_nids);
        if (!list_empty(&start->u.tc_start.ts_nids)) {
-               if (cfs_parse_nidlist(rule->tr_nids_str,
-                                     &rule->tr_nids) < 0) {
+               if (cfs_parse_nidlist(rule->tr_nids_str, len, &rule->tr_nids)) {
                        CERROR("nids {%s} illegal\n",
                               rule->tr_nids_str);
-                       OBD_FREE(rule->tr_nids_str,
-                                strlen(start->u.tc_start.ts_nids_str) + 1);
+                       kfree(rule->tr_nids_str);
                        return -EINVAL;
                }
        }
@@ -1254,20 +1251,21 @@ static void nrs_tbf_nid_cmd_fini(struct nrs_tbf_cmd *cmd)
 static int nrs_tbf_nid_parse(struct nrs_tbf_cmd *cmd, char *id)
 {
        int rc;
+       size_t len;
 
        rc = nrs_tbf_check_id_value(&id, "nid");
        if (rc)
                return rc;
 
-       OBD_ALLOC(cmd->u.tc_start.ts_nids_str, strlen(id) + 1);
-       if (cmd->u.tc_start.ts_nids_str == NULL)
-               return -ENOMEM;
+       len = strlen(id);
 
-       strcpy(cmd->u.tc_start.ts_nids_str, id);
+       cmd->u.tc_start.ts_nids_str = kstrndup(id, len, GFP_KERNEL);
+       if (!cmd->u.tc_start.ts_nids_str)
+               return -ENOMEM;
 
        /* parse NID list */
-       if (cfs_parse_nidlist(cmd->u.tc_start.ts_nids_str,
-                             &cmd->u.tc_start.ts_nids) < 0) {
+       if (cfs_parse_nidlist(cmd->u.tc_start.ts_nids_str, len,
+                             &cmd->u.tc_start.ts_nids)) {
                nrs_tbf_nid_cmd_fini(cmd);
                return -EINVAL;
        }
@@ -1867,7 +1865,7 @@ nrs_tbf_expression_parse(char *str, struct list_head *cond_list)
        len -= 2;
 
        if (strcmp(field, "nid") == 0) {
-               if (cfs_parse_nidlist(str, &expr->te_cond) < 0)
+               if (cfs_parse_nidlist(str, len, &expr->te_cond) < 0)
                        GOTO(out, rc = -EINVAL);
                expr->te_field = NRS_TBF_FIELD_NID;
        } else if (strcmp(field, "jobid") == 0) {
index 9fca587..b3396c8 100755 (executable)
@@ -4271,6 +4271,41 @@ test_42() { #bug 14693
 }
 run_test 42 "allow client/server mount/unmount with invalid config param"
 
+test_43a_check_nosquash_nids() {
+       local nidlist="$1"
+
+       set_persistent_param_and_check mds1                             \
+               "mdt.$FSNAME-MDT0000.nosquash_nids"                     \
+               "$FSNAME-MDTall.mdt.nosquash_nids"                      \
+               "$nidlist"
+       wait_update $HOSTNAME                                           \
+               "$LCTL get_param -n llite.${FSNAME}*.nosquash_nids"     \
+               "$nidlist" ||
+               error "check llite nosquash_nids failed!"
+
+       ST=$(stat -c "%n: owner uid %u (%A)" $DIR/$tfile-rootfile)
+       dd if=$DIR/$tfile-rootfile 1>/dev/null 2>/dev/null ||
+               error "$ST: root read permission is denied"
+       echo "$ST: root read permission is granted - ok"
+
+       echo "666" |
+       dd conv=notrunc of=$DIR/$tfile-rootfile 1>/dev/null 2>/dev/null ||
+               error "$ST: root write permission is denied"
+       echo "$ST: root write permission is granted - ok"
+
+       ST=$(stat -c "%n: owner uid %u (%A)" $DIR/$tdir-rootdir)
+       rm $DIR/$tdir-rootdir/tfile-1 ||
+               error "$ST: root unlink permission is denied"
+       echo "$ST: root unlink permission is granted - ok"
+       touch $DIR/$tdir-rootdir/tfile-2 ||
+               error "$ST: root create permission is denied"
+       echo "$ST: root create permission is granted - ok"
+
+       # Re-create test file deleted above in case this function is called
+       # again
+       touch $DIR/$tdir-rootdir/tfile-1 || error "touch failed"
+}
+
 test_43a() {
        [[ "$MGS_VERSION" -ge $(version_code 2.5.58) ]] ||
                skip "Need MDS version at least 2.5.58"
@@ -4389,35 +4424,44 @@ test_43a() {
        #   put client's NID into nosquash_nids list,
        #   root should be able to access root file after that
        #
-       local NIDLIST=$($LCTL list_nids all | tr '\n' ' ')
-       NIDLIST="2@gni $NIDLIST 192.168.0.[2,10]@tcp"
-       NIDLIST=$(echo $NIDLIST | tr -s ' ' ' ')
-       set_persistent_param_and_check mds1                             \
-               "mdt.$FSNAME-MDT0000.nosquash_nids"                     \
-               "$FSNAME-MDTall.mdt.nosquash_nids"                      \
-               "$NIDLIST"
-       wait_update $HOSTNAME                                           \
-               "$LCTL get_param -n llite.${FSNAME}*.nosquash_nids"     \
-               "$NIDLIST" ||
-               error "check llite nosquash_nids failed!"
+       local nidlist=$($LCTL list_nids all | tr '\n' ' ')
+       nidlist="2@gni $nidlist 192.168.0.[2,10]@tcp"
+       nidlist=$(echo $nidlist | tr -s ' ' ' ')
 
-       ST=$(stat -c "%n: owner uid %u (%A)" $DIR/$tfile-rootfile)
-       dd if=$DIR/$tfile-rootfile 1>/dev/null 2>/dev/null ||
-               error "$ST: root read permission is denied"
-       echo "$ST: root read permission is granted - ok"
+       test_43a_check_nosquash_nids "$nidlist"
 
-       echo "666" |
-       dd conv=notrunc of=$DIR/$tfile-rootfile 1>/dev/null 2>/dev/null ||
-               error "$ST: root write permission is denied"
-       echo "$ST: root write permission is granted - ok"
+       if ! [[ $NETTYPE =~ ^(tcp|o2ib) ]]; then
+               log "Skip nidmask test for NETTYPE = $NETTYPE"
+               cleanup || error "cleanup failed with $?"
+               return 0
+       fi
+
+       # check nosquash_nids:
+       #   create a nidmask that contains the client's NID and place it
+       #   into nosquash_nids list.
+       #   root should be able to access root file after that
+       local interfaces=( $(lnet_if_list) )
+       local intf netmasks nm
+
+       for intf in ${interfaces[@]}; do
+               nm=$(ip -o -4 a s ${intf} | awk '{print $4}')
+               [[ -n $nm ]] && netmasks+=" $nm@${NETTYPE}"
+               nm=$(ip -o -6 a s ${intf} | grep -v 'fe80::' | awk '{print $4}')
+               [[ -n $nm ]] && netmasks+=" $nm@${NETTYPE}"
+       done
+
+       netmasks="${netmasks/ }"
+
+       if [[ -z $netmasks ]]; then
+               error "Unable to determine netmasks for ${interfaces[@]}"
+       fi
+
+       test_43a_check_nosquash_nids "$netmasks"
+
+       # cleanup test dir/files
+       rm -rf $DIR/$tfile-* $DIR/$tdir-rootdir ||
+               error "Failed to remove test files/dir rc = $?"
 
-       ST=$(stat -c "%n: owner uid %u (%A)" $DIR/$tdir-rootdir)
-       rm $DIR/$tdir-rootdir/tfile-1 ||
-               error "$ST: root unlink permission is denied"
-       echo "$ST: root unlink permission is granted - ok"
-       touch $DIR/$tdir-rootdir/tfile-2 ||
-               error "$ST: root create permission is denied"
-       echo "$ST: root create permission is granted - ok"
        cleanup || error "cleanup failed with $?"
 }
 run_test 43a "check root_squash and nosquash_nids"
index 312a503..b8cd40e 100644 (file)
@@ -4181,8 +4181,8 @@ static int parse_nid_range(char *nodemap_range, char *nid_range, int range_len)
 
        INIT_LIST_HEAD(&nidlist);
 
-       if (cfs_parse_nidlist(nodemap_range, strlen(nodemap_range),
-                             &nidlist) <= 0) {
+       if (!cfs_parse_nidlist(nodemap_range, strlen(nodemap_range),
+                              &nidlist)) {
                fprintf(stderr,
                        "error: nodemap_xxx_range: can't parse nid range: %s\n",
                        nodemap_range);