]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add support for stateful object maps
authorPablo Neira Ayuso <pablo@netfilter.org>
Sun, 27 Nov 2016 23:03:50 +0000 (00:03 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 3 Jan 2017 13:21:53 +0000 (14:21 +0100)
You can create these maps using explicit map declarations:

 # nft add table filter
 # nft add chain filter input { type filter hook input priority 0\; }
 # nft add map filter badguys { type ipv4_addr : counter \; }
 # nft add rule filter input counter name ip saddr map @badguys
 # nft add counter filter badguy1
 # nft add counter filter badguy2
 # nft add element filter badguys { 192.168.2.3 : "badguy1" }
 # nft add element filter badguys { 192.168.2.4 : "badguy2" }

Or through implicit map definitions:

 table ip filter {
        counter http-traffic {
                packets 8 bytes 672
        }

        chain input {
                type filter hook input priority 0; policy accept;
                counter name tcp dport map { 80 : "http-traffic", 443 : "http-traffic"}
        }
 }

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

index 9028c84b0033d6c9181d3c7fba6615915b23e633..86c0392f89af299fe2558a3c8326ecf4a2627933 100644 (file)
@@ -212,6 +212,7 @@ extern struct rule *rule_lookup(const struct chain *chain, uint64_t handle);
  * @keylen:    key length
  * @datatype:  mapping data type
  * @datalen:   mapping data len
+ * @objtype:   mapping object type
  * @init:      initializer
  * @policy:    set mechanism policy
  * @desc:      set mechanism desc
@@ -228,6 +229,7 @@ struct set {
        unsigned int            keylen;
        const struct datatype   *datatype;
        unsigned int            datalen;
+       uint32_t                objtype;
        struct expr             *init;
        uint32_t                policy;
        struct {
index b868f1bc283aca517ec3aacd2c16cc31094346f5..cebc5a9ead7a4b291c417c0792270d931f5c1224 100644 (file)
@@ -1188,7 +1188,7 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
        if (set == NULL)
                return expr_error(ctx->msgs, mapping,
                                  "mapping outside of map context");
-       if (!(set->flags & NFT_SET_MAP))
+       if (!(set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)))
                return set_error(ctx, set, "set is not a map");
 
        expr_set_context(&ctx->ectx, set->keytype, set->keylen);
@@ -2464,8 +2464,77 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
        return 0;
 }
 
+static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       struct expr *map = stmt->objref.expr;
+       struct expr *mappings;
+
+       expr_set_context(&ctx->ectx, NULL, 0);
+       if (expr_evaluate(ctx, &map->map) < 0)
+               return -1;
+       if (expr_is_constant(map->map))
+               return expr_error(ctx->msgs, map->map,
+                                 "Map expression can not be constant");
+
+       mappings = map->mappings;
+       mappings->set_flags |= NFT_SET_OBJECT;
+
+       switch (map->mappings->ops->type) {
+       case EXPR_SET:
+               mappings = implicit_set_declaration(ctx, "__objmap%d",
+                                                   ctx->ectx.dtype,
+                                                   ctx->ectx.len,
+                                                   mappings);
+               mappings->set->datatype = &string_type;
+               mappings->set->datalen  = NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE;
+               mappings->set->objtype  = stmt->objref.type;
+
+               map->mappings = mappings;
+
+               ctx->set = mappings->set;
+               if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
+                       return -1;
+               ctx->set = NULL;
+
+               map->mappings->set->flags |=
+                       map->mappings->set->init->set_flags;
+       case EXPR_SYMBOL:
+               if (expr_evaluate(ctx, &map->mappings) < 0)
+                       return -1;
+               if (map->mappings->ops->type != EXPR_SET_REF ||
+                   !(map->mappings->set->flags & NFT_SET_OBJECT))
+                       return expr_error(ctx->msgs, map->mappings,
+                                         "Expression is not a map");
+               break;
+       default:
+               BUG("invalid mapping expression %s\n",
+                   map->mappings->ops->name);
+       }
+
+       if (!datatype_equal(map->map->dtype, map->mappings->set->keytype))
+               return expr_binary_error(ctx->msgs, map->mappings, map->map,
+                                        "datatype mismatch, map expects %s, "
+                                        "mapping expression has type %s",
+                                        map->mappings->set->keytype->desc,
+                                        map->map->dtype->desc);
+
+       map->dtype = map->mappings->set->datatype;
+       map->flags |= EXPR_F_CONSTANT;
+
+       /* Data for range lookups needs to be in big endian order */
+       if (map->mappings->set->flags & NFT_SET_INTERVAL &&
+           byteorder_conversion(ctx, &map->map, BYTEORDER_BIG_ENDIAN) < 0)
+               return -1;
+
+       return 0;
+}
+
 static int stmt_evaluate_objref(struct eval_ctx *ctx, struct stmt *stmt)
 {
+       /* We need specific map evaluation for stateful objects. */
+       if (stmt->objref.expr->ops->type == EXPR_MAP)
+               return stmt_evaluate_objref_map(ctx, stmt);
+
        if (stmt_evaluate_arg(ctx, stmt,
                              &string_type, NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE,
                              &stmt->objref.expr) < 0)
@@ -2585,6 +2654,9 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
                if (set->datalen == 0 && set->datatype->type != TYPE_VERDICT)
                        return set_error(ctx, set, "unqualified mapping data "
                                         "type specified in map definition");
+       } else if (set->flags & NFT_SET_OBJECT) {
+               set->datatype = &string_type;
+               set->datalen  = NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE;
        }
 
        ctx->set = set;
index 68bed201a36d856dd9a793f5757c0352e43fa824..97220f451343d59d308404e578ddce5997d77b3b 100644 (file)
@@ -205,7 +205,8 @@ struct nftnl_set *alloc_nftnl_set(const struct handle *h)
        return nls;
 }
 
-static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *expr)
+static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+                                                 const struct expr *expr)
 {
        const struct expr *elem, *key, *data;
        struct nftnl_set_elem *nlse;
@@ -243,8 +244,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *expr)
                                   nftnl_udata_buf_len(udbuf));
                nftnl_udata_buf_free(udbuf);
        }
-
-       if (data != NULL) {
+       if (set->set_flags & NFT_SET_MAP && data != NULL) {
                netlink_gen_data(data, &nld);
                switch (data->ops->type) {
                case EXPR_VERDICT:
@@ -263,6 +263,11 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *expr)
                        break;
                }
        }
+       if (set->set_flags & NFT_SET_OBJECT) {
+               netlink_gen_data(data, &nld);
+               nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_OBJREF,
+                                  nld.value, nld.len);
+       }
 
        if (expr->flags & EXPR_F_INTERVAL_END)
                nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_FLAGS,
@@ -1110,7 +1115,7 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
 {
        struct set *set;
        const struct datatype *keytype, *datatype;
-       uint32_t flags, key, data, data_len;
+       uint32_t flags, key, data, data_len, objtype = 0;
 
        key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE);
        keytype = dtype_map_from_kernel(key);
@@ -1133,6 +1138,11 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
        } else
                datatype = NULL;
 
+       if (flags & NFT_SET_OBJECT) {
+               objtype = nftnl_set_get_u32(nls, NFTNL_SET_OBJ_TYPE);
+               datatype = &string_type;
+       }
+
        set = set_alloc(&netlink_location);
        set->handle.family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
        set->handle.table  = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE));
@@ -1142,6 +1152,8 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
        set->keylen  = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE;
        set->flags   = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
 
+       set->objtype = objtype;
+
        set->datatype = datatype;
        if (nftnl_set_is_set(nls, NFTNL_SET_DATA_LEN)) {
                data_len = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN);
@@ -1214,6 +1226,9 @@ static int netlink_add_set_batch(struct netlink_ctx *ctx,
                nftnl_set_set_u32(nls, NFTNL_SET_DATA_LEN,
                                  set->datalen / BITS_PER_BYTE);
        }
+       if (set->flags & NFT_SET_OBJECT)
+               nftnl_set_set_u32(nls, NFTNL_SET_OBJ_TYPE, set->objtype);
+
        if (set->timeout)
                nftnl_set_set_u64(nls, NFTNL_SET_TIMEOUT, set->timeout);
        if (set->gc_int)
@@ -1357,7 +1372,7 @@ static void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
        const struct expr *expr;
 
        list_for_each_entry(expr, &set->expressions, list) {
-               nlse = alloc_nftnl_setelem(expr);
+               nlse = alloc_nftnl_setelem(set, expr);
                nftnl_set_elem_add(nls, nlse);
        }
 }
@@ -1577,10 +1592,10 @@ static int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
                nle = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_EXPR, NULL);
                expr->stmt = netlink_parse_set_expr(set, nle);
        }
-
-       if (flags & NFT_SET_ELEM_INTERVAL_END) {
+       if (flags & NFT_SET_ELEM_INTERVAL_END)
                expr->flags |= EXPR_F_INTERVAL_END;
-       } else {
+
+       if (set->flags & NFT_SET_MAP) {
                if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_DATA)) {
                        nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_DATA,
                                                       &nld.len);
@@ -1602,6 +1617,15 @@ static int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
 
                expr = mapping_expr_alloc(&netlink_location, expr, data);
        }
+       if (set->flags & NFT_SET_OBJECT) {
+               nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_OBJREF,
+                                              &nld.len);
+               data = netlink_alloc_value(&netlink_location, &nld);
+               data->dtype = &string_type;
+               data->byteorder = BYTEORDER_HOST_ENDIAN;
+               mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE);
+               expr = mapping_expr_alloc(&netlink_location, expr, data);
+       }
 out:
        compound_expr_add(set->init, expr);
        return 0;
index 90fb9e6707514eaefa8682b157b67b1bdb50d444..48968442d9bcc04b4fdc1fc4acbc66d13ddcd9e8 100644 (file)
@@ -1142,6 +1142,35 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
                expr = netlink_alloc_value(&netlink_location, &nld);
                expr->dtype = &string_type;
                expr->byteorder = BYTEORDER_HOST_ENDIAN;
+       } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_OBJREF_SET_SREG)) {
+               struct expr *left, *right;
+               enum nft_registers sreg;
+               const char *name;
+               struct set *set;
+
+               name = nftnl_expr_get_str(nle, NFTNL_EXPR_OBJREF_SET_NAME);
+               set  = set_lookup(ctx->table, name);
+               if (set == NULL)
+                       return netlink_error(ctx, loc,
+                                            "Unknown set '%s' in objref expression",
+                                            name);
+
+               sreg = netlink_parse_register(nle, NFTNL_EXPR_OBJREF_SET_SREG);
+               left = netlink_get_register(ctx, loc, sreg);
+               if (left == NULL)
+                       return netlink_error(ctx, loc,
+                                            "objref expression has no left hand side");
+
+               if (left->len < set->keylen) {
+                       left = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen);
+                       if (left == NULL)
+                               return;
+               }
+
+               right = set_ref_expr_alloc(loc, set);
+               expr = map_expr_alloc(loc, left, right);
+               expr_set_type(expr, &string_type, BYTEORDER_HOST_ENDIAN);
+               type = set->objtype;
        } else {
                netlink_error(ctx, loc, "unknown objref expression type %u",
                              type);
index c9488b3212bc15cf70045177eda1d8b327e42694..5030135cd5d5806bd2fbf092c9c5cae5e6adcf54 100644 (file)
@@ -692,14 +692,34 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
 static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx,
                                    const struct stmt *stmt)
 {
+       struct expr *expr = stmt->objref.expr;
        struct nft_data_linearize nld;
        struct nftnl_expr *nle;
+       uint32_t sreg_key;
 
        nle = alloc_nft_expr("objref");
-       netlink_gen_data(stmt->objref.expr, &nld);
-       nftnl_expr_set(nle, NFTNL_EXPR_OBJREF_IMM_NAME, nld.value, nld.len);
-       nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_IMM_TYPE, stmt->objref.type);
+       switch (expr->ops->type) {
+       case EXPR_MAP:
+               sreg_key = get_register(ctx, expr->map);
+               netlink_gen_expr(ctx, expr->map, sreg_key);
+               release_register(ctx, expr->map);
 
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_SET_SREG, sreg_key);
+               nftnl_expr_set_str(nle, NFTNL_EXPR_OBJREF_SET_NAME,
+                                  expr->mappings->set->handle.set);
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_SET_ID,
+                                  expr->mappings->set->handle.set_id);
+               break;
+       case EXPR_VALUE:
+               netlink_gen_data(stmt->objref.expr, &nld);
+               nftnl_expr_set(nle, NFTNL_EXPR_OBJREF_IMM_NAME,
+                              nld.value, nld.len);
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_IMM_TYPE,
+                                  stmt->objref.type);
+               break;
+       default:
+               BUG("unsupported expression %u\n", expr->ops->type);
+       }
        nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
index 795b0ee210a3d45d0a3f79f6af208d60e7c717a0..122e2496acde499317e06801c1e910ceb876b6a0 100644 (file)
@@ -1218,7 +1218,6 @@ set_flag          :       CONSTANT        { $$ = NFT_SET_CONSTANT; }
 map_block_alloc                :       /* empty */
                        {
                                $$ = set_alloc(NULL);
-                               $$->flags |= NFT_SET_MAP;
                        }
                        ;
 
@@ -1231,6 +1230,25 @@ map_block                :       /* empty */     { $$ = $<set>-1; }
                        {
                                $1->keytype  = $3;
                                $1->datatype = $5;
+                               $1->flags |= NFT_SET_MAP;
+                               $$ = $1;
+                       }
+                       |       map_block       TYPE
+                                               data_type       COLON   COUNTER
+                                               stmt_seperator
+                       {
+                               $1->keytype = $3;
+                               $1->objtype = NFT_OBJECT_COUNTER;
+                               $1->flags  |= NFT_SET_OBJECT;
+                               $$ = $1;
+                       }
+                       |       map_block       TYPE
+                                               data_type       COLON   QUOTA
+                                               stmt_seperator
+                       {
+                               $1->keytype = $3;
+                               $1->objtype = NFT_OBJECT_QUOTA;
+                               $1->flags  |= NFT_SET_OBJECT;
                                $$ = $1;
                        }
                        |       map_block       FLAGS           set_flag_list   stmt_seperator
index 9eeb436cd37a62bfc90df01e8119d72666eb5177..b97213e94954d22c3a0ca567ecf120fa1b653879 100644 (file)
@@ -273,7 +273,7 @@ static void set_print_declaration(const struct set *set,
        const char *type;
        uint32_t flags;
 
-       if (set->flags & NFT_SET_MAP)
+       if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
                type = "map";
        else if (set->flags & NFT_SET_EVAL)
                type = "flow table";
@@ -293,6 +293,8 @@ static void set_print_declaration(const struct set *set,
        printf("%s%stype %s", opts->tab, opts->tab, set->keytype->name);
        if (set->flags & NFT_SET_MAP)
                printf(" : %s", set->datatype->name);
+       else if (set->flags & NFT_SET_OBJECT)
+               printf(" : %s", obj_type_name(set->objtype));
 
        printf("%s", opts->stmt_separator);
 
@@ -913,6 +915,7 @@ static int __do_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
            set_to_intervals(ctx->msgs, set, expr, true) < 0)
                return -1;
 
+       expr->set_flags |= set->flags;
        if (netlink_add_setelems(ctx, h, expr, excl) < 0)
                return -1;