MEDIUM: acl: move the ->parse, ->match and ->smp fields to acl_expr

We'll need each ACL expression to be able to support its own parse and
match methods, so we're moving these fields to the ACL expression.
This commit is contained in:
Willy Tarreau 2013-03-31 18:34:33 +02:00
parent ff5afcc32b
commit d76a98a5fc
2 changed files with 43 additions and 39 deletions

View File

@ -165,19 +165,21 @@ struct acl_kw_list {
/* /*
* Description of an ACL expression. * Description of an ACL expression.
* It contains a subject and a set of patterns to test against it. * The expression is part of a list. It contains pointers to the keyword, the
* - the function get() is called to retrieve the subject from the * parse and match functions which default to the keyword's, the sample fetch
* current session or transaction and build a test. * descriptor which also defaults to the keyword's, and a list or tree of
* - the function test() is called to evaluate the test based on the * patterns to test against. The structure is organized so that the hot parts
* available patterns and return ACL_PAT_* * are grouped together in order to optimize caching.
* Both of those functions are available through the keyword.
*/ */
struct acl_expr { struct acl_expr {
struct list list; /* chaining */ int (*parse)(const char **text, struct acl_pattern *pattern, int *opaque, char **err);
struct acl_keyword *kw; /* back-reference to the keyword */ int (*match)(struct sample *smp, struct acl_pattern *pattern);
struct arg *args; /* optional argument list (eg: header or cookie name) */ struct arg *args; /* optional fetch argument list (eg: header or cookie name) */
struct sample_fetch *smp; /* the sample fetch we depend on */
struct list patterns; /* list of acl_patterns */ struct list patterns; /* list of acl_patterns */
struct eb_root pattern_tree; /* may be used for lookup in large datasets */ struct eb_root pattern_tree; /* may be used for lookup in large datasets */
struct list list; /* chaining */
struct acl_keyword *kw; /* back-reference to the keyword */
}; };
/* The acl will be linked to from the proxy where it is declared */ /* The acl will be linked to from the proxy where it is declared */

View File

@ -878,8 +878,7 @@ static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
/* Reads patterns from a file. If <err_msg> is non-NULL, an error message will /* Reads patterns from a file. If <err_msg> is non-NULL, an error message will
* be returned there on errors and the caller will have to free it. * be returned there on errors and the caller will have to free it.
*/ */
static int acl_read_patterns_from_file( struct acl_keyword *aclkw, static int acl_read_patterns_from_file(struct acl_expr *expr,
struct acl_expr *expr,
const char *filename, int patflags, const char *filename, int patflags,
char **err) char **err)
{ {
@ -938,7 +937,7 @@ static int acl_read_patterns_from_file( struct acl_keyword *aclkw,
pattern->flags = patflags; pattern->flags = patflags;
if (!(pattern->flags & ACL_PAT_F_IGNORE_CASE) && if (!(pattern->flags & ACL_PAT_F_IGNORE_CASE) &&
(aclkw->match == acl_match_str || aclkw->match == acl_match_ip)) { (expr->match == acl_match_str || expr->match == acl_match_ip)) {
/* we pre-set the data pointer to the tree's head so that functions /* 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. * which are able to insert in a tree know where to do that.
*/ */
@ -947,7 +946,7 @@ static int acl_read_patterns_from_file( struct acl_keyword *aclkw,
} }
pattern->type = SMP_TYPES; /* unspecified type by default */ pattern->type = SMP_TYPES; /* unspecified type by default */
if (!aclkw->parse(args, pattern, &opaque, err)) if (!expr->parse(args, pattern, &opaque, err))
goto out_free_pattern; goto out_free_pattern;
/* if the parser did not feed the tree, let's chain the pattern to the list */ /* if the parser did not feed the tree, let's chain the pattern to the list */
@ -998,10 +997,13 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
aclkw->use_cnt++; aclkw->use_cnt++;
LIST_INIT(&expr->patterns); LIST_INIT(&expr->patterns);
expr->pattern_tree = EB_ROOT_UNIQUE; expr->pattern_tree = EB_ROOT_UNIQUE;
expr->parse = aclkw->parse;
expr->match = aclkw->match;
expr->args = empty_arg_list; expr->args = empty_arg_list;
expr->smp = aclkw->smp;
arg = strchr(args[0], '('); arg = strchr(args[0], '(');
if (aclkw->smp->arg_mask) { if (expr->smp->arg_mask) {
int nbargs = 0; int nbargs = 0;
char *end; char *end;
@ -1010,7 +1012,7 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
arg++; arg++;
end = strchr(arg, ')'); end = strchr(arg, ')');
if (!end) { if (!end) {
memprintf(err, "missing closing ')' after arguments to ACL keyword '%s'", aclkw->kw); memprintf(err, "missing closing ')' after arguments to ACL keyword '%s'", expr->kw->kw);
goto out_free_expr; goto out_free_expr;
} }
@ -1019,34 +1021,34 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
* An error is also reported if some mandatory arguments are * An error is also reported if some mandatory arguments are
* missing. * missing.
*/ */
nbargs = make_arg_list(arg, end - arg, aclkw->smp->arg_mask, &expr->args, nbargs = make_arg_list(arg, end - arg, expr->smp->arg_mask, &expr->args,
err, NULL, NULL); err, NULL, NULL);
if (nbargs < 0) { if (nbargs < 0) {
/* note that make_arg_list will have set <err> here */ /* note that make_arg_list will have set <err> here */
memprintf(err, "in argument to '%s', %s", aclkw->kw, *err); memprintf(err, "in argument to '%s', %s", expr->kw->kw, *err);
goto out_free_expr; goto out_free_expr;
} }
if (!expr->args) if (!expr->args)
expr->args = empty_arg_list; expr->args = empty_arg_list;
if (aclkw->smp->val_args && !aclkw->smp->val_args(expr->args, err)) { if (expr->smp->val_args && !expr->smp->val_args(expr->args, err)) {
/* invalid keyword argument, error must have been /* invalid keyword argument, error must have been
* set by val_args(). * set by val_args().
*/ */
memprintf(err, "in argument to '%s', %s", aclkw->kw, *err); memprintf(err, "in argument to '%s', %s", expr->kw->kw, *err);
goto out_free_expr; goto out_free_expr;
} }
} }
else if (ARGM(aclkw->smp->arg_mask) == 1) { else if (ARGM(expr->smp->arg_mask) == 1) {
int type = (aclkw->smp->arg_mask >> 4) & 15; int type = (expr->smp->arg_mask >> 4) & 15;
/* If a proxy is noted as a mandatory argument, we'll fake /* If a proxy is noted as a mandatory argument, we'll fake
* an empty one so that acl_find_targets() resolves it as * an empty one so that acl_find_targets() resolves it as
* the current one later. * the current one later.
*/ */
if (type != ARGT_FE && type != ARGT_BE && type != ARGT_TAB) { if (type != ARGT_FE && type != ARGT_BE && type != ARGT_TAB) {
memprintf(err, "ACL keyword '%s' expects %d arguments", aclkw->kw, ARGM(aclkw->smp->arg_mask)); memprintf(err, "ACL keyword '%s' expects %d arguments", expr->kw->kw, ARGM(expr->smp->arg_mask));
goto out_free_expr; goto out_free_expr;
} }
@ -1061,16 +1063,16 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
expr->args[0].data.str.len = 0; expr->args[0].data.str.len = 0;
expr->args[1].type = ARGT_STOP; expr->args[1].type = ARGT_STOP;
} }
else if (ARGM(aclkw->smp->arg_mask)) { else if (ARGM(expr->smp->arg_mask)) {
/* there were some mandatory arguments */ /* there were some mandatory arguments */
memprintf(err, "ACL keyword '%s' expects %d arguments", aclkw->kw, ARGM(aclkw->smp->arg_mask)); memprintf(err, "ACL keyword '%s' expects %d arguments", expr->kw->kw, ARGM(expr->smp->arg_mask));
goto out_free_expr; goto out_free_expr;
} }
} }
else { else {
if (arg) { if (arg) {
/* no argument expected */ /* no argument expected */
memprintf(err, "ACL keyword '%s' takes no argument", aclkw->kw); memprintf(err, "ACL keyword '%s' takes no argument", expr->kw->kw);
goto out_free_expr; goto out_free_expr;
} }
} }
@ -1087,7 +1089,7 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
if ((*args)[1] == 'i') if ((*args)[1] == 'i')
patflags |= ACL_PAT_F_IGNORE_CASE; patflags |= ACL_PAT_F_IGNORE_CASE;
else if ((*args)[1] == 'f') { else if ((*args)[1] == 'f') {
if (!acl_read_patterns_from_file(aclkw, expr, args[1], patflags | ACL_PAT_F_FROM_FILE, err)) if (!acl_read_patterns_from_file(expr, args[1], patflags | ACL_PAT_F_FROM_FILE, err))
goto out_free_expr; goto out_free_expr;
args++; args++;
} }
@ -1112,7 +1114,7 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
pattern->flags = patflags; pattern->flags = patflags;
pattern->type = SMP_TYPES; /* unspecified type */ pattern->type = SMP_TYPES; /* unspecified type */
ret = aclkw->parse(args, pattern, &opaque, err); ret = expr->parse(args, pattern, &opaque, err);
if (!ret) if (!ret)
goto out_free_pattern; goto out_free_pattern;
@ -1213,8 +1215,8 @@ struct acl *parse_acl(const char **args, struct list *known_acl, char **err)
* and where it may be used. If an ACL relies on multiple matches, it is * and where it may be used. If an ACL relies on multiple matches, it is
* OK if at least one of them may match in the context where it is used. * OK if at least one of them may match in the context where it is used.
*/ */
cur_acl->use |= acl_expr->kw->smp->use; cur_acl->use |= acl_expr->smp->use;
cur_acl->val |= acl_expr->kw->smp->val; cur_acl->val |= acl_expr->smp->val;
LIST_ADDQ(&cur_acl->expr, &acl_expr->list); LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
return cur_acl; return cur_acl;
@ -1299,8 +1301,8 @@ struct acl *find_acl_default(const char *acl_name, struct list *known_acl, char
} }
cur_acl->name = name; cur_acl->name = name;
cur_acl->use |= acl_expr->kw->smp->use; cur_acl->use |= acl_expr->smp->use;
cur_acl->val |= acl_expr->kw->smp->val; cur_acl->val |= acl_expr->smp->val;
LIST_INIT(&cur_acl->expr); LIST_INIT(&cur_acl->expr);
LIST_ADDQ(&cur_acl->expr, &acl_expr->list); LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
if (known_acl) if (known_acl)
@ -1585,7 +1587,7 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v
/* we need to reset context and flags */ /* we need to reset context and flags */
memset(&smp, 0, sizeof(smp)); memset(&smp, 0, sizeof(smp));
fetch_next: fetch_next:
if (!expr->kw->smp->process(px, l4, l7, opt, expr->args, &smp)) { if (!expr->smp->process(px, l4, l7, opt, expr->args, &smp)) {
/* maybe we could not fetch because of missing data */ /* maybe we could not fetch because of missing data */
if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL)) if (smp.flags & SMP_F_MAY_CHANGE && !(opt & SMP_OPT_FINAL))
acl_res |= ACL_PAT_MISS; acl_res |= ACL_PAT_MISS;
@ -1601,9 +1603,9 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v
else { else {
if (!eb_is_empty(&expr->pattern_tree)) { if (!eb_is_empty(&expr->pattern_tree)) {
/* a tree is present, let's check what type it is */ /* a tree is present, let's check what type it is */
if (expr->kw->match == acl_match_str) if (expr->match == acl_match_str)
acl_res |= acl_lookup_str(&smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL; acl_res |= acl_lookup_str(&smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL;
else if (expr->kw->match == acl_match_ip) else if (expr->match == acl_match_ip)
acl_res |= acl_lookup_ip(&smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL; acl_res |= acl_lookup_ip(&smp, expr) ? ACL_PAT_PASS : ACL_PAT_FAIL;
} }
@ -1611,7 +1613,7 @@ int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, v
list_for_each_entry(pattern, &expr->patterns, list) { list_for_each_entry(pattern, &expr->patterns, list) {
if (acl_res == ACL_PAT_PASS) if (acl_res == ACL_PAT_PASS)
break; break;
acl_res |= expr->kw->match(&smp, pattern); acl_res |= expr->match(&smp, pattern);
} }
} }
/* /*
@ -1698,7 +1700,7 @@ int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struc
list_for_each_entry(suite, &cond->suites, list) { list_for_each_entry(suite, &cond->suites, list) {
list_for_each_entry(term, &suite->terms, list) { list_for_each_entry(term, &suite->terms, list) {
list_for_each_entry(expr, &term->acl->expr, list) { list_for_each_entry(expr, &term->acl->expr, list) {
if (!(expr->kw->smp->val & where)) { if (!(expr->smp->val & where)) {
if (acl) if (acl)
*acl = term->acl; *acl = term->acl;
if (kw) if (kw)