Hi Willy,
On Sat, May 27, Willy Tarreau wrote:
> On Sun, May 21, 2017 at 07:49:00PM +0300, Jarno Huuskonen wrote:
> > I noticed that ipv6 version of ipmask() converter is missing ?
> >
> > I'm attaching an example implementation for ipv6mask (incomplete:
> > missing at least documentation / lua) for comments.
> >
> > Maybe instead of just working with one mask ipv6mask(converter) could
> > take two arguments: first ipv6mask, second(optional) ipv4 mask applied to
> > ipv4mapped ipv6 address(::ffff:1.2.3.4).
>
> That could indeed be a good idea. Thinking a bit further, shouldn't we
> extend the current ipmask to support an optional ipv6 mask as a second
> argument (ie exactly what you did but with ipv6 optional instead of
> ipv4) ? This would allow it to be used on any address. It would end
> up like this :
> - ipv4 in => ipv4 out, using ipv4 mask
> - ipv4-in-ipv6 in => ipv4-in-ipv6 out, using ipv4 mask
> - ipv6 in => ipv6 out, using ipv6 mask
So ipmask would internally work on ipv6 addresses and ipv4
addresses are converted to/from mapped (::ffff:1.2.3.4) addresses ?
> What do you think ?
ipmask() with optional ipv6mask is fine with me, I guess it won't break
existing configs because ipv6 mask is optional. For ipv4 only it probably
adds a little overhead when converting to/from mapped ::ffff:1.2.3.4 addresses.
If something like this looks ok, then I can add documentation and
send proper patch.
-Jarno
diff --git a/include/common/standard.h b/include/common/standard.h
index f94eff9..5f21092 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -375,6 +375,12 @@ struct sockaddr_storage *str2sa_range(const char *str,
*/
int str2mask(const char *str, struct in_addr *mask);
+/* converts <str> to a struct in6_addr containing a network mask. It can be
+ * passed in colon form (ffff:ffff:ffff:ffff::) or in CIDR form (64).
+ * It returns 1 if the conversion succeeds otherwise zero.
+ */
+int str2mask6(const char *str, struct in6_addr *mask);
+
/* convert <cidr> to struct in_addr <mask>. It returns 1 if the conversion
* succeeds otherwise non-zero.
*/
diff --git a/src/arg.c b/src/arg.c
index 52977b7..b31858d 100644
--- a/src/arg.c
+++ b/src/arg.c
@@ -206,8 +206,15 @@ int make_arg_list(const char *in, int len, uint64_t mask,
struct arg **argp,
goto parse_err;
break;
- case ARGT_MSK6: /* not yet implemented */
- goto not_impl;
+ case ARGT_MSK6:
+ if (in == beg) // empty mask
+ goto empty_err;
+
+ if (!str2mask6(word, &arg->data.ipv6))
+ goto parse_err;
+
+ arg->type = ARGT_IPV6;
+ break;
case ARGT_TIME:
if (in == beg) // empty time
diff --git a/src/sample.c b/src/sample.c
index 20a59be..01b55ae 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1554,11 +1554,22 @@ static int sample_conv_str2upper(const struct arg
*arg_p, struct sample *smp, vo
return 1;
}
-/* takes the netmask in arg_p */
+/* takes the ipv4 netmask in arg_p[0] and optional ipv6 netmask in arg_p[1]*/
static int sample_conv_ipmask(const struct arg *arg_p, struct sample *smp,
void *private)
{
- smp->data.u.ipv4.s_addr &= arg_p->data.ipv4.s_addr;
- smp->data.type = SMP_T_IPV4;
+ if (*(uint32_t*)&smp->data.u.ipv6.s6_addr[0] == 0 &&
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[4] == 0 &&
+ (*(uint32_t*)&smp->data.u.ipv6.s6_addr[8] == 0 ||
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[8] == htonl(0xFFFF))) {
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &=
arg_p[0].data.ipv4.s_addr;
+ }
+ else if (arg_p[1].type == ARGT_IPV6) {
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[0] &=
*(uint32_t*)&arg_p[1].data.ipv6.s6_addr[0];
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[4] &=
*(uint32_t*)&arg_p[1].data.ipv6.s6_addr[4];
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[8] &=
*(uint32_t*)&arg_p[1].data.ipv6.s6_addr[8];
+ *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &=
*(uint32_t*)&arg_p[1].data.ipv6.s6_addr[12];
+ }
+ smp->data.type = SMP_T_IPV6;
return 1;
}
@@ -2736,7 +2747,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH,
{
{ "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR,
SMP_T_STR },
{ "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR,
SMP_T_STR },
{ "hex", sample_conv_bin2hex, 0, NULL, SMP_T_BIN,
SMP_T_STR },
- { "ipmask", sample_conv_ipmask, ARG1(1,MSK4), NULL, SMP_T_IPV4,
SMP_T_IPV4 },
+ { "ipmask", sample_conv_ipmask, ARG2(1,MSK4,MSK6), NULL, SMP_T_IPV6,
SMP_T_IPV6 },
{ "ltime", sample_conv_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT,
SMP_T_STR },
{ "utime", sample_conv_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT,
SMP_T_STR },
{ "crc32", sample_conv_crc32, ARG1(0,SINT), NULL, SMP_T_BIN,
SMP_T_SINT },
diff --git a/src/standard.c b/src/standard.c
index 82b7a01..d6f0c9b 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -1029,6 +1029,30 @@ int str2mask(const char *str, struct in_addr *mask)
return 1;
}
+/* converts <str> to a struct in6_addr containing a network mask. It can be
+ * passed in colon form (ffff:ffff:ffff:ffff::) or in CIDR form (64).
+ * It returns 1 if the conversion succeeds otherwise zero.
+ */
+int str2mask6(const char *str, struct in6_addr *mask)
+{
+ if (strchr(str, ':') != NULL) { /* colon notation */
+ if (!inet_pton(AF_INET6, str, mask))
+ return 0;
+ }
+ else { /* mask length */
+ char *err;
+ unsigned long len = strtol(str, &err, 10);
+
+ if (!*str || (err && *err) || (unsigned)len > 128)
+ return 0;
+ if (len)
+ len2mask6(len, mask);
+ else
+ memset(mask->s6_addr, 0, sizeof(*mask));
+ }
+ return 1;
+}
+
/* convert <cidr> to struct in_addr <mask>. It returns 1 if the conversion
* succeeds otherwise zero.
*/
--
Jarno Huuskonen