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

Reply via email to