]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: store expr, not dtype to track data in sets
authorFlorian Westphal <fw@strlen.de>
Tue, 16 Jul 2019 17:03:55 +0000 (19:03 +0200)
committerFlorian Westphal <fw@strlen.de>
Mon, 16 Dec 2019 16:06:18 +0000 (17:06 +0100)
This will be needed once we add support for the 'typeof' keyword to
handle maps that could e.g. store 'ct helper' "type" values.

Instead of:

set foo {
type ipv4_addr . mark;

this would allow

set foo {
typeof(ip saddr) . typeof(ct mark);

(exact syntax TBD).

This would be needed to allow sets that store variable-sized data types
(string, integer and the like) that can't be used at at the moment.

Adding special data types for everything is problematic due to the
large amount of different types needed.

For anonymous sets, e.g. "string" can be used because the needed size can
be inferred from the statement, e.g.  'osf name { "Windows", "Linux }',
but in case of named sets that won't work because 'type string' lacks the
context needed to derive the size information.

With 'typeof(osf name)' the context is there, but at the moment it won't
help because the expression is discarded instantly and only the data
type is retained.

Signed-off-by: Florian Westphal <fw@strlen.de>
14 files changed:
include/datatype.h
include/netlink.h
include/rule.h
src/datatype.c
src/evaluate.c
src/expression.c
src/json.c
src/mnl.c
src/monitor.c
src/netlink.c
src/parser_bison.y
src/parser_json.c
src/rule.c
src/segtree.c

index 49b8f608aa1d7593b29a67b3d21ebd1dc0f760ba..04b4892b29acd4dc991ee7910252ff957c2d32b1 100644 (file)
@@ -293,7 +293,6 @@ concat_subtype_lookup(uint32_t type, unsigned int n)
 
 extern const struct datatype *
 set_datatype_alloc(const struct datatype *orig_dtype, unsigned int byteorder);
-extern void set_datatype_destroy(const struct datatype *dtype);
 
 extern void time_print(uint64_t msec, struct output_ctx *octx);
 extern struct error_record *time_parse(const struct location *loc,
index 53a55b61e4de6be6ff4d6dabf4c171a14c807df0..d02533ec4430b8834a5cc0fb364126e923819e43 100644 (file)
@@ -189,6 +189,5 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
                            struct netlink_mon_handler *monh);
 
 enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype);
-const struct datatype *dtype_map_from_kernel(enum nft_data_types type);
 
 #endif /* NFTABLES_NETLINK_H */
index dadeb4b941dad81d736f7bfc46d377468bb61a61..ce1f40db031ee43eb5a1bc4f1fee17ba382af783 100644 (file)
@@ -283,8 +283,7 @@ extern struct rule *rule_lookup_by_index(const struct chain *chain,
  * @gc_int:    garbage collection interval
  * @timeout:   default timeout value
  * @key:       key expression (data type, length))
- * @datatype:  mapping data type
- * @datalen:   mapping data len
+ * @data:      mapping data expression
  * @objtype:   mapping object type
  * @init:      initializer
  * @rg_cache:  cached range element (left)
@@ -301,8 +300,7 @@ struct set {
        uint32_t                gc_int;
        uint64_t                timeout;
        struct expr             *key;
-       const struct datatype   *datatype;
-       unsigned int            datalen;
+       struct expr             *data;
        uint32_t                objtype;
        struct expr             *init;
        struct expr             *rg_cache;
index b9e167e03765bd9b07a7e992895defe466e79913..189e1b482c29f1a7eed03e5b79f60d6ef1f31a4f 100644 (file)
@@ -1190,11 +1190,6 @@ const struct datatype *set_datatype_alloc(const struct datatype *orig_dtype,
        return dtype;
 }
 
-void set_datatype_destroy(const struct datatype *dtype)
-{
-       datatype_free(dtype);
-}
-
 static struct error_record *time_unit_parse(const struct location *loc,
                                            const char *str, uint64_t *unit)
 {
index a865902c0fc7bbdda53a55fc77defcbea2632dc7..91d6b254c6590021256e9895c24121f5e6848335 100644 (file)
@@ -1367,6 +1367,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
 {
        struct expr_ctx ectx = ctx->ectx;
        struct expr *map = *expr, *mappings;
+       const struct datatype *dtype;
        struct expr *key;
 
        expr_set_context(&ctx->ectx, NULL, 0);
@@ -1389,10 +1390,14 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
                mappings = implicit_set_declaration(ctx, "__map%d",
                                                    key,
                                                    mappings);
-               mappings->set->datatype =
-                       datatype_get(set_datatype_alloc(ectx.dtype,
-                                                       ectx.byteorder));
-               mappings->set->datalen  = ectx.len;
+
+               dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
+
+               mappings->set->data = constant_expr_alloc(&netlink_location,
+                                                         dtype, dtype->byteorder,
+                                                         ectx.len, NULL);
+               if (ectx.len && mappings->set->data->len != ectx.len)
+                       BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
 
                map->mappings = mappings;
 
@@ -1428,7 +1433,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
                                         map->mappings->set->key->dtype->desc,
                                         map->map->dtype->desc);
 
-       datatype_set(map, map->mappings->set->datatype);
+       datatype_set(map, map->mappings->set->data->dtype);
        map->flags |= EXPR_F_CONSTANT;
 
        /* Data for range lookups needs to be in big endian order */
@@ -1458,7 +1463,12 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
                                  "Key must be a constant");
        mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON;
 
-       expr_set_context(&ctx->ectx, set->datatype, set->datalen);
+       if (set->data) {
+               expr_set_context(&ctx->ectx, set->data->dtype, set->data->len);
+       } else {
+               assert((set->flags & NFT_SET_MAP) == 0);
+       }
+
        if (expr_evaluate(ctx, &mapping->right) < 0)
                return -1;
        if (!expr_is_constant(mapping->right))
@@ -2103,7 +2113,7 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
                                         (*expr)->len);
        else if ((*expr)->dtype->type != TYPE_INTEGER &&
                 !datatype_equal((*expr)->dtype, dtype))
-               return stmt_binary_error(ctx, *expr, stmt,
+               return stmt_binary_error(ctx, *expr, stmt,              /* verdict vs invalid? */
                                         "datatype mismatch: expected %s, "
                                         "expression has type %s",
                                         dtype->desc, (*expr)->dtype->desc);
@@ -3097,9 +3107,9 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
                                  "Key expression comments are not supported");
 
        if (stmt_evaluate_arg(ctx, stmt,
-                             stmt->map.set->set->datatype,
-                             stmt->map.set->set->datalen,
-                             stmt->map.set->set->datatype->byteorder,
+                             stmt->map.set->set->data->dtype,
+                             stmt->map.set->set->data->len,
+                             stmt->map.set->set->data->byteorder,
                              &stmt->map.data->key) < 0)
                return -1;
        if (expr_is_constant(stmt->map.data))
@@ -3145,8 +3155,12 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
 
                mappings = implicit_set_declaration(ctx, "__objmap%d",
                                                    key, mappings);
-               mappings->set->datatype = &string_type;
-               mappings->set->datalen  = NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE;
+
+               mappings->set->data = constant_expr_alloc(&netlink_location,
+                                                         &string_type,
+                                                         BYTEORDER_HOST_ENDIAN,
+                                                         NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE,
+                                                         NULL);
                mappings->set->objtype  = stmt->objref.type;
 
                map->mappings = mappings;
@@ -3181,7 +3195,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
                                         map->mappings->set->key->dtype->desc,
                                         map->map->dtype->desc);
 
-       datatype_set(map, map->mappings->set->datatype);
+       datatype_set(map, map->mappings->set->data->dtype);
        map->flags |= EXPR_F_CONSTANT;
 
        /* Data for range lookups needs to be in big endian order */
@@ -3326,17 +3340,25 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
                return set_error(ctx, set, "concatenated types not supported in interval sets");
 
        if (set_is_datamap(set->flags)) {
-               if (set->datatype == NULL)
+               if (set->data == NULL)
                        return set_error(ctx, set, "map definition does not "
                                         "specify mapping data type");
 
-               set->datalen = set->datatype->size;
-               if (set->datalen == 0 && set->datatype->type != TYPE_VERDICT)
+               if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT)
                        return set_error(ctx, set, "unqualified mapping data "
                                         "type specified in map definition");
        } else if (set_is_objmap(set->flags)) {
-               set->datatype = &string_type;
-               set->datalen  = NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE;
+               if (set->data) {
+                       assert(set->data->etype == EXPR_VALUE);
+                       assert(set->data->dtype == &string_type);
+               }
+
+               assert(set->data == NULL);
+               set->data = constant_expr_alloc(&netlink_location, &string_type,
+                                               BYTEORDER_HOST_ENDIAN,
+                                               NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE,
+                                               NULL);
+
        }
 
        ctx->set = set;
index 5070b1014392cc16eb103ee953f3d1c64bdbf4dc..6fa2f1dd9b1204fc374de84326dad885ec5cee4f 100644 (file)
@@ -1010,7 +1010,7 @@ static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
 {
        expr_print(expr->map, octx);
        if (expr->mappings->etype == EXPR_SET_REF &&
-           expr->mappings->set->datatype->type == TYPE_VERDICT)
+           expr->mappings->set->data->dtype->type == TYPE_VERDICT)
                nft_print(octx, " vmap ");
        else
                nft_print(octx, " map ");
index 3498e24db363fe153b83536e2752e2f42522e651..1906e7db7c1957b2289136621b4ea511e3bcbaf8 100644 (file)
@@ -82,7 +82,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
 
        if (set_is_datamap(set->flags)) {
                type = "map";
-               datatype_ext = set->datatype->name;
+               datatype_ext = set->data->dtype->name;
        } else if (set_is_objmap(set->flags)) {
                type = "map";
                datatype_ext = obj_type_name(set->objtype);
@@ -645,7 +645,7 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx)
        const char *type = "map";
 
        if (expr->mappings->etype == EXPR_SET_REF &&
-           expr->mappings->set->datatype->type == TYPE_VERDICT)
+           expr->mappings->set->data->dtype->type == TYPE_VERDICT)
                type = "vmap";
 
        return json_pack("{s:{s:o, s:o}}", type,
index aa5b0b4652e82310afeecd5afc04c5e34d667989..06b790c01acc6a3b59f3f54b9ecf5592d7c651b6 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -839,9 +839,9 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
                          div_round_up(set->key->len, BITS_PER_BYTE));
        if (set_is_datamap(set->flags)) {
                nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,
-                                 dtype_map_to_kernel(set->datatype));
+                                 dtype_map_to_kernel(set->data->dtype));
                nftnl_set_set_u32(nls, NFTNL_SET_DATA_LEN,
-                                 set->datalen / BITS_PER_BYTE);
+                                 set->data->len / BITS_PER_BYTE);
        }
        if (set_is_objmap(set->flags))
                nftnl_set_set_u32(nls, NFTNL_SET_OBJ_TYPE, set->objtype);
@@ -873,7 +873,7 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
 
        if (set_is_datamap(set->flags) &&
            !nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATABYTEORDER,
-                                set->datatype->byteorder))
+                                set->data->byteorder))
                memory_allocation_error();
 
        if (set->automerge &&
index ea0393cddff41c3d3e625848045aadf57624dc63..d586cfa34a979d289064ce0c60d82df4dda316be 100644 (file)
@@ -401,7 +401,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
         */
        dummyset = set_alloc(monh->loc);
        dummyset->key = expr_clone(set->key);
-       dummyset->datatype = set->datatype;
+       dummyset->data = set->data;
        dummyset->flags = set->flags;
        dummyset->init = set_expr_alloc(monh->loc, set);
 
index 9fc0b17194a07ee8e78f99ab5c82592a668ebffa..46897e43e72339f755d237d03e55b35a9a70b576 100644 (file)
@@ -534,7 +534,7 @@ enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype)
        }
 }
 
-const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
+static const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
 {
        switch (type) {
        case NFT_DATA_VERDICT:
@@ -581,10 +581,10 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
                                    const struct nftnl_set *nls)
 {
        const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {};
-       uint32_t flags, key, data, data_len, objtype = 0;
        enum byteorder keybyteorder = BYTEORDER_INVALID;
        enum byteorder databyteorder = BYTEORDER_INVALID;
-       const struct datatype *keytype, *datatype;
+       const struct datatype *keytype, *datatype = NULL;
+       uint32_t flags, key, objtype = 0;
        bool automerge = false;
        const char *udata;
        struct set *set;
@@ -618,6 +618,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
 
        flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
        if (set_is_datamap(flags)) {
+               uint32_t data;
+
                data = nftnl_set_get_u32(nls, NFTNL_SET_DATA_TYPE);
                datatype = dtype_map_from_kernel(data);
                if (datatype == NULL) {
@@ -626,8 +628,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
                                         data);
                        return NULL;
                }
-       } else
-               datatype = NULL;
+       }
 
        if (set_is_objmap(flags)) {
                objtype = nftnl_set_get_u32(nls, NFTNL_SET_OBJ_TYPE);
@@ -650,16 +651,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
 
        set->objtype = objtype;
 
+       set->data = NULL;
        if (datatype)
-               set->datatype = datatype_get(set_datatype_alloc(datatype,
-                                                               databyteorder));
-       else
-               set->datatype = NULL;
-
-       if (nftnl_set_is_set(nls, NFTNL_SET_DATA_LEN)) {
-               data_len = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN);
-               set->datalen = data_len * BITS_PER_BYTE;
-       }
+               set->data = constant_expr_alloc(&netlink_location,
+                                               set_datatype_alloc(datatype, databyteorder),
+                                               databyteorder,
+                                               nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE,
+                                               NULL);
 
        if (nftnl_set_is_set(nls, NFTNL_SET_TIMEOUT))
                set->timeout = nftnl_set_get_u64(nls, NFTNL_SET_TIMEOUT);
@@ -847,10 +845,10 @@ int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
                        goto out;
 
                data = netlink_alloc_data(&netlink_location, &nld,
-                                         set->datatype->type == TYPE_VERDICT ?
+                                         set->data->dtype->type == TYPE_VERDICT ?
                                          NFT_REG_VERDICT : NFT_REG_1);
-               datatype_set(data, set->datatype);
-               data->byteorder = set->datatype->byteorder;
+               datatype_set(data, set->data->dtype);
+               data->byteorder = set->data->byteorder;
                if (data->byteorder == BYTEORDER_HOST_ENDIAN)
                        mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE);
 
index 0fd9b94c5b2f783122d102bc89f27dc8cba90bfd..6d17539fa57e16644f1e6dccf262ce1a49e902fb 100644 (file)
@@ -1749,9 +1749,8 @@ map_block         :       /* empty */     { $$ = $<set>-1; }
                                                stmt_separator
                        {
                                $1->key = $3;
-                               $1->datatype = $5->dtype;
+                               $1->data = $5;
 
-                               expr_free($5);
                                $1->flags |= NFT_SET_MAP;
                                $$ = $1;
                        }
index 031930e2d7088fb3d564578df09f9df0faab3e6e..85082ccee7ef61cf6534f8d6bbd200cec8485f1e 100644 (file)
@@ -2826,11 +2826,15 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
        }
 
        if (!json_unpack(root, "{s:s}", "map", &dtype_ext)) {
+               const struct datatype *dtype;
+
                set->objtype = string_to_nft_object(dtype_ext);
                if (set->objtype) {
                        set->flags |= NFT_SET_OBJECT;
-               } else if (datatype_lookup_byname(dtype_ext)) {
-                       set->datatype = datatype_lookup_byname(dtype_ext);
+               } else if ((dtype = datatype_lookup_byname(dtype_ext))) {
+                       set->data = constant_expr_alloc(&netlink_location,
+                                                       dtype, dtype->byteorder,
+                                                       dtype->size, NULL);
                        set->flags |= NFT_SET_MAP;
                } else {
                        json_error(ctx, "Invalid map type '%s'.", dtype_ext);
index d985d3a2316e45a1c598795d5a2439ec54c637b0..f8cd4a73054bce49d0688c27708e9aaab7bc5bd5 100644 (file)
@@ -332,8 +332,8 @@ struct set *set_clone(const struct set *set)
        new_set->gc_int         = set->gc_int;
        new_set->timeout        = set->timeout;
        new_set->key            = expr_clone(set->key);
-       new_set->datatype       = datatype_get(set->datatype);
-       new_set->datalen        = set->datalen;
+       if (set->data)
+               new_set->data   = expr_clone(set->data);
        new_set->objtype        = set->objtype;
        new_set->policy         = set->policy;
        new_set->automerge      = set->automerge;
@@ -356,7 +356,7 @@ void set_free(struct set *set)
                expr_free(set->init);
        handle_free(&set->handle);
        expr_free(set->key);
-       set_datatype_destroy(set->datatype);
+       expr_free(set->data);
        xfree(set);
 }
 
@@ -469,7 +469,7 @@ static void set_print_declaration(const struct set *set,
        nft_print(octx, "%s%stype %s",
                  opts->tab, opts->tab, set->key->dtype->name);
        if (set_is_datamap(set->flags))
-               nft_print(octx, " : %s", set->datatype->name);
+               nft_print(octx, " : %s", set->data->dtype->name);
        else if (set_is_objmap(set->flags))
                nft_print(octx, " : %s", obj_type_name(set->objtype));
 
index d1dbe10c81a7de566767a4a413342b99188a0353..e8e32412f3a41471c01d8919068264873b5447c1 100644 (file)
@@ -79,8 +79,12 @@ static void seg_tree_init(struct seg_tree *tree, const struct set *set,
        tree->root      = RB_ROOT;
        tree->keytype   = set->key->dtype;
        tree->keylen    = set->key->len;
-       tree->datatype  = set->datatype;
-       tree->datalen   = set->datalen;
+       tree->datatype  = NULL;
+       tree->datalen   = 0;
+       if (set->data) {
+               tree->datatype  = set->data->dtype;
+               tree->datalen   = set->data->len;
+       }
        tree->byteorder = first->byteorder;
        tree->debug_mask = debug_mask;
 }