diff --git a/doc/configuration.txt b/doc/configuration.txt index 12fee1ef9..a484a0458 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 492e9639f..bf5537f93 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 013820364..8572bd174 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 (smp->type != SMP_T_IPV4 || pattern->type != SMP_T_IPV4) - return ACL_PAT_FAIL; + 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; - s = &smp->data.ipv4; - if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) + 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; + } + + 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; } }