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