From deaf962ebd7c6b9d8a161d9378a710031e4f1dd6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:03:50 +0100 Subject: src: add support for stateful object maps 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 --- src/evaluate.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'src/evaluate.c') diff --git a/src/evaluate.c b/src/evaluate.c index b868f1bc..cebc5a9e 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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; -- cgit v1.2.3