]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] acl: add ability to insert patterns in trees
authorWilly Tarreau <w@1wt.eu>
Tue, 11 May 2010 21:25:05 +0000 (23:25 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 13 May 2010 19:37:41 +0000 (21:37 +0200)
The code is now ready to support loading pattern from filesinto trees. For
that, it will be required that the ACL keyword has a flag ACL_MAY_LOOKUP
and that the expr is case sensitive. When that is true, the pattern will
have a flag ACL_PAT_F_TREE_OK to indicate that it is possible to feed the
tree instead of a usual pattern if the parsing function is able to do this.
The tree's root is pre-initialized in the pattern's value so that the
function can easily find it. At that point, if the parsing function decides
to use the tree, it just sets ACL_PAT_F_TREE in the return flags so that
the caller knows the tree has been used and the pattern can be recycled.

That way it will be possible to load some patterns into the tree when it
is compatible, and other ones as linear linked lists. A good example of
this might be IPv4 network entries : right now we support holes in masks,
but this very rare feature is not compatible with binary lookup in trees.
So the parser will be able to decide itself whether the pattern can go to
the tree or not.

include/types/acl.h
src/acl.c

index 957f214d1381bd60ae884e1b40a9645e2b11266f..831b4d105f6c2e91a5572e386c3de8d3f29004b0 100644 (file)
@@ -220,6 +220,7 @@ struct acl_pattern {
                } ipv4;                         /* IPv4 address */
                struct acl_time time;           /* valid hours and days */
                unsigned int group_mask;
+               struct eb_root *tree;           /* tree storing all values if any */
        } val;                                  /* direct value */
        union {
                void *ptr;              /* any data */
index a4ea068f6cf33ad07c2bef4872ff58bd3d0982e4..ad3feaf910c7bdfaa18c8ef06d78c33c0470507c 100644 (file)
--- a/src/acl.c
+++ b/src/acl.c
@@ -650,9 +650,21 @@ static void free_pattern_list(struct list *head)
                free_pattern(pat);
 }
 
+static void free_pattern_tree(struct eb_root *root)
+{
+       struct eb_node *node, *next;
+       node = eb_first(root);
+       while (node) {
+               next = eb_next(node);
+               free(node);
+               node = next;
+       }
+}
+
 static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
 {
        free_pattern_list(&expr->patterns);
+       free_pattern_tree(&expr->pattern_tree);
        LIST_INIT(&expr->patterns);
        if (expr->arg_len && expr->arg.str)
                free(expr->arg.str);
@@ -679,6 +691,7 @@ static int acl_read_patterns_from_file(     struct acl_keyword *aclkw,
         * The pattern stops at the first CR, LF or EOF encountered.
         */
        opaque = 0;
+       pattern = NULL;
        args[0] = trash;
        args[1] = "";
        while (fgets(trash, sizeof(trash), file) != NULL) {
@@ -688,15 +701,34 @@ static int acl_read_patterns_from_file(   struct acl_keyword *aclkw,
                        c++;
                *c = 0;
 
-               pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
+               /* we keep the previous pattern along iterations as long as it's not used */
+               if (!pattern)
+                       pattern = (struct acl_pattern *)malloc(sizeof(*pattern));
                if (!pattern)
                        goto out_close;
+
+               memset(pattern, 0, sizeof(*pattern));
                pattern->flags = patflags;
 
+               if ((aclkw->requires & ACL_MAY_LOOKUP) && !(pattern->flags & ACL_PAT_F_IGNORE_CASE)) {
+                       /* we pre-set the data pointer to the tree's head so that functions
+                        * which are able to insert in a tree know where to do that.
+                        */
+                       pattern->flags |= ACL_PAT_F_TREE_OK;
+                       pattern->val.tree = &expr->pattern_tree;
+               }
+
                if (!aclkw->parse(args, pattern, &opaque))
                        goto out_free_pattern;
-               LIST_ADDQ(&expr->patterns, &pattern->list);
+
+               /* if the parser did not feed the tree, let's chain the pattern to the list */
+               if (!(pattern->flags & ACL_PAT_F_TREE)) {
+                       LIST_ADDQ(&expr->patterns, &pattern->list);
+                       pattern = NULL; /* get a new one */
+               }
        }
+       if (pattern)
+               free_pattern(pattern);
        return 1;
 
  out_free_pattern:
@@ -730,6 +762,7 @@ struct acl_expr *parse_acl_expr(const char **args)
        expr->kw = aclkw;
        aclkw->use_cnt++;
        LIST_INIT(&expr->patterns);
+       expr->pattern_tree = EB_ROOT_UNIQUE;
        expr->arg.str = NULL;
        expr->arg_len = 0;
 
@@ -1193,12 +1226,13 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v
                                else {
                                        /* call the match() function for all tests on this value */
                                        list_for_each_entry(pattern, &expr->patterns, list) {
-                                               acl_res |= expr->kw->match(&test, pattern);
                                                if (acl_res == ACL_PAT_PASS)
                                                        break;
+                                               acl_res |= expr->kw->match(&test, pattern);
                                        }
 
-                                       if ((test.flags & ACL_TEST_F_NULL_MATCH) && LIST_ISEMPTY(&expr->patterns))
+                                       if ((test.flags & ACL_TEST_F_NULL_MATCH) &&
+                                           LIST_ISEMPTY(&expr->patterns) && !expr->pattern_tree.b[EB_LEFT])
                                                acl_res |= expr->kw->match(&test, NULL);
                                }
                                /*