Add an optional second parameter to the ipmask converter that specifies
the number of bits to mask off IPv6 addresses.

If the second parameter is not given IPv6 addresses fail to mask (resulting
in an empty string), preserving backwards compatibility: Previously
a sample like `src,ipmask(24)` failed to give a result for IPv6 addresses.

This feature can be tested like this:

  defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000

  frontend fe
        bind :::8080 v4v6

        # Masked IPv4 for IPv4, empty for IPv6 (with and without this commit)
        http-response set-header Test %[src,ipmask(24)]
        # Correctly masked IP addresses for both IPv4 and IPv6
        http-response set-header Test2 %[src,ipmask(24,ffff:ffff:ffff:ffff::)]
        # Correctly masked IP addresses for both IPv4 and IPv6
        http-response set-header Test3 %[src,ipmask(24,64)]

        default_backend be

  backend be
        server s example.com:80

Tested-By: Jarno Huuskonen <jarno.huusko...@uef.fi>
---
 doc/configuration.txt | 11 +++++++----
 src/sample.c          | 27 ++++++++++++++++++++++-----
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 827506788..eaaf0f71f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12870,11 +12870,14 @@ in_table(<table>)
   the presence of a certain key in a table tracking some elements (e.g. whether
   or not a source IP address or an Authorization header was already seen).
 
-ipmask(<mask>)
-  Apply a mask to an IPv4 address, and use the result for lookups and storage.
+ipmask(<mask4>, [<mask6>])
+  Apply a mask to an IP address, and use the result for lookups and storage.
   This can be used to make all hosts within a certain mask to share the same
-  table entries and as such use the same server. The mask can be passed in
-  dotted form (e.g. 255.255.255.0) or in CIDR form (e.g. 24).
+  table entries and as such use the same server. The mask4 can be passed in
+  dotted form (e.g. 255.255.255.0) or in CIDR form (e.g. 24). The mask6 can
+  be passed in quadruplet form (e.g. ffff:ffff::) or in CIDR form (e.g. 64).
+  If no mask6 is given IPv6 addresses will fail to convert for backwards
+  compatibility reasons.
 
 json([<input-code>])
   Escapes the input string and produces an ASCII output string ready to use as 
a
diff --git a/src/sample.c b/src/sample.c
index 04cec4fc8..0155b3d69 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1603,11 +1603,28 @@ static int sample_conv_str2upper(const struct arg 
*arg_p, struct sample *smp, vo
        return 1;
 }
 
-/* takes the netmask in arg_p */
-static int sample_conv_ipmask(const struct arg *arg_p, struct sample *smp, 
void *private)
+/* takes the IPv4 mask in args[0] and an optional IPv6 mask in args[1] */
+static int sample_conv_ipmask(const struct arg *args, struct sample *smp, void 
*private)
 {
-       smp->data.u.ipv4.s_addr &= arg_p->data.ipv4.s_addr;
-       smp->data.type = SMP_T_IPV4;
+       /* Attempt to convert to IPv4 to apply the correct mask. */
+       c_ipv62ip(smp);
+
+       if (smp->data.type == SMP_T_IPV4) {
+               smp->data.u.ipv4.s_addr &= args[0].data.ipv4.s_addr;
+               smp->data.type = SMP_T_IPV4;
+       }
+       else if (smp->data.type == SMP_T_IPV6) {
+               /* IPv6 cannot be converted without an IPv6 mask. */
+               if (args[1].type != ARGT_IPV6)
+                       return 0;
+
+               *(uint32_t*)&smp->data.u.ipv6.s6_addr[0]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[0];
+               *(uint32_t*)&smp->data.u.ipv6.s6_addr[4]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[4];
+               *(uint32_t*)&smp->data.u.ipv6.s6_addr[8]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[8];
+               *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[12];
+               smp->data.type = SMP_T_IPV6;
+       }
+
        return 1;
 }
 
@@ -2797,7 +2814,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, 
{
        { "length", sample_conv_length,    0,            NULL, SMP_T_STR,  
SMP_T_SINT },
        { "hex",    sample_conv_bin2hex,   0,            NULL, SMP_T_BIN,  
SMP_T_STR  },
        { "hex2i",  sample_conv_hex2int,   0,            NULL, SMP_T_STR,  
SMP_T_SINT },
-       { "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_ADDR, 
SMP_T_IPV4 },
        { "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  },
-- 
2.15.1


Reply via email to