From 4b12a9dcaf7ef08ec37c9209c31e44623f57548c Mon Sep 17 00:00:00 2001 From: Chris Horn Date: Fri, 26 Jul 2024 14:36:51 -0600 Subject: [PATCH] LU-14288 lnet: Introduce nidmasks 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 Change-Id: Id9d0bc6f4f8b977591f0b6f88bda46ae03cb58d5 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55922 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin Reviewed-by: Neil Brown Reviewed-by: Serguei Smirnov --- libcfs/include/libcfs/util/string.h | 7 + libcfs/libcfs/util/nidstrings.c | 453 ++++++++++++++++++++++++++++------ libcfs/libcfs/util/string.c | 24 ++ lnet/include/lnet/lib-types.h | 3 + lnet/include/uapi/linux/lnet/nidstr.h | 7 +- lnet/lnet/nidstrings.c | 326 +++++++++++++++++++++--- lnet/utils/lnetconfig/liblnetconfig.c | 63 ++++- lnet/utils/lnetconfig/liblnetconfig.h | 1 + lnet/utils/lnetctl.c | 21 ++ lustre/obdclass/lprocfs_status.c | 2 +- lustre/obdclass/obd_mount.c | 18 +- lustre/ptlrpc/nrs_tbf.c | 34 ++- lustre/tests/conf-sanity.sh | 96 +++++-- lustre/utils/obd.c | 4 +- 14 files changed, 876 insertions(+), 183 deletions(-) diff --git a/libcfs/include/libcfs/util/string.h b/libcfs/include/libcfs/util/string.h index 97edf59..d484d10 100644 --- a/libcfs/include/libcfs/util/string.h +++ b/libcfs/include/libcfs/util/string.h @@ -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 diff --git a/libcfs/libcfs/util/nidstrings.c b/libcfs/libcfs/util/nidstrings.c index 2b11313..d892b8b 100644 --- a/libcfs/libcfs/util/nidstrings.c +++ b/libcfs/libcfs/util/nidstrings.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -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 * - * :== [ ' ' ] - * :== '@' - * :== '*' | - * | - * - * :== ... - * - * :== | - * - * :== '[' [ ',' ] ']' - * :== | - * '-' | - * '-' '/' - * :== | - * :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" | - * "vib" | "ra" | "elan" | "mx" | "ptl" + * :== [ ' ' ] + * :== '@' + * :== '*' | + * | + * | + * | + * + * :== An IPv4 or IPv6 network mask in CIDR notation. + * e.g. 192.168.1.0/24 or 2001:0db8::/32 + * :== A single IPv6 address + * :== + * ... + * :== | + * + * :== '[' [ ',' ] ']' + * :== | + * '-' | + * '-' '/' + * :== | + * :== "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 *@ is found. */ int nr_all; @@ -1096,6 +1143,38 @@ struct nidrange { }; /** + * Structure to represent \ 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 \ 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 \ token of the syntax. * - * \retval 1 if \a src parses to \ '@' \ - * \retval 0 otherwise + * \retval 0 if \a src parses to \ '@' \ + * \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; } diff --git a/libcfs/libcfs/util/string.c b/libcfs/libcfs/util/string.c index b3e3209..3f3aae6 100644 --- a/libcfs/libcfs/util/string.c +++ b/libcfs/libcfs/util/string.c @@ -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); +} diff --git a/lnet/include/lnet/lib-types.h b/lnet/include/lnet/lib-types.h index 9396b88..6e8d5b9 100644 --- a/lnet/include/lnet/lib-types.h +++ b/lnet/include/lnet/lib-types.h @@ -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); }; diff --git a/lnet/include/uapi/linux/lnet/nidstr.h b/lnet/include/uapi/linux/lnet/nidstr.h index 23e57e9..f35a4b5 100644 --- a/lnet/include/uapi/linux/lnet/nidstr.h +++ b/lnet/include/uapi/linux/lnet/nidstr.h @@ -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); diff --git a/lnet/lnet/nidstrings.c b/lnet/lnet/nidstrings.c index 6e409ad..b960725 100644 --- a/lnet/lnet/nidstrings.c +++ b/lnet/lnet/nidstrings.c @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include /* 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 * * :== [ ' ' ] * :== '@' * :== '*' | - * | - * - * :== ... - * - * :== | + * | + * | + * | + * + * :== An IPv4 or IPv6 network mask in CIDR notation. + * e.g. 192.168.1.0/24 or 2001:0db8::/32 + * :== A single IPv6 address + * :== + * ... + * :== | * * :== '[' [ ',' ] ']' - * :== | + * :== | * '-' | * '-' '/' * :== | - * :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" | - * "vib" | "ra" | "elan" | "mx" | "ptl" + * :== "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 *@ 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 \ 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", diff --git a/lnet/utils/lnetconfig/liblnetconfig.c b/lnet/utils/lnetconfig/liblnetconfig.c index 4151446..025903e 100644 --- a/lnet/utils/lnetconfig/liblnetconfig.c +++ b/lnet/utils/lnetconfig/liblnetconfig.c @@ -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); diff --git a/lnet/utils/lnetconfig/liblnetconfig.h b/lnet/utils/lnetconfig/liblnetconfig.h index ca47f30..7d00dc5 100644 --- a/lnet/utils/lnetconfig/liblnetconfig.h +++ b/lnet/utils/lnetconfig/liblnetconfig.h @@ -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 diff --git a/lnet/utils/lnetctl.c b/lnet/utils/lnetctl.c index 0120a47..701696d 100644 --- a/lnet/utils/lnetctl.c +++ b/lnet/utils/lnetctl.c @@ -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; diff --git a/lustre/obdclass/lprocfs_status.c b/lustre/obdclass/lprocfs_status.c index 18174af..ad79eef 100644 --- a/lustre/obdclass/lprocfs_status.c +++ b/lustre/obdclass/lprocfs_status.c @@ -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); } diff --git a/lustre/obdclass/obd_mount.c b/lustre/obdclass/obd_mount.c index d117eaa..975d0fc 100644 --- a/lustre/obdclass/obd_mount.c +++ b/lustre/obdclass/obd_mount.c @@ -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++; diff --git a/lustre/ptlrpc/nrs_tbf.c b/lustre/ptlrpc/nrs_tbf.c index ccbcdf7..aba3be4 100644 --- a/lustre/ptlrpc/nrs_tbf.c +++ b/lustre/ptlrpc/nrs_tbf.c @@ -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) { diff --git a/lustre/tests/conf-sanity.sh b/lustre/tests/conf-sanity.sh index 9fca587..b3396c8 100755 --- a/lustre/tests/conf-sanity.sh +++ b/lustre/tests/conf-sanity.sh @@ -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" diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index 312a503..b8cd40e 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -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); -- 1.8.3.1