To parse and store an IPv6 host or subnet address, init_netmask()
needs to handle 128 bit subnet masks.
Unfortunately what once was a pretty simple little function has grown
much larger. This logic must now not only parse IPv6 addresses
correctly, but must also distinguish between IPv4 and IPv6.
To avoid code duplication, I'm "bending" the cardinal rule of not
using "#ifdef" inside functions.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
};
unsigned long prefixlen;
uint32_t shift;
};
unsigned long prefixlen;
uint32_t shift;
+#ifdef IPV6_SUPPORTED
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ int i;
+#endif
/* No slash present; assume netmask is all ones */
if (slash == NULL) {
/* No slash present; assume netmask is all ones */
if (slash == NULL) {
case AF_INET:
prefixlen = 32;
break;
case AF_INET:
prefixlen = 32;
break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ prefixlen = 128;
+ break;
+#endif
default:
goto out_badfamily;
}
default:
goto out_badfamily;
}
set_addrlist_in(clp, 1, &sin);
return 1;
}
set_addrlist_in(clp, 1, &sin);
return 1;
}
+#ifdef IPV6_SUPPORTED
+ if (strchr(slash + 1, ':')) {
+ if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr))
+ goto out_badmask;
+ set_addrlist_in6(clp, 1, &sin6);
+ return 1;
+ }
+#endif
/* A prefixlen was given */
prefixlen = strtoul(slash + 1, &endptr, 10);
/* A prefixlen was given */
prefixlen = strtoul(slash + 1, &endptr, 10);
sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift);
set_addrlist_in(clp, 1, &sin);
return 1;
sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift);
set_addrlist_in(clp, 1, &sin);
return 1;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ if (prefixlen > 128)
+ goto out_badprefix;
+ for (i = 0; prefixlen > 32; i++) {
+ sin6.sin6_addr.s6_addr32[i] = 0xffffffff;
+ prefixlen -= 32;
+ }
+ shift = 32 - (uint32_t)prefixlen;
+ sin6.sin6_addr.s6_addr32[i] = htonl((uint32_t)~0 << shift);
+ set_addrlist_in6(clp, 1, &sin6);
+ return 1;
+#endif