From 11d851c51c6ce3893aaea705f000680ef04737da Mon Sep 17 00:00:00 2001 From: Chris Horn Date: Tue, 30 Jan 2024 22:00:47 -0600 Subject: [PATCH] LU-10391 obd: Update lmd_parse to handle IPv6 NIDs lmd_find_delimiter()/lmd_parse_nidlist() are updated to handle IPv6 NIDs. class_parse_value() is updated to handle IPv6 NIDs that begin with '::'. Also, when parsing a Lustre network name, the buffer needn't contain an '@', nor do we want to search for '@' when locating the next delimiter. Fixes: 101f6e8 ("LU-10391 obdclass: handle large NIDs for mount strings") Signed-off-by: Chris Horn Change-Id: Ib9e5a0d161babfea368989dd9521d923ec592185 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/53882 Tested-by: jenkins Tested-by: Maloo Reviewed-by: James Simmons Reviewed-by: Neil Brown Reviewed-by: Oleg Drokin --- lustre/obdclass/obd_config.c | 30 +++++-- lustre/obdclass/obd_mount.c | 168 ++++++++++++++++++++++++++++++--------- lustre/osd-ldiskfs/osd_handler.c | 1 + 3 files changed, 156 insertions(+), 43 deletions(-) diff --git a/lustre/obdclass/obd_config.c b/lustre/obdclass/obd_config.c index 72947f8..37d3563 100644 --- a/lustre/obdclass/obd_config.c +++ b/lustre/obdclass/obd_config.c @@ -443,25 +443,41 @@ static int class_parse_value(char *buf, int opc, void *value, char **endh, int quiet) { char *endp; - char tmp; - int rc = 0; + char tmp; + int rc = 0; + int ncolons = 0; if (!buf) return 1; - while (*buf == ',' || *buf == ':') + while (*buf == ',' || *buf == ':') { + if (*buf == ':') + ncolons++; + else + ncolons = 0; buf++; + } + + /* IPv6 addresses can start with '::' */ + if (opc == CLASS_PARSE_NID && ncolons >= 2) + buf = buf - 2; + if (*buf == ' ' || *buf == '/' || *buf == '\0') return 1; /* NID separators or end of NIDs. Large NIDs can contain ':' so * skip ahead to @ and then look for one of the delimiters. */ - endp = strchr(buf, '@'); - if (!endp) - return 1; + if (opc == CLASS_PARSE_NID) { + endp = strchr(buf, '@'); + if (!endp) + return 1; + + endp = strpbrk(endp, ",: /"); + } else { + endp = strpbrk(buf, ",: /"); + } - endp = strpbrk(endp, ",: /"); if (!endp) endp = buf + strlen(buf); diff --git a/lustre/obdclass/obd_mount.c b/lustre/obdclass/obd_mount.c index 1d4a772..331a031 100644 --- a/lustre/obdclass/obd_mount.c +++ b/lustre/obdclass/obd_mount.c @@ -40,6 +40,7 @@ #define D_MOUNT (D_SUPER|D_CONFIG/*|D_WARNING */) #define PRINT_CMD CDEBUG +#include #include #include #include @@ -1226,8 +1227,9 @@ static int lmd_parse_mgs(struct lustre_mount_data *lmd, char **ptr) /** * Find the first delimiter (comma or colon) from the specified \a buf and - * make \a *endh point to the string starting with the delimiter. The commas - * in expression list [...] will be skipped. + * make \a *endh point to the string starting with the delimiter. The commas and + * colons in expression list [...] will be skipped. Colons that are part of a + * NID address are not considered delimiters. * * @buf a delimiter-separated string * @endh a pointer to a pointer that will point to the string @@ -1237,44 +1239,103 @@ static int lmd_parse_mgs(struct lustre_mount_data *lmd, char **ptr) */ static bool lmd_find_delimiter(char *buf, char **endh) { - char *c = buf; - size_t pos; - bool found; - - if (!buf) + char *c; + char *at = NULL; + char *colon = NULL; + char *delim = NULL; + int brackets = 0; + int ncolons = 0; + + if (!buf || *buf == '\0') return false; -try_again: - if (*c == ',' || *c == ':') - return true; - pos = strcspn(c, "[:,]"); - if (!pos) - return false; + c = buf; + do { + if (*c == '[') { + brackets++; + continue; + } - /* Not a valid mount string */ - if (*c == ']') { - CWARN("invalid mount string format\n"); - return false; - } + if (*c == ']') { + brackets--; + if (brackets < 0) { + CWARN("imbalanced brackets detected in \"%s\"", + buf); + return false; + } + continue; + } + + /* Ignore all characters inside brackets */ + if (brackets > 0) + continue; - c += pos; - if (*c == '[') { - c = strchr(c, ']'); + if (*c == ':') { + if (at) { + /* We previously saw an '@', so this ':' is a + * delimiter separating NIDs + */ + delim = c; + break; + } - /* invalid mount string */ - if (!c) { - CWARN("invalid mount string format\n"); - return false; + /* Record the first ':' that we find, as this may be a + * delimiter + */ + if (!colon) + colon = c; + + ncolons++; + if (ncolons > 2) { + /* Something like :::. The first ':' is a + * delimiter + */ + delim = colon; + break; + } + continue; + } + + ncolons = 0; + if (*c == '@') { + at = c; + } else if (*c == ',') { + /* When we find a ',', we can determine whether a ':' + * seen earlier was a delimiter or part of a NID + */ + if (colon && !at) { + /* We previously saw a ':', but never + * encountered an '@'. Thus, the ':' was a + * delimiter + */ + delim = colon; + } else { + /* We either never saw a ':', or we saw both a + * ':' and an '@' (so the ':' was part of a + * NID). In both cases this ',' is our delimiter + */ + delim = c; + } + break; } - c++; - goto try_again; + } while (c++ && *c != '\0'); + + + if (brackets != 0) { + CWARN("imbalanced brackets detected in \"%s\"", buf); + return false; } - found = *c != '\0'; - if (found && endh) - *endh = c; + if (!delim && colon && !at) + /* We saw a ':' but reached end of string without finding an '@' + * or ','. Thus, the ':' is considered a delimiter + */ + delim = colon; + + if (delim && endh) + *endh = delim; - return found; + return delim != NULL; } /** @@ -1295,11 +1356,23 @@ static int lmd_parse_nidlist(char *buf, char **endh) char *endp = buf; char tmp; int rc = 0; + int ncolons = 0; - if (buf == NULL) + if (!buf) return 1; - while (*buf == ',' || *buf == ':') + + while (*buf == ',' || *buf == ':') { + if (*buf == ':') + ncolons++; + else + ncolons = 0; buf++; + } + + /* IPv6 addresses can start with '::' */ + if (ncolons >= 2) + buf = buf - 2; + if (*buf == ' ' || *buf == '/' || *buf == '\0') return 1; @@ -1309,15 +1382,38 @@ static int lmd_parse_nidlist(char *buf, char **endh) tmp = *endp; *endp = '\0'; - if (cfs_parse_nidlist(buf, &nidlist) < 0) + /* FIXME: cfs_parse_nidlist does not support IPv6 addresses, so if + * buf contains a ':' and an '@' then we assume this is supposed to be + * an IPv6 address. + */ + if (strchr(buf, ':')) { + struct in6_addr addr; + char *at; + + rc = 1; + at = strchr(buf, '@'); + if (at) { + *at = '\0'; + rc = in6_pton(buf, -1, (u8 *)&addr.s6_addr, -1, NULL); + if (rc == 1) + rc = 0; + *at = '@'; + } + } else if (cfs_parse_nidlist(buf, &nidlist) < 0) { rc = 1; + } + cfs_free_nidlist(&nidlist); + /* Restore the delimiter */ *endp = tmp; - if (rc != 0) + + if (rc) return rc; - if (endh != NULL) + + if (endh) *endh = endp; + return 0; } diff --git a/lustre/osd-ldiskfs/osd_handler.c b/lustre/osd-ldiskfs/osd_handler.c index 6b8fe72..49b6591 100644 --- a/lustre/osd-ldiskfs/osd_handler.c +++ b/lustre/osd-ldiskfs/osd_handler.c @@ -8397,6 +8397,7 @@ static int osd_mount(const struct lu_env *env, "resetoi", NULL }; + strncat(options, opts, PAGE_SIZE); for (rc = 0, str = options; sout[rc]; ) { char *op = strstr(str, sout[rc]); -- 1.8.3.1