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.
This commit is contained in:
Willy Tarreau 2012-04-28 00:41:46 +02:00
parent 6d20e28556
commit ceb4ac9c34
3 changed files with 108 additions and 25 deletions

View File

@ -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 <ipv4>
addr <ipv4|ipv6>
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(<backend>) <integer>
will be -1.
dst <ip_address>
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 <integer>
Applies to the number of currently established connections on the same socket
@ -7555,9 +7573,9 @@ so_id <integer>
Applies to the socket's id. Useful in frontends with many bind keywords.
src <ip_address>
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 <integer>
src_bytes_in_rate(<table>) <integer>
@ -7983,10 +8001,10 @@ hdr_end(<header>[,<occ>]) <string>
response headers sent by the server.
hdr_ip <ip_address>
hdr_ip(<header>[,<occ>]) <ip_address>
Returns true when one of the headers' values contains an IP address matching
<ip_address>. 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(<header>[,<occ>]) <address>
Returns true when one of the headers' values contains an IPv4 or IPv6 address
matching <address>. 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 <integer>
@ -8118,10 +8136,10 @@ url_end <string>
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 <ip_address>
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 <address>
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 <integer>
Returns true when the url length matches the values or ranges specified. This
@ -8170,8 +8188,8 @@ urlp_end(<name>) <string>
Returns true when the URL parameter "<name>" ends with one of the strings.
urlp_ip(<name>) <ip_address>
Returns true when the URL parameter "<name>" contains an IPv4 address which
matches one of the specified IP addresses.
Returns true when the URL parameter "<name>" contains an IPv4 or IPv6 address
which matches one of the specified addresses.
urlp_len(<name>) <integer>
Returns true when the URL parameter "<name>" has a length matching the values

View File

@ -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 */

View File

@ -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 <addr>/<mask> 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;
}
}