From ceb4ac9c342e644334d3db9bbba56d9c1a39b596 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 28 Apr 2012 00:41:46 +0200 Subject: [PATCH] MEDIUM: acl: support IPv6 address matching Make use of the new IPv6 pattern type so that acl_match_ip() knows how to compare pattern and sample. IPv6 may be entered in their usual form, with or without a netmask appended. Only bit counts are accepted for IPv6 netmasks. In order to avoid any risk of trouble with randomly resolved IP addresses, host names are never allowed in IPv6 patterns. HAProxy is also able to match IPv4 addresses with IPv6 addresses in the following situations : - tested address is IPv4, pattern address is IPv4, the match applies in IPv4 using the supplied mask if any. - tested address is IPv6, pattern address is IPv6, the match applies in IPv6 using the supplied mask if any. - tested address is IPv6, pattern address is IPv4, the match applies in IPv4 using the pattern's mask if the IPv6 address matches with 2002:IPV4::, ::IPV4 or ::ffff:IPV4, otherwise it fails. - tested address is IPv4, pattern address is IPv6, the IPv4 address is first converted to IPv6 by prefixing ::ffff: in front of it, then the match is applied in IPv6 using the supplied IPv6 mask. --- doc/configuration.txt | 54 ++++++++++++++++++++----------- include/types/acl.h | 4 +++ src/acl.c | 75 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 108 insertions(+), 25 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 12fee1ef9a..a484a04586 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -61,7 +61,7 @@ Summary 7.1. Matching integers 7.2. Matching strings 7.3. Matching regular expressions (regexes) -7.4. Matching IPv4 addresses +7.4. Matching IPv4 and IPv6 addresses 7.5. Available matching criteria 7.5.1. Matching at Layer 4 and below 7.5.2. Matching contents at Layer 4 @@ -6731,7 +6731,7 @@ address if they are used: The currently supported settings are the following ones. -addr +addr Using the "addr" parameter, it becomes possible to use a different IP address to send health-checks. On some servers, it may be desirable to dedicate an IP address to specific component able to perform complex tests which are more @@ -7264,7 +7264,7 @@ the "--" flag before the first string. Same principle applies of course to match the string "--". -7.4. Matching IPv4 addresses +7.4. Matching IPv4 and IPv6 addresses ---------------------------- IPv4 addresses values can be specified either as plain addresses or with a @@ -7276,6 +7276,24 @@ at least ensure that they are present in /etc/hosts so that the configuration does not depend on any random DNS match at the moment the configuration is parsed. +IPv6 may be entered in their usual form, with or without a netmask appended. +Only bit counts are accepted for IPv6 netmasks. In order to avoid any risk of +trouble with randomly resolved IP addresses, host names are never allowed in +IPv6 patterns. + +HAProxy is also able to match IPv4 addresses with IPv6 addresses in the +following situations : + - tested address is IPv4, pattern address is IPv4, the match applies + in IPv4 using the supplied mask if any. + - tested address is IPv6, pattern address is IPv6, the match applies + in IPv6 using the supplied mask if any. + - tested address is IPv6, pattern address is IPv4, the match applies in IPv4 + using the pattern's mask if the IPv6 address matches with 2002:IPV4::, + ::IPV4 or ::ffff:IPV4, otherwise it fails. + - tested address is IPv4, pattern address is IPv6, the IPv4 address is first + converted to IPv6 by prefixing ::ffff: in front of it, then the match is + applied in IPv6 using the supplied IPv6 mask. + 7.5. Available matching criteria -------------------------------- @@ -7359,8 +7377,8 @@ connslots() will be -1. dst - Applies to the local IPv4 address the client connected to. It can be used to - switch to a different backend for some alternative addresses. + Applies to the local IPv4 or IPv6 address the client connected to. It can be + used to switch to a different backend for some alternative addresses. dst_conn Applies to the number of currently established connections on the same socket @@ -7555,9 +7573,9 @@ so_id Applies to the socket's id. Useful in frontends with many bind keywords. src - Applies to the client's IPv4 address. It is usually used to limit access to - certain resources such as statistics. Note that it is the TCP-level source - address which is used, and not the address of a client behind a proxy. + Applies to the client's IPv4 or IPv6 address. It is usually used to limit + access to certain resources such as statistics. Note that it is the TCP-level + source address which is used, and not the address of a client behind a proxy. src_bytes_in_rate src_bytes_in_rate() @@ -7983,10 +8001,10 @@ hdr_end(
[,]) response headers sent by the server. hdr_ip -hdr_ip(
[,]) - Returns true when one of the headers' values contains an IP address matching - . This is mainly used with headers such as X-Forwarded-For or - X-Client-IP. See "hdr" for more information on header matching. Use the +hdr_ip(
[,])
+ Returns true when one of the headers' values contains an IPv4 or IPv6 address + matching
. This is mainly used with headers such as X-Forwarded-For + or X-Client-IP. See "hdr" for more information on header matching. Use the shdr_ip() variant for response headers sent by the server. hdr_len @@ -8118,10 +8136,10 @@ url_end Returns true when the URL ends with one of the strings. It has very limited use. "path_end" should be used instead for filename matching. -url_ip - Applies to the IP address specified in the absolute URI in an HTTP request. - It can be used to prevent access to certain resources such as local network. - It is useful with option "http_proxy". +url_ip
+ Applies to the IPv4 or IPv6 address specified in the absolute URI in an HTTP + request. It can be used to prevent access to certain resources such as local + network. It is useful with option "http_proxy". url_len Returns true when the url length matches the values or ranges specified. This @@ -8170,8 +8188,8 @@ urlp_end() Returns true when the URL parameter "" ends with one of the strings. urlp_ip() - Returns true when the URL parameter "" contains an IPv4 address which - matches one of the specified IP addresses. + Returns true when the URL parameter "" contains an IPv4 or IPv6 address + which matches one of the specified addresses. urlp_len() Returns true when the URL parameter "" has a length matching the values diff --git a/include/types/acl.h b/include/types/acl.h index 492e9639f2..bf5537f93f 100644 --- a/include/types/acl.h +++ b/include/types/acl.h @@ -202,6 +202,10 @@ struct acl_pattern { struct in_addr addr; struct in_addr mask; } ipv4; /* IPv4 address */ + struct { + struct in6_addr addr; + unsigned char mask; /* number of bits */ + } ipv6; /* IPv6 address/mask */ struct acl_time time; /* valid hours and days */ unsigned int group_mask; struct eb_root *tree; /* tree storing all values if any */ diff --git a/src/acl.c b/src/acl.c index 013820364a..8572bd174c 100644 --- a/src/acl.c +++ b/src/acl.c @@ -707,14 +707,70 @@ int acl_match_len(struct sample *smp, struct acl_pattern *pattern) int acl_match_ip(struct sample *smp, struct acl_pattern *pattern) { - struct in_addr *s; + unsigned int v4; /* in network byte order */ + struct in6_addr *v6; + int bits, pos; + struct in6_addr tmp6; + + if (pattern->type == SMP_T_IPV4) { + if (smp->type == SMP_T_IPV4) { + v4 = smp->data.ipv4.s_addr; + } + else if (smp->type == SMP_T_IPV6) { + /* v4 match on a V6 sample. We want to check at least for + * the following forms : + * - ::ffff:ip:v4 (ipv4 mapped) + * - ::0000:ip:v4 (old ipv4 mapped) + * - 2002:ip:v4:: (6to4) + */ + if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 && + *(uint32_t*)&smp->data.ipv6.s6_addr[4] == 0 && + (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 || + *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) { + v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; + } + else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { + v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + + ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); + } + else + return ACL_PAT_FAIL; + } + else + return ACL_PAT_FAIL; - if (smp->type != SMP_T_IPV4 || pattern->type != SMP_T_IPV4) - return ACL_PAT_FAIL; + if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) + return ACL_PAT_PASS; + else + return ACL_PAT_FAIL; + } + else if (pattern->type == SMP_T_IPV6) { + if (smp->type == SMP_T_IPV4) { + /* Convert the IPv4 sample address to IPv4 with the + * mapping method using the ::ffff: prefix. + */ + memset(&tmp6, 0, 10); + *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); + *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; + v6 = &tmp6; + } + else if (smp->type == SMP_T_IPV6) { + v6 = &smp->data.ipv6; + } + else { + return ACL_PAT_FAIL; + } - s = &smp->data.ipv4; - if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) + bits = pattern->val.ipv6.mask; + for (pos = 0; bits > 0; pos += 4, bits -= 32) { + v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos]; + if (bits < 32) + v4 &= (~0U) << (32-bits); + if (v4) + return ACL_PAT_FAIL; + } return ACL_PAT_PASS; + } return ACL_PAT_FAIL; } @@ -1033,7 +1089,6 @@ int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, ch if (pattern->flags & ACL_PAT_F_TREE_OK) tree = pattern->val.tree; - pattern->type = SMP_T_IPV4; if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) { unsigned int mask = ntohl(pattern->val.ipv4.mask.s_addr); struct ebmb_node *node; @@ -1042,6 +1097,7 @@ int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, ch * the left. This means that this mask + its lower bit added * once again is null. */ + pattern->type = SMP_T_IPV4; if (mask + (mask & -mask) == 0 && tree) { mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */ /* FIXME: insert / into the tree here */ @@ -1060,9 +1116,14 @@ int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque, ch } return 1; } + else if (str62net(*text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) { + /* no tree support right now */ + pattern->type = SMP_T_IPV6; + return 1; + } else { if (err) - memprintf(err, "'%s' is not a valid IPv4 address", *text); + memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", *text); return 0; } } -- 2.39.5