[PATCH nft 09/10] src: add support for stateful object maps

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxx>
---
 include/rule.h            |  2 ++
 src/evaluate.c            | 74 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/netlink.c             | 40 ++++++++++++++++++++-----
 src/netlink_delinearize.c | 29 +++++++++++++++++++
 src/netlink_linearize.c   | 26 +++++++++++++++--
 src/parser_bison.y        | 20 ++++++++++++-
 src/rule.c                |  5 +++-
 7 files changed, 182 insertions(+), 14 deletions(-)

diff --git a/include/rule.h b/include/rule.h
index 9028c84b0033..86c0392f89af 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -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 {
diff --git a/src/evaluate.c b/src/evaluate.c
index b868f1bc283a..cebc5a9ead7a 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;
diff --git a/src/netlink.c b/src/netlink.c
index 68bed201a36d..97220f451343 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -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;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 90fb9e670751..48968442d9bc 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -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);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c9488b3212bc..5030135cd5d5 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -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);
 }
 
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 795b0ee210a3..122e2496acde 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -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
diff --git a/src/rule.c b/src/rule.c
index 9eeb436cd37a..b97213e94954 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -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;
 
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux