Whamcloud - gitweb
LU-10391 obd: Update lmd_parse to handle IPv6 NIDs 82/53882/4
authorChris Horn <chris.horn@hpe.com>
Wed, 31 Jan 2024 04:00:47 +0000 (22:00 -0600)
committerOleg Drokin <green@whamcloud.com>
Wed, 13 Mar 2024 03:21:06 +0000 (03:21 +0000)
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 <chris.horn@hpe.com>
Change-Id: Ib9e5a0d161babfea368989dd9521d923ec592185
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/53882
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Neil Brown <neilb@suse.de>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/obdclass/obd_config.c
lustre/obdclass/obd_mount.c
lustre/osd-ldiskfs/osd_handler.c

index 72947f8..37d3563 100644 (file)
@@ -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);
 
index 1d4a772..331a031 100644 (file)
@@ -40,6 +40,7 @@
 #define D_MOUNT (D_SUPER|D_CONFIG/*|D_WARNING */)
 #define PRINT_CMD CDEBUG
 
+#include <linux/inet.h>
 #include <linux/random.h>
 #include <linux/uuid.h>
 #include <linux/version.h>
@@ -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 :::<foo>. 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;
 }
 
index 6b8fe72..49b6591 100644 (file)
@@ -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]);