]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: acl: support IPv6 address matching
authorWilly Tarreau <w@1wt.eu>
Fri, 27 Apr 2012 22:41:46 +0000 (00:41 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 8 May 2012 19:28:14 +0000 (21:28 +0200)
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
include/types/acl.h
src/acl.c

index 12fee1ef9a6802aca0da2b0c2ba131d7ed3d1525..a484a04586a781607c074704f87e4f5f09f6993d 100644 (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
index 492e9639f21627a3e984c75a40df4996b3b20c9b..bf5537f93fef6822a385ffaeadd5c1bbc419fd59 100644 (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 */
index 013820364abd489320d56b727ae69fe9c3c4a3ee..8572bd174cdaa20d96c969cb1ee231f174ea9a47 100644 (file)
--- 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 <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;
        }
 }