summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2016-11-28 00:03:50 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2017-01-03 14:21:53 +0100
commitdeaf962ebd7c6b9d8a161d9378a710031e4f1dd6 (patch)
tree8e885dfedb3eefafa29bd46edc1ebe09f5f8c41c /src
parentb139f738f558d6afb8c8f3e73526f578b059abd6 (diff)
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 <pablo@netfilter.org>
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c74
-rw-r--r--src/netlink.c40
-rw-r--r--src/netlink_delinearize.c29
-rw-r--r--src/netlink_linearize.c26
-rw-r--r--src/parser_bison.y20
-rw-r--r--src/rule.c5
6 files changed, 180 insertions, 14 deletions
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;
diff --git a/src/netlink.c b/src/netlink.c
index 68bed201..97220f45 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 90fb9e67..48968442 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 c9488b32..5030135c 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 795b0ee2..122e2496 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 9eeb436c..b97213e9 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;