]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Accept IPv6 netmasks like /dead:beef::, in addition to /N
authorDaniel Lenski <dlenski@gmail.com>
Mon, 12 Apr 2021 23:49:00 +0000 (16:49 -0700)
committerDavid Woodhouse <dwmw2@infradead.org>
Tue, 13 Apr 2021 09:37:55 +0000 (10:37 +0100)
Some F5 server configurations appear to require an IPv6 netmask in this
form.

This adds a netmasklen6() function, analogous to the Legacy IP version in
netmasklen().  It also adds gcc-optimized forms, using __builtin_clz, for
both netmasklen functions.

[dwmw2: Detect __builtin_clz() with autoconf, wrap it once in cls()]
Signed-off-by: Daniel Lenski <dlenski@gmail.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
configure.ac
script.c

index 26d47ca95281cc12b35322ced58a4d86d44290bf..3482c6707bfc41f3826e17d10d2c6973012c582c 100644 (file)
@@ -174,6 +174,12 @@ AC_CHECK_FUNC(asprintf, [AC_DEFINE(HAVE_ASPRINTF, 1, [Have asprintf() function])
 AC_CHECK_FUNC(vasprintf, [AC_DEFINE(HAVE_VASPRINTF, 1, [Have vasprintf() function])],
     [symver_vasprintf="openconnect__vasprintf;"])
 
+AC_MSG_CHECKING([for __builtin_clz])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([],[return __builtin_clz(0xffffffff);])],
+               [AC_MSG_RESULT(yes)
+                AC_DEFINE(HAVE_BUILTIN_CLZ, 1, [Have __builtin_clz()])],
+               [AC_MSG_RESULT(no)])
+
 if test -n "$symver_vasprintf"; then
   AC_MSG_CHECKING([for va_copy])
   AC_LINK_IFELSE([AC_LANG_PROGRAM([
index 1817aeb5a49a6846bdffb6c3c3b18d03ba2d11fe..17cda887d5f3325004694e0902f40464b730ed29 100644 (file)
--- a/script.c
+++ b/script.c
@@ -68,15 +68,40 @@ int script_setenv_int(struct openconnect_info *vpninfo, const char *opt, int val
        return script_setenv(vpninfo, opt, buf, 0, 0);
 }
 
+static inline int cls(uint32_t word)
+{
+#if defined(HAVE_BUILTIN_CLZ) && UINT_MAX == UINT32_MAX
+       word = ~word;
+       if (!word)
+               return 32;
+       return __builtin_clz(word);
+#else
+       int masklen = 0;
+       while (word & 0x80000000) {
+               word <<= 1;
+               masklen++;
+       }
+       return masklen;
+#endif
+}
+
 static int netmasklen(struct in_addr addr)
+{
+       return cls(ntohl(addr.s_addr));
+}
+
+static int netmasklen6(struct in6_addr *addr)
 {
        int masklen;
+       uint32_t *p = (uint32_t *)(addr->s6_addr);
 
-       for (masklen = 0; masklen < 32; masklen++) {
-               if (ntohl(addr.s_addr) >= (0xffffffff << masklen))
-                       break;
+       for (masklen = 0; masklen < 128; p++, masklen += 32) {
+               uint32_t v = ntohl(*p);
+               if (~v == 0)
+                       continue;
+               return masklen + cls(v);
        }
-       return 32 - masklen;
+       return 128;
 }
 
 static uint32_t netmaskbits(int masklen)
@@ -106,10 +131,30 @@ static int process_split_xxclude(struct openconnect_info *vpninfo,
                         *v6_incs);
                script_setenv(vpninfo, envname, route, slash ? slash - route : 0, 0);
 
+               /* Accept IPv6 netmask in several forms */
                snprintf(envname, 79, "CISCO_IPV6_SPLIT_%sC_%d_MASKLEN", in_ex,
                         *v6_incs);
-               script_setenv(vpninfo, envname, slash ? slash + 1 : "128", 0, 0);
-
+               if (!slash) {
+                       /* no mask (same as /128) */
+                       script_setenv_int(vpninfo, envname, 128);
+               } else if ((masklen = strtol(slash+1, &endp, 10))<=128 && (*endp=='\0' || isspace(*endp))) {
+                       /* mask is /N */
+                       script_setenv_int(vpninfo, envname, masklen);
+               } else {
+                       /* mask is /dead:beef:: */
+                       struct in6_addr a;
+                       if (inet_pton(AF_INET6, slash+1, &a) <= 0)
+                               goto bad;
+                       masklen = netmasklen6(&a);
+                       /* something invalid like /ffff::1 */
+                       for (int ii = (masklen >> 3); ii < 16; ii++) {
+                               if (ii == (masklen >> 3) && (~a.s6_addr[ii] & 0xff) != (0xff >> (masklen & 0x07)))
+                                       goto bad;
+                               else if (a.s6_addr[ii] != 0)
+                                       goto bad;
+                       }
+                       script_setenv_int(vpninfo, envname, masklen);
+               }
                (*v6_incs)++;
                return 0;
        }