]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add variable expression and use it to allow redefinitions
authorPablo Neira Ayuso <pablo@netfilter.org>
Sat, 3 Mar 2018 21:52:35 +0000 (22:52 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sat, 3 Mar 2018 23:22:50 +0000 (00:22 +0100)
Add new variable expression that we can use to attach symbols in
runtime, this allows us to redefine variables via new keyword, eg.

 table ip x {
        chain y {
                define address = { 1.1.1.1, 2.2.2.2 }
                ip saddr $address
                redefine address = { 3.3.3.3 }
                ip saddr $address
        }
 }

 # nft list ruleset
 table ip x {
        chain y {
                ip saddr { 1.1.1.1, 2.2.2.2 }
                ip saddr { 3.3.3.3 }
        }
 }

Note that redefinition just places a new symbol version before the
existing one, so symbol lookups always find the latest version. The
undefine keyword decrements the reference counter and removes the symbol
from the list, so it cannot be used anymore. Still, previous references
to this symbol via variable expression are still valid.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/expression.h
include/rule.h
src/evaluate.c
src/expression.c
src/parser_bison.y
src/rule.c

index 26182120f63dbd30829a53eda16c95ff1976bc24..7b9b62297150b7be8817a5319b3588baed33947a 100644 (file)
@@ -16,6 +16,7 @@
  * @EXPR_INVALID:      uninitialized type, should not happen
  * @EXPR_VERDICT:      nftables verdict expression
  * @EXPR_SYMBOL:       unparsed symbol
+ * @EXPR_VARIABLE:     variable
  * @EXPR_VALUE:                literal numeric or string expression
  * @EXPR_PREFIX:       prefixed expression
  * @EXPR_RANGE:                literal range
@@ -41,6 +42,7 @@ enum expr_types {
        EXPR_INVALID,
        EXPR_VERDICT,
        EXPR_SYMBOL,
+       EXPR_VARIABLE,
        EXPR_VALUE,
        EXPR_PREFIX,
        EXPR_RANGE,
@@ -97,7 +99,6 @@ extern const char *expr_op_symbols[];
 
 enum symbol_types {
        SYMBOL_VALUE,
-       SYMBOL_DEFINE,
        SYMBOL_SET,
 };
 
@@ -225,6 +226,10 @@ struct expr {
                        const char              *identifier;
                        enum symbol_types       symtype;
                };
+               struct {
+                       /* EXPR_VARIABLE */
+                       struct symbol           *sym;
+               };
                struct {
                        /* EXPR_VERDICT */
                        int                     verdict;
@@ -387,6 +392,9 @@ static inline void symbol_expr_set_type(struct expr *expr,
                expr->dtype = dtype;
 }
 
+struct expr *variable_expr_alloc(const struct location *loc,
+                                struct scope *scope, struct symbol *sym);
+
 extern struct expr *constant_expr_alloc(const struct location *loc,
                                        const struct datatype *dtype,
                                        enum byteorder byteorder,
index 531222ce1efc50b49effb405a41204334b9101fe..58c4aeef700e4305c1acaba665ee8bacae4b630a 100644 (file)
@@ -73,18 +73,21 @@ extern void scope_release(const struct scope *scope);
  * @list:      scope symbol list node
  * @identifier:        identifier
  * @expr:      initializer
+ * @refcnt:    reference counter
  */
 struct symbol {
        struct list_head        list;
        const char              *identifier;
        struct expr             *expr;
+       int                     refcnt;
 };
 
 extern void symbol_bind(struct scope *scope, const char *identifier,
                        struct expr *expr);
-extern int symbol_unbind(struct scope *scope, const char *identifier);
+extern int symbol_unbind(const struct scope *scope, const char *identifier);
 extern struct symbol *symbol_lookup(const struct scope *scope,
                                    const char *identifier);
+struct symbol *symbol_get(const struct scope *scope, const char *identifier);
 
 enum table_flags {
        TABLE_F_DORMANT         = (1 << 0),
index 40a9292fe53af843d7b55bdd74a5ce98eaa9cae4..cc8eac83f1d988017debde63d97731b425e1d0c6 100644 (file)
@@ -168,7 +168,6 @@ static struct table *table_lookup_global(struct eval_ctx *ctx)
 static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
 {
        struct error_record *erec;
-       struct symbol *sym;
        struct table *table;
        struct set *set;
        struct expr *new;
@@ -183,14 +182,6 @@ static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
                        return -1;
                }
                break;
-       case SYMBOL_DEFINE:
-               sym = symbol_lookup((*expr)->scope, (*expr)->identifier);
-               if (sym == NULL)
-                       return expr_error(ctx->msgs, *expr,
-                                         "undefined identifier '%s'",
-                                         (*expr)->identifier);
-               new = expr_clone(sym->expr);
-               break;
        case SYMBOL_SET:
                ret = cache_update(ctx->nf_sock, ctx->cache, ctx->cmd->op,
                                   ctx->msgs, ctx->debug_mask & NFT_DEBUG_NETLINK, ctx->octx);
@@ -1776,6 +1767,16 @@ static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
        return expr_evaluate_primary(ctx, exprp);
 }
 
+static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
+{
+       struct expr *new = expr_clone((*exprp)->sym->expr);
+
+       expr_free(*exprp);
+       *exprp = new;
+
+       return expr_evaluate(ctx, exprp);
+}
+
 static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 {
        if (ctx->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -1791,6 +1792,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
        switch ((*expr)->ops->type) {
        case EXPR_SYMBOL:
                return expr_evaluate_symbol(ctx, expr);
+       case EXPR_VARIABLE:
+               return expr_evaluate_variable(ctx, expr);
        case EXPR_SET_REF:
                return 0;
        case EXPR_VALUE:
index 393c1b2b2cfeb3d8b71e1826096157e6c94f5488..ee75284e79b4ecda693c50439b297d3084e9212c 100644 (file)
@@ -254,6 +254,45 @@ struct expr *symbol_expr_alloc(const struct location *loc,
        return expr;
 }
 
+static void variable_expr_print(const struct expr *expr,
+                               struct output_ctx *octx)
+{
+       nft_print(octx, "$%s", expr->sym->identifier);
+}
+
+static void variable_expr_clone(struct expr *new, const struct expr *expr)
+{
+       new->scope      = expr->scope;
+       new->sym        = expr->sym;
+
+       expr->sym->refcnt++;
+}
+
+static void variable_expr_destroy(struct expr *expr)
+{
+       expr->sym->refcnt--;
+}
+
+static const struct expr_ops variable_expr_ops = {
+       .type           = EXPR_VARIABLE,
+       .name           = "variable",
+       .print          = variable_expr_print,
+       .clone          = variable_expr_clone,
+       .destroy        = variable_expr_destroy,
+};
+
+struct expr *variable_expr_alloc(const struct location *loc,
+                                struct scope *scope, struct symbol *sym)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &variable_expr_ops, &invalid_type,
+                         BYTEORDER_INVALID, 0);
+       expr->scope      = scope;
+       expr->sym        = sym;
+       return expr;
+}
+
 static void constant_expr_print(const struct expr *expr,
                                 struct output_ctx *octx)
 {
index 58bc680503d7c632d7b31d6956334cd06175bdcb..ee6729f11abdcf6e80ff1fba9a50abe1c1694ad5 100644 (file)
@@ -771,8 +771,6 @@ common_block                :       INCLUDE         QUOTED_STRING   stmt_separator
                        {
                                struct scope *scope = current_scope(state);
 
-                               /* ignore missing identifier */
-                               symbol_unbind(scope, $2);
                                symbol_bind(scope, $2, $4);
                                xfree($2);
                        }
@@ -2584,16 +2582,17 @@ match_stmt              :       relational_expr
 variable_expr          :       '$'     identifier
                        {
                                struct scope *scope = current_scope(state);
+                               struct symbol *sym;
 
-                               if (symbol_lookup(scope, $2) == NULL) {
+                               sym = symbol_get(scope, $2);
+                               if (!sym) {
                                        erec_queue(error(&@2, "unknown identifier '%s'", $2),
                                                   state->msgs);
                                        xfree($2);
                                        YYERROR;
                                }
 
-                               $$ = symbol_expr_alloc(&@$, SYMBOL_DEFINE,
-                                                      scope, $2);
+                               $$ = variable_expr_alloc(&@$, scope, sym);
                                xfree($2);
                        }
                        ;
index c0b42b1c186b06f0e06ddea585e9ad81396168b3..b331416ac0666b0dc9689f4c0b8e4aa41d500e29 100644 (file)
@@ -467,6 +467,7 @@ void scope_release(const struct scope *scope)
        struct symbol *sym, *next;
 
        list_for_each_entry_safe(sym, next, &scope->symbols, list) {
+               assert(sym->refcnt == 1);
                list_del(&sym->list);
                xfree(sym->identifier);
                expr_free(sym->expr);
@@ -481,22 +482,48 @@ void symbol_bind(struct scope *scope, const char *identifier, struct expr *expr)
        sym = xzalloc(sizeof(*sym));
        sym->identifier = xstrdup(identifier);
        sym->expr = expr;
+       sym->refcnt = 1;
 
-       list_add_tail(&sym->list, &scope->symbols);
+       list_add(&sym->list, &scope->symbols);
 }
 
-int symbol_unbind(struct scope *scope, const char *identifier)
+struct symbol *symbol_get(const struct scope *scope, const char *identifier)
 {
        struct symbol *sym;
 
        sym = symbol_lookup(scope, identifier);
        if (!sym)
-               return -1;
+               return NULL;
+
+       sym->refcnt++;
 
+       return sym;
+}
+
+static void symbol_put(struct symbol *sym)
+{
+       if (--sym->refcnt == 0) {
+               xfree(sym->identifier);
+               expr_free(sym->expr);
+               xfree(sym);
+       }
+}
+
+static void symbol_remove(struct symbol *sym)
+{
        list_del(&sym->list);
-       xfree(sym->identifier);
-       expr_free(sym->expr);
-       xfree(sym);
+       symbol_put(sym);
+}
+
+int symbol_unbind(const struct scope *scope, const char *identifier)
+{
+       struct symbol *sym, *next;
+
+       list_for_each_entry_safe(sym, next, &scope->symbols, list) {
+               if (!strcmp(sym->identifier, identifier))
+                       symbol_remove(sym);
+       }
+
        return 0;
 }