]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: pattern/ip: simplify pat_match_ip() function
authorAurelien DARRAGON <adarragon@haproxy.com>
Wed, 6 Sep 2023 09:32:54 +0000 (11:32 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 21 Sep 2023 07:50:56 +0000 (09:50 +0200)
pat_match_ip() has been updated several times over the last decade to
introduce new features, but it was never cleaned up.

The result is that the function is pretty hard to read, and there are
multiple duplicated code blocks so it becomes error-prone to maintain it,
plus it bloats the haproxy binary for nothing.

In this patch, we move the tree search (ip4 / ip6) logic into 2
dedicated helper functions. This allows us to refactor pat_match_ip()
without touching to the original behavior.

src/pattern.c

index 0a71237f3b6aa12a791af2504f11baa4d6369521..080aeb00bbbe38f0c07019ee4919f972faac1232 100644 (file)
@@ -964,138 +964,128 @@ struct pattern *pat_match_len(struct sample *smp, struct pattern_expr *expr, int
        return NULL;
 }
 
-struct pattern *pat_match_ip(struct sample *smp, struct pattern_expr *expr, int fill)
+/* Performs ipv4 key lookup in <expr> ipv4 tree
+ * Returns NULL on failure
+ */
+static struct pattern *_pat_match_tree_ipv4(struct in_addr *key, struct pattern_expr *expr, int fill)
+{
+       struct ebmb_node *node;
+       struct pattern_tree *elt;
+
+       /* Lookup an IPv4 address in the expression's pattern tree using
+        * the longest match method.
+        */
+       node = ebmb_lookup_longest(&expr->pattern_tree, key);
+       while (node) {
+               elt = ebmb_entry(node, struct pattern_tree, node);
+               if (elt->ref->gen_id != expr->ref->curr_gen) {
+                       node = ebmb_lookup_shorter(node);
+                       continue;
+               }
+               if (fill) {
+                       static_pattern.data = elt->data;
+                       static_pattern.ref = elt->ref;
+                       static_pattern.sflags = PAT_SF_TREE;
+                       static_pattern.type = SMP_T_IPV4;
+                       static_pattern.val.ipv4.addr.s_addr = read_u32(elt->node.key);
+                       if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
+                               return NULL;
+               }
+               return &static_pattern;
+       }
+       return NULL;
+}
+
+/* Performs ipv6 key lookup in <expr> ipv6 tree
+ * Returns NULL on failure
+ */
+static struct pattern *_pat_match_tree_ipv6(struct in6_addr *key, struct pattern_expr *expr, int fill)
 {
-       struct in_addr v4; /* in network byte order */
-       struct in6_addr tmp6;
-       struct in_addr *s;
        struct ebmb_node *node;
        struct pattern_tree *elt;
+
+       /* Lookup an IPv6 address in the expression's pattern tree using
+        * the longest match method.
+        */
+       node = ebmb_lookup_longest(&expr->pattern_tree_2, key);
+       while (node) {
+               elt = ebmb_entry(node, struct pattern_tree, node);
+               if (elt->ref->gen_id != expr->ref->curr_gen) {
+                       node = ebmb_lookup_shorter(node);
+                       continue;
+               }
+               if (fill) {
+                       static_pattern.data = elt->data;
+                       static_pattern.ref = elt->ref;
+                       static_pattern.sflags = PAT_SF_TREE;
+                       static_pattern.type = SMP_T_IPV6;
+                       memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
+                       static_pattern.val.ipv6.mask = elt->node.node.pfx;
+               }
+               return &static_pattern;
+       }
+       return NULL;
+}
+
+struct pattern *pat_match_ip(struct sample *smp, struct pattern_expr *expr, int fill)
+{
+       struct in_addr v4;
+       struct in6_addr v6;
        struct pattern_list *lst;
        struct pattern *pattern;
 
        /* The input sample is IPv4. Try to match in the trees. */
        if (smp->data.type == SMP_T_IPV4) {
-               /* Lookup an IPv4 address in the expression's pattern tree using
-                * the longest match method.
-                */
-               s = &smp->data.u.ipv4;
-               node = ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr);
-               while (node) {
-                       elt = ebmb_entry(node, struct pattern_tree, node);
-                       if (elt->ref->gen_id != expr->ref->curr_gen) {
-                               node = ebmb_lookup_shorter(node);
-                               continue;
-                       }
-                       if (fill) {
-                               static_pattern.data = elt->data;
-                               static_pattern.ref = elt->ref;
-                               static_pattern.sflags = PAT_SF_TREE;
-                               static_pattern.type = SMP_T_IPV4;
-                               static_pattern.val.ipv4.addr.s_addr = read_u32(elt->node.key);
-                               if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
-                                       return NULL;
-                       }
-                       return &static_pattern;
-               }
-
+               pattern = _pat_match_tree_ipv4(&smp->data.u.ipv4, expr, fill);
+               if (pattern)
+                       return pattern;
                /* The IPv4 sample don't match the IPv4 tree. Convert the IPv4
                 * sample address to IPv6 and try to lookup in the IPv6 tree.
                 */
-               v4tov6(&tmp6, &smp->data.u.ipv4);
-               node = ebmb_lookup_longest(&expr->pattern_tree_2, &tmp6);
-               while (node) {
-                       elt = ebmb_entry(node, struct pattern_tree, node);
-                       if (elt->ref->gen_id != expr->ref->curr_gen) {
-                               node = ebmb_lookup_shorter(node);
-                               continue;
-                       }
-                       if (fill) {
-                               static_pattern.data = elt->data;
-                               static_pattern.ref = elt->ref;
-                               static_pattern.sflags = PAT_SF_TREE;
-                               static_pattern.type = SMP_T_IPV6;
-                               memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
-                               static_pattern.val.ipv6.mask = elt->node.node.pfx;
-                       }
-                       return &static_pattern;
-               }
+               v4tov6(&v6, &smp->data.u.ipv4);
+               pattern = _pat_match_tree_ipv6(&v6, expr, fill);
+               if (pattern)
+                       return pattern;
+               /* eligible for list lookup using IPv4 address */
+               v4 = smp->data.u.ipv4;
+               goto list_lookup;
        }
 
        /* The input sample is IPv6. Try to match in the trees. */
        if (smp->data.type == SMP_T_IPV6) {
-               /* Lookup an IPv6 address in the expression's pattern tree using
-                * the longest match method.
+               pattern = _pat_match_tree_ipv6(&smp->data.u.ipv6, expr, fill);
+               if (pattern)
+                       return pattern;
+               /* No match in the IPv6 tree. Try to convert 6 to 4 to lookup in
+                * the IPv4 tree
                 */
-               node = ebmb_lookup_longest(&expr->pattern_tree_2, &smp->data.u.ipv6);
-               while (node) {
-                       elt = ebmb_entry(node, struct pattern_tree, node);
-                       if (elt->ref->gen_id != expr->ref->curr_gen) {
-                               node = ebmb_lookup_shorter(node);
-                               continue;
-                       }
-                       if (fill) {
-                               static_pattern.data = elt->data;
-                               static_pattern.ref = elt->ref;
-                               static_pattern.sflags = PAT_SF_TREE;
-                               static_pattern.type = SMP_T_IPV6;
-                               memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
-                               static_pattern.val.ipv6.mask = elt->node.node.pfx;
-                       }
-                       return &static_pattern;
-               }
-
-               /* Try to convert 6 to 4 */
                if (v6tov4(&v4, &smp->data.u.ipv6)) {
-                       /* Lookup an IPv4 address in the expression's pattern tree using the longest
-                        * match method.
-                        */
-                       node = ebmb_lookup_longest(&expr->pattern_tree, &v4);
-                       while (node) {
-                               elt = ebmb_entry(node, struct pattern_tree, node);
-                               if (elt->ref->gen_id != expr->ref->curr_gen) {
-                                       node = ebmb_lookup_shorter(node);
-                                       continue;
-                               }
-                               if (fill) {
-                                       static_pattern.data = elt->data;
-                                       static_pattern.ref = elt->ref;
-                                       static_pattern.sflags = PAT_SF_TREE;
-                                       static_pattern.type = SMP_T_IPV4;
-                                       static_pattern.val.ipv4.addr.s_addr = read_u32(elt->node.key);
-                                       if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
-                                               return NULL;
-                               }
-                               return &static_pattern;
-                       }
+                       pattern = _pat_match_tree_ipv4(&v4, expr, fill);
+                       if (pattern)
+                               return pattern;
+                       /* eligible for list lookup using IPv4 address */
+                       goto list_lookup;
                }
        }
 
-       /* Lookup in the list. the list contain only IPv4 patterns */
+ not_found:
+       return NULL;
+
+ list_lookup:
+       /* No match in the trees, but we still have a valid IPv4 address: lookup
+        * in the IPv4 list (non-contiguous masks list). This is our last resort
+        */
        list_for_each_entry(lst, &expr->patterns, list) {
                pattern = &lst->pat;
 
                if (pattern->ref->gen_id != expr->ref->curr_gen)
                        continue;
 
-               /* The input sample is IPv4, use it as is. */
-               if (smp->data.type == SMP_T_IPV4) {
-                       v4.s_addr = smp->data.u.ipv4.s_addr;
-               }
-               else if (smp->data.type == SMP_T_IPV6) {
-                       /* v4 match on a V6 sample. Try to convert v6 form to v4 address */
-                       if (!v6tov4(&v4, &smp->data.u.ipv6))
-                               continue;
-               } else {
-                 /* impossible */
-                 continue;
-               }
-
                /* Check if the input sample match the current pattern. */
                if (((v4.s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
                        return pattern;
        }
-       return NULL;
+       goto not_found;
 }
 
 /* finds the pattern holding <list> from list head <head> and deletes it.