summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am10
-rw-r--r--src/cache.c6
-rw-r--r--src/cmd.c159
-rw-r--r--src/ct.c2
-rw-r--r--src/datatype.c3
-rw-r--r--src/evaluate.c353
-rw-r--r--src/expression.c173
-rw-r--r--src/json.c4
-rw-r--r--src/libnftables.c12
-rw-r--r--src/main.c295
-rw-r--r--src/mnl.c398
-rw-r--r--src/monitor.c3
-rw-r--r--src/netlink.c184
-rw-r--r--src/netlink_delinearize.c96
-rw-r--r--src/netlink_linearize.c75
-rw-r--r--src/parser_bison.y177
-rw-r--r--src/parser_json.c74
-rw-r--r--src/rule.c103
-rw-r--r--src/scanner.l3
-rw-r--r--src/segtree.c97
-rw-r--r--src/statement.c7
-rw-r--r--src/xt.c4
22 files changed, 1720 insertions, 518 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 740c21f2..3041a933 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,6 +13,15 @@ endif
if BUILD_XTABLES
AM_CPPFLAGS += ${XTABLES_CFLAGS}
endif
+if BUILD_MINIGMP
+AM_CPPFLAGS += -DHAVE_MINIGMP
+endif
+if BUILD_JSON
+AM_CPPFLAGS += -DHAVE_JSON
+endif
+if BUILD_XTABLES
+AM_CPPFLAGS += -DHAVE_XTABLES
+endif
AM_CFLAGS = -Wall \
-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
@@ -32,6 +41,7 @@ libnftables_la_SOURCES = \
rule.c \
statement.c \
cache.c \
+ cmd.c \
datatype.c \
expression.c \
evaluate.c \
diff --git a/src/cache.c b/src/cache.c
index 05f0d68e..a45111a7 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -25,7 +25,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
case CMD_OBJ_FLOWTABLE:
flags |= NFT_CACHE_TABLE;
break;
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_TABLE |
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
@@ -53,7 +53,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_SETELEM;
break;
default:
@@ -66,7 +66,7 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_TABLE |
NFT_CACHE_SET |
NFT_CACHE_SETELEM;
diff --git a/src/cmd.c b/src/cmd.c
new file mode 100644
index 00000000..c8ea4492
--- /dev/null
+++ b/src/cmd.c
@@ -0,0 +1,159 @@
+#include <erec.h>
+#include <mnl.h>
+#include <cmd.h>
+#include <parser.h>
+#include <utils.h>
+#include <iface.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc)
+{
+ struct table *table;
+
+ table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache);
+ if (!table)
+ return 0;
+
+ netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ strerror(ENOENT), table->handle.table.name,
+ family2str(table->handle.family));
+ return 1;
+}
+
+static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc)
+{
+ const struct table *table;
+ struct chain *chain;
+
+ chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
+ if (!chain)
+ return 0;
+
+ netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ strerror(ENOENT), chain->handle.chain.name,
+ family2str(table->handle.family),
+ table->handle.table.name);
+ return 1;
+}
+
+static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc)
+{
+ const struct table *table;
+ struct set *set;
+
+ set = set_lookup_fuzzy(cmd->handle.set.name, &ctx->nft->cache, &table);
+ if (!set)
+ return 0;
+
+ netlink_io_error(ctx, loc, "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ strerror(ENOENT),
+ set_is_map(set->flags) ? "map" : "set",
+ set->handle.set.name,
+ family2str(set->handle.family),
+ table->handle.table.name);
+ return 1;
+}
+
+static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc)
+{
+ const struct table *table;
+ struct obj *obj;
+
+ obj = obj_lookup_fuzzy(cmd->handle.obj.name, &ctx->nft->cache, &table);
+ if (!obj)
+ return 0;
+
+ netlink_io_error(ctx, loc, "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ strerror(ENOENT), obj->handle.obj.name,
+ family2str(obj->handle.family),
+ table->handle.table.name);
+ return 1;
+}
+
+static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
+ const struct cmd *cmd, struct location *loc)
+{
+ const struct table *table;
+ struct flowtable *ft;
+
+ ft = flowtable_lookup_fuzzy(cmd->handle.flowtable.name,
+ &ctx->nft->cache, &table);
+ if (!ft)
+ return 0;
+
+ netlink_io_error(ctx, loc, "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ strerror(ENOENT), ft->handle.flowtable.name,
+ family2str(ft->handle.family),
+ table->handle.table.name);
+ return 1;
+}
+
+static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc, int err)
+{
+ int ret = 0;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ ret = nft_cmd_enoent_table(ctx, cmd, loc);
+ break;
+ case CMD_OBJ_CHAIN:
+ ret = nft_cmd_enoent_chain(ctx, cmd, loc);
+ break;
+ case CMD_OBJ_SET:
+ ret = nft_cmd_enoent_set(ctx, cmd, loc);
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_SYNPROXY:
+ ret = nft_cmd_enoent_obj(ctx, cmd, loc);
+ break;
+ case CMD_OBJ_FLOWTABLE:
+ ret = nft_cmd_enoent_flowtable(ctx, cmd, loc);
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ return;
+
+ netlink_io_error(ctx, loc, "Could not process rule: %s", strerror(err));
+}
+
+void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct mnl_err *err)
+{
+ struct location *loc = NULL;
+ int i;
+
+ for (i = 0; i < cmd->num_attrs; i++) {
+ if (!cmd->attr[i].offset)
+ break;
+ if (cmd->attr[i].offset == err->offset)
+ loc = cmd->attr[i].location;
+ }
+
+ if (loc) {
+ if (err->err == ENOENT) {
+ nft_cmd_enoent(ctx, cmd, loc, err->err);
+ return;
+ }
+ } else {
+ loc = &cmd->location;
+ }
+
+ netlink_io_error(ctx, loc, "Could not process rule: %s",
+ strerror(err->err));
+}
diff --git a/src/ct.c b/src/ct.c
index db1dabd3..0842c838 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -301,6 +301,8 @@ const struct ct_template ct_templates[__NFT_CT_MAX] = {
BYTEORDER_BIG_ENDIAN, 128),
[NFT_CT_SECMARK] = CT_TEMPLATE("secmark", &integer_type,
BYTEORDER_HOST_ENDIAN, 32),
+ [NFT_CT_ID] = CT_TEMPLATE("id", &integer_type,
+ BYTEORDER_BIG_ENDIAN, 32),
};
static void ct_print(enum nft_ct_keys key, int8_t dir, uint8_t nfproto,
diff --git a/src/datatype.c b/src/datatype.c
index 095598d9..90905258 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -825,6 +825,7 @@ static const struct symbol_table icmp_code_tbl = {
SYMBOL("net-prohibited", ICMP_NET_ANO),
SYMBOL("host-prohibited", ICMP_HOST_ANO),
SYMBOL("admin-prohibited", ICMP_PKT_FILTERED),
+ SYMBOL("frag-needed", ICMP_FRAG_NEEDED),
SYMBOL_LIST_END
},
};
@@ -1083,6 +1084,8 @@ struct datatype *datatype_get(const struct datatype *ptr)
void datatype_set(struct expr *expr, const struct datatype *dtype)
{
+ if (dtype == expr->dtype)
+ return;
datatype_free(expr->dtype);
expr->dtype = datatype_get(dtype);
}
diff --git a/src/evaluate.c b/src/evaluate.c
index a3cbf939..fbc8f1fb 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -18,6 +18,7 @@
#include <linux/netfilter_arp.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_synproxy.h>
+#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter_ipv4.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
@@ -75,6 +76,7 @@ static void key_fix_dtype_byteorder(struct expr *key)
datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
}
+static int set_evaluate(struct eval_ctx *ctx, struct set *set);
static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
const char *name,
struct expr *key,
@@ -100,11 +102,14 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
handle_merge(&set->handle, &ctx->cmd->handle);
memset(&h, 0, sizeof(h));
handle_merge(&h, &set->handle);
+ h.set.location = expr->location;
cmd = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, &expr->location, set);
cmd->location = set->location;
list_add_tail(&cmd->list, &ctx->cmd->list);
}
+ set_evaluate(ctx, set);
+
return set_ref_expr_alloc(&expr->location, set);
}
@@ -147,7 +152,7 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
byteorder_names[byteorder],
byteorder_names[(*expr)->byteorder]);
- if (expr_is_constant(*expr))
+ if (expr_is_constant(*expr) || (*expr)->len / BITS_PER_BYTE < 2)
(*expr)->byteorder = byteorder;
else {
op = byteorder_conversion_op(*expr, byteorder);
@@ -483,6 +488,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
mask = constant_expr_alloc(&expr->location, expr_basetype(expr),
BYTEORDER_HOST_ENDIAN, masklen, NULL);
mpz_set(mask->value, bitmask);
+ mpz_clear(bitmask);
and = binop_expr_alloc(&expr->location, OP_AND, expr, mask);
and->dtype = expr->dtype;
@@ -657,7 +663,7 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
if (err < 0)
return err;
- list_add_tail(&nstmt->list, &ctx->stmt->list);
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
}
assert(base <= PROTO_BASE_MAX);
@@ -671,7 +677,7 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
return 1;
payload->payload.offset += ctx->pctx.protocol[base].offset;
- list_add_tail(&nstmt->list, &ctx->stmt->list);
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
return 0;
}
@@ -696,7 +702,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc == NULL) {
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
- list_add_tail(&nstmt->list, &ctx->stmt->list);
+
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
} else {
/* No conflict: Same payload protocol as context, adjust offset
* if needed.
@@ -736,6 +743,9 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
+ if (expr->payload.evaluated)
+ return 0;
+
if (__expr_evaluate_payload(ctx, expr) < 0)
return -1;
@@ -745,6 +755,8 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
if (payload_needs_adjustment(expr))
expr_evaluate_bits(ctx, exprp);
+ expr->payload.evaluated = true;
+
return 0;
}
@@ -833,8 +845,8 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
relational_expr_pctx_update(&ctx->pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- list_add_tail(&nstmt->list, &ctx->stmt->list);
return 0;
}
@@ -1300,8 +1312,25 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
{
+ struct set *set = ctx->set;
struct expr *elem = *expr;
+ if (elem->stmt) {
+ if (set->stmt && set->stmt->ops != elem->stmt->ops) {
+ return stmt_error(ctx, elem->stmt,
+ "statement mismatch, element expects %s, "
+ "but %s has type %s",
+ elem->stmt->ops->name,
+ set_is_map(set->flags) ? "map" : "set",
+ set->stmt->ops->name);
+ } else if (!set->stmt && !(set->flags & NFT_SET_EVAL)) {
+ return stmt_error(ctx, elem->stmt,
+ "missing %s statement in %s definition",
+ elem->stmt->ops->name,
+ set_is_map(set->flags) ? "map" : "set");
+ }
+ }
+
if (expr_evaluate(ctx, &elem->key) < 0)
return -1;
@@ -1358,10 +1387,16 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
set->size += i->size - 1;
set->set_flags |= i->set_flags;
expr_free(i);
- } else if (!expr_is_singleton(i))
+ } else if (!expr_is_singleton(i)) {
set->set_flags |= NFT_SET_INTERVAL;
+ if (i->key->etype == EXPR_CONCAT)
+ set->set_flags |= NFT_SET_CONCAT;
+ }
}
+ if (ctx->set && (ctx->set->flags & NFT_SET_CONCAT))
+ set->set_flags |= NFT_SET_CONCAT;
+
set->set_flags |= NFT_SET_CONSTANT;
datatype_set(set, ctx->ectx.dtype);
@@ -1416,6 +1451,9 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
if (binop_transfer(ctx, expr) < 0)
return -1;
+ if (ctx->set->data->flags & EXPR_F_INTERVAL)
+ ctx->set->data->len *= 2;
+
ctx->set->key->len = ctx->ectx.len;
ctx->set = NULL;
map = *expr;
@@ -1456,6 +1494,7 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *mapping = *expr;
struct set *set = ctx->set;
+ uint32_t datalen;
if (set == NULL)
return expr_error(ctx->msgs, mapping,
@@ -1472,7 +1511,13 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON;
if (set->data) {
- expr_set_context(&ctx->ectx, set->data->dtype, set->data->len);
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ datalen = set->data->len / 2;
+ else
+ datalen = set->data->len;
+
+ expr_set_context(&ctx->ectx, set->data->dtype, datalen);
} else {
assert((set->flags & NFT_SET_MAP) == 0);
}
@@ -1482,7 +1527,14 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
if (!expr_is_constant(mapping->right))
return expr_error(ctx->msgs, mapping->right,
"Value must be a constant");
- if (!expr_is_singleton(mapping->right))
+
+ if (set_is_anonymous(set->flags) &&
+ (mapping->right->etype == EXPR_RANGE ||
+ mapping->right->etype == EXPR_PREFIX))
+ set->data->flags |= EXPR_F_INTERVAL;
+
+ if (!(set->data->flags & EXPR_F_INTERVAL) &&
+ !expr_is_singleton(mapping->right))
return expr_error(ctx->msgs, mapping->right,
"Value must be a singleton");
@@ -2102,14 +2154,10 @@ static int stmt_prefix_conversion(struct eval_ctx *ctx, struct expr **expr,
return 0;
}
-static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
- const struct datatype *dtype, unsigned int len,
- enum byteorder byteorder, struct expr **expr)
+static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
+ const struct datatype *dtype, unsigned int len,
+ enum byteorder byteorder, struct expr **expr)
{
- __expr_set_context(&ctx->ectx, dtype, byteorder, len, 0);
- if (expr_evaluate(ctx, expr) < 0)
- return -1;
-
if ((*expr)->etype == EXPR_PAYLOAD &&
(*expr)->dtype->type == TYPE_INTEGER &&
((*expr)->dtype->type != datatype_basetype(dtype)->type ||
@@ -2147,6 +2195,17 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return 0;
}
+static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
+ const struct datatype *dtype, unsigned int len,
+ enum byteorder byteorder, struct expr **expr)
+{
+ __expr_set_context(&ctx->ectx, dtype, byteorder, len, 0);
+ if (expr_evaluate(ctx, expr) < 0)
+ return -1;
+
+ return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
+}
+
static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
{
if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0)
@@ -2197,7 +2256,7 @@ static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct expr *binop, *mask, *and, *payload_bytes;
+ struct expr *mask, *and, *xor, *payload_bytes;
unsigned int masklen, extra_len = 0;
unsigned int payload_byte_size, payload_byte_offset;
uint8_t shift_imm, data[NFT_REG_SIZE];
@@ -2213,6 +2272,11 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload->byteorder, &stmt->payload.val) < 0)
return -1;
+ if (!expr_is_constant(stmt->payload.val) &&
+ byteorder_conversion(ctx, &stmt->payload.val,
+ payload->byteorder) < 0)
+ return -1;
+
need_csum = stmt_evaluate_payload_need_csum(payload);
if (!payload_needs_adjustment(payload)) {
@@ -2229,8 +2293,8 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
shift_imm = expr_offset_shift(payload, payload->payload.offset,
&extra_len);
- payload_byte_size = round_up(payload->len, BITS_PER_BYTE) / BITS_PER_BYTE;
- payload_byte_size += (extra_len / BITS_PER_BYTE);
+ payload_byte_size = div_round_up(payload->len + extra_len,
+ BITS_PER_BYTE);
if (need_csum && payload_byte_size & 1) {
payload_byte_size++;
@@ -2244,22 +2308,21 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
}
if (shift_imm) {
- struct expr *off;
+ struct expr *off, *lshift;
off = constant_expr_alloc(&payload->location,
expr_basetype(payload),
BYTEORDER_HOST_ENDIAN,
sizeof(shift_imm), &shift_imm);
- binop = binop_expr_alloc(&payload->location, OP_LSHIFT,
- stmt->payload.val, off);
- binop->dtype = payload->dtype;
- binop->byteorder = payload->byteorder;
+ lshift = binop_expr_alloc(&payload->location, OP_LSHIFT,
+ stmt->payload.val, off);
+ lshift->dtype = payload->dtype;
+ lshift->byteorder = payload->byteorder;
- stmt->payload.val = binop;
+ stmt->payload.val = lshift;
}
-
masklen = payload_byte_size * BITS_PER_BYTE;
mpz_init_bitmask(ff, masklen);
@@ -2274,6 +2337,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_export_data(data, bitmask, BYTEORDER_HOST_ENDIAN, sizeof(data));
mask = constant_expr_alloc(&payload->location, expr_basetype(payload),
BYTEORDER_HOST_ENDIAN, masklen, data);
+ mpz_clear(bitmask);
payload_bytes = payload_expr_alloc(&payload->location, NULL, 0);
payload_init_raw(payload_bytes, payload->payload.base,
@@ -2288,16 +2352,17 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
and = binop_expr_alloc(&payload->location, OP_AND, payload_bytes, mask);
- and->dtype = payload_bytes->dtype;
- and->byteorder = payload_bytes->byteorder;
- and->len = payload_bytes->len;
+ and->dtype = payload_bytes->dtype;
+ and->byteorder = payload_bytes->byteorder;
+ and->len = payload_bytes->len;
+
+ xor = binop_expr_alloc(&payload->location, OP_XOR, and,
+ stmt->payload.val);
+ xor->dtype = payload->dtype;
+ xor->byteorder = payload->byteorder;
+ xor->len = mask->len;
- binop = binop_expr_alloc(&payload->location, OP_XOR, and,
- stmt->payload.val);
- binop->dtype = payload->dtype;
- binop->byteorder = payload->byteorder;
- binop->len = mask->len;
- stmt->payload.val = binop;
+ stmt->payload.val = xor;
return expr_evaluate(ctx, &stmt->payload.val);
}
@@ -2554,7 +2619,7 @@ static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
const struct proto_desc *desc;
desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- if (desc != &proto_eth)
+ if (desc != &proto_eth && desc != &proto_vlan)
return stmt_binary_error(ctx,
&ctx->pctx.protocol[PROTO_BASE_LL_HDR],
stmt, "unsupported link layer protocol");
@@ -2681,6 +2746,8 @@ static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
return -1;
}
stmt->reject.icmp_code = mpz_get_uint8(code->value);
+ expr_free(code);
+
return 0;
}
@@ -2762,22 +2829,28 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
}
}
+static const struct datatype *get_addr_dtype(uint8_t family)
+{
+ switch (family) {
+ case NFPROTO_IPV4:
+ return &ipaddr_type;
+ case NFPROTO_IPV6:
+ return &ip6addr_type;
+ }
+
+ return &invalid_type;
+}
+
static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
struct proto_ctx *pctx = &ctx->pctx;
const struct datatype *dtype;
- unsigned int len;
- if (pctx->family == NFPROTO_IPV4) {
- dtype = &ipaddr_type;
- len = 4 * BITS_PER_BYTE;
- } else {
- dtype = &ip6addr_type;
- len = 16 * BITS_PER_BYTE;
- }
+ dtype = get_addr_dtype(pctx->family);
- return stmt_evaluate_arg(ctx, stmt, dtype, len, BYTEORDER_BIG_ENDIAN,
+ return stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ BYTEORDER_BIG_ENDIAN,
expr);
}
@@ -2807,9 +2880,10 @@ static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
(nproto == &proto_ip6 && family != NFPROTO_IPV6))
return stmt_binary_error(ctx, stmt,
&ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
- "conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in tproxy statement",
+ "conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in %s statement",
ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
- family2str(stmt->tproxy.family));
+ family2str(family),
+ stmt->ops->name);
return 0;
}
@@ -2818,25 +2892,15 @@ static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **addr)
{
const struct datatype *dtype;
- unsigned int len;
int err;
if (ctx->pctx.family == NFPROTO_INET) {
- switch (family) {
- case NFPROTO_IPV4:
- dtype = &ipaddr_type;
- len = 4 * BITS_PER_BYTE;
- break;
- case NFPROTO_IPV6:
- dtype = &ip6addr_type;
- len = 16 * BITS_PER_BYTE;
- break;
- default:
+ dtype = get_addr_dtype(family);
+ if (dtype->size == 0)
return stmt_error(ctx, stmt,
"ip or ip6 must be specified with address for inet tables.");
- }
- err = stmt_evaluate_arg(ctx, stmt, dtype, len,
+ err = stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN, addr);
} else {
err = evaluate_addr(ctx, stmt, addr);
@@ -2845,6 +2909,67 @@ static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
return err;
}
+static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *one, *two, *data, *tmp;
+ const struct datatype *dtype;
+ int addr_type, err;
+
+ switch (stmt->nat.family) {
+ case NFPROTO_IPV4:
+ addr_type = TYPE_IPADDR;
+ break;
+ case NFPROTO_IPV6:
+ addr_type = TYPE_IP6ADDR;
+ break;
+ default:
+ return -1;
+ }
+ dtype = concat_type_alloc((addr_type << TYPE_BITS) | TYPE_INET_SERVICE);
+
+ expr_set_context(&ctx->ectx, dtype, dtype->size);
+ if (expr_evaluate(ctx, &stmt->nat.addr))
+ return -1;
+
+ if (stmt->nat.addr->etype != EXPR_MAP)
+ return 0;
+
+ data = stmt->nat.addr->mappings->set->data;
+ datatype_set(data, dtype);
+
+ if (expr_ops(data)->type != EXPR_CONCAT)
+ return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ BYTEORDER_BIG_ENDIAN,
+ &stmt->nat.addr);
+
+ one = list_first_entry(&data->expressions, struct expr, list);
+ two = list_entry(one->list.next, struct expr, list);
+
+ if (one == two || !list_is_last(&two->list, &data->expressions))
+ return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ BYTEORDER_BIG_ENDIAN,
+ &stmt->nat.addr);
+
+ dtype = get_addr_dtype(stmt->nat.family);
+ tmp = one;
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ BYTEORDER_BIG_ENDIAN,
+ &tmp);
+ if (err < 0)
+ return err;
+ if (tmp != one)
+ BUG("Internal error: Unexpected alteration of l3 expression");
+
+ tmp = two;
+ err = nat_evaluate_transport(ctx, stmt, &tmp);
+ if (err < 0)
+ return err;
+ if (tmp != two)
+ BUG("Internal error: Unexpected alteration of l4 expression");
+
+ return err;
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -2858,15 +2983,47 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
if (err < 0)
return err;
+ if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ err = stmt_evaluate_nat_map(ctx, stmt);
+ if (err < 0)
+ return err;
+
+ stmt->flags |= STMT_F_TERMINAL;
+ return 0;
+ }
+
err = stmt_evaluate_addr(ctx, stmt, stmt->nat.family,
&stmt->nat.addr);
if (err < 0)
return err;
}
+
+ if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) {
+ switch (stmt->nat.addr->etype) {
+ case EXPR_MAP:
+ if (!(stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL))
+ return expr_error(ctx->msgs, stmt->nat.addr,
+ "map is not defined as interval");
+ break;
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
+ break;
+ default:
+ return expr_error(ctx->msgs, stmt->nat.addr,
+ "neither prefix, range nor map expression");
+ }
+
+ stmt->flags |= STMT_F_TERMINAL;
+
+ return 0;
+ }
+
if (stmt->nat.proto != NULL) {
err = nat_evaluate_transport(ctx, stmt, &stmt->nat.proto);
if (err < 0)
return err;
+
+ stmt->nat.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
}
stmt->flags |= STMT_F_TERMINAL;
@@ -3162,12 +3319,6 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
mappings = implicit_set_declaration(ctx, "__objmap%d",
key, mappings);
-
- 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;
@@ -3360,6 +3511,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
memcpy(&set->desc.field_len, &set->key->field_len,
sizeof(set->desc.field_len));
set->desc.field_count = set->key->field_count;
+ set->flags |= NFT_SET_CONCAT;
}
if (set_is_datamap(set->flags)) {
@@ -3367,6 +3519,13 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return set_error(ctx, set, "map definition does not "
"specify mapping data type");
+ if (set->data->flags & EXPR_F_INTERVAL)
+ set->data->len *= 2;
+
+ if (set->data->etype == EXPR_CONCAT &&
+ expr_evaluate_concat(ctx, &set->data, false) < 0)
+ return -1;
+
if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT)
return set_key_data_error(ctx, set,
set->data->dtype, type);
@@ -3384,9 +3543,17 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
}
+ /* Default timeout value implies timeout support */
+ if (set->timeout)
+ set->flags |= NFT_SET_TIMEOUT;
+
+ if (set_is_anonymous(set->flags))
+ return 0;
+
ctx->set = set;
if (set->init != NULL) {
- expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
+ __expr_set_context(&ctx->ectx, set->key->dtype,
+ set->key->byteorder, set->key->len, 0);
if (expr_evaluate(ctx, &set->init) < 0)
return -1;
}
@@ -3395,10 +3562,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (set_lookup(table, set->handle.set.name) == NULL)
set_add_hash(set_get(set), table);
- /* Default timeout value implies timeout support */
- if (set->timeout)
- set->flags |= NFT_SET_TIMEOUT;
-
return 0;
}
@@ -3461,17 +3624,16 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- ft->hooknum = str2hooknum(NFPROTO_NETDEV, ft->hookstr);
- if (ft->hooknum == NF_INET_NUMHOOKS)
- return chain_error(ctx, ft, "invalid hook %s", ft->hookstr);
-
- if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hooknum))
- return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
- "invalid priority expression %s.",
- expr_name(ft->priority.expr));
-
- if (!ft->dev_expr)
- return chain_error(ctx, ft, "Unbound flowtable not allowed (must specify devices)");
+ if (ft->hook.name) {
+ ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
+ if (ft->hook.num == NF_INET_NUMHOOKS)
+ return chain_error(ctx, ft, "invalid hook %s",
+ ft->hook.name);
+ if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num))
+ return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
+ "invalid priority expression %s.",
+ expr_name(ft->priority.expr));
+ }
return 0;
}
@@ -3676,14 +3838,15 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
}
if (chain->flags & CHAIN_F_BASECHAIN) {
- chain->hooknum = str2hooknum(chain->handle.family,
- chain->hookstr);
- if (chain->hooknum == NF_INET_NUMHOOKS)
- return chain_error(ctx, chain, "invalid hook %s",
- chain->hookstr);
+ chain->hook.num = str2hooknum(chain->handle.family,
+ chain->hook.name);
+ if (chain->hook.num == NF_INET_NUMHOOKS)
+ return __stmt_binary_error(ctx, &chain->hook.loc, NULL,
+ "The %s family does not support this hook",
+ family2str(chain->handle.family));
if (!evaluate_priority(ctx, &chain->priority,
- chain->handle.family, chain->hooknum))
+ chain->handle.family, chain->hook.num))
return __stmt_binary_error(ctx, &chain->priority.loc, NULL,
"invalid priority expression %s in this context.",
expr_name(chain->priority.expr));
@@ -3692,6 +3855,15 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
return chain_error(ctx, chain, "invalid policy expression %s",
expr_name(chain->policy));
}
+
+ if (chain->handle.family == NFPROTO_NETDEV) {
+ if (!chain->dev_expr)
+ return __stmt_binary_error(ctx, &chain->loc, NULL,
+ "Missing `device' in this chain definition");
+ } else if (chain->dev_expr) {
+ return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
+ "This chain type cannot be bound to device");
+ }
}
list_for_each_entry(rule, &chain->rules, list) {
@@ -3732,6 +3904,7 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
ct->timeout[ts->timeout_index] = ts->timeout_value;
list_del(&ts->head);
+ xfree(ts->timeout_str);
xfree(ts);
}
@@ -3803,7 +3976,7 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
return setelem_evaluate(ctx, &cmd->expr);
case CMD_OBJ_SET:
handle_merge(&cmd->set->handle, &cmd->handle);
@@ -3835,7 +4008,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
return setelem_evaluate(ctx, &cmd->expr);
case CMD_OBJ_SET:
case CMD_OBJ_RULE:
@@ -3862,7 +4035,7 @@ static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd)
struct set *set;
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
table = table_lookup(&cmd->handle, &ctx->nft->cache);
if (table == NULL)
return table_not_found(ctx);
diff --git a/src/expression.c b/src/expression.c
index cb11cda4..a6bde70f 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -829,6 +829,142 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
compound_expr_print(expr, " . ", octx);
}
+#define NFTNL_UDATA_SET_KEY_CONCAT_NEST 0
+#define NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX NFT_REG32_SIZE
+
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE 0
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2
+
+static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
+ const struct expr *concat_expr)
+{
+ struct nftnl_udata *nest;
+ unsigned int i = 0;
+ struct expr *expr;
+
+ list_for_each_entry(expr, &concat_expr->expressions, list) {
+ struct nftnl_udata *nest_expr;
+ int err;
+
+ if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
+ return -1;
+
+ nest = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_NEST + i);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE, expr->etype);
+ nest_expr = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA);
+ err = expr_ops(expr)->build_udata(udbuf, expr);
+ if (err < 0)
+ return err;
+ nftnl_udata_nest_end(udbuf, nest_expr);
+ nftnl_udata_nest_end(udbuf, nest);
+ i++;
+ }
+
+ return 0;
+}
+
+static int concat_parse_udata_nest(const struct nftnl_udata *attr, void *data)
+{
+ const struct nftnl_udata **ud = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+
+ if (type >= NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX)
+ return -1;
+
+ if (len <= sizeof(uint32_t))
+ return -1;
+
+ ud[type] = attr;
+ return 0;
+}
+
+static int concat_parse_udata_nested(const struct nftnl_udata *attr, void *data)
+{
+ const struct nftnl_udata **ud = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+
+ switch (type) {
+ case NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE:
+ if (len != sizeof(uint32_t))
+ return -1;
+ break;
+ case NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA:
+ if (len <= sizeof(uint32_t))
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+
+ ud[type] = attr;
+ return 0;
+}
+
+static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
+{
+ const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {};
+ const struct datatype *dtype;
+ struct expr *concat_expr;
+ uint32_t dt = 0;
+ unsigned int i;
+ int err;
+
+ err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
+ concat_parse_udata_nest, ud);
+ if (err < 0)
+ return NULL;
+
+ concat_expr = concat_expr_alloc(&internal_location);
+ if (!concat_expr)
+ return NULL;
+
+ for (i = 0; i < array_size(ud); i++) {
+ const struct nftnl_udata *nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX];
+ const struct nftnl_udata *nested, *subdata;
+ const struct expr_ops *ops;
+ struct expr *expr;
+ uint32_t etype;
+
+ if (ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i] == NULL)
+ break;
+
+ nested = ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i];
+ err = nftnl_udata_parse(nftnl_udata_get(nested), nftnl_udata_len(nested),
+ concat_parse_udata_nested, nest_ud);
+ if (err < 0)
+ goto err_free;
+
+ etype = nftnl_udata_get_u32(nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE]);
+ ops = expr_ops_by_type(etype);
+ if (!ops || !ops->parse_udata)
+ goto err_free;
+
+ subdata = nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA];
+ expr = ops->parse_udata(subdata);
+ if (!expr)
+ goto err_free;
+
+ dt = concat_subtype_add(dt, expr->dtype->type);
+ compound_expr_add(concat_expr, expr);
+ }
+
+ dtype = concat_type_alloc(dt);
+ if (!dtype)
+ goto err_free;
+
+ concat_expr->dtype = datatype_get(dtype);
+ concat_expr->len = dtype->size;
+
+ return concat_expr;
+
+err_free:
+ expr_free(concat_expr);
+ return NULL;
+}
+
static const struct expr_ops concat_expr_ops = {
.type = EXPR_CONCAT,
.name = "concat",
@@ -836,6 +972,8 @@ static const struct expr_ops concat_expr_ops = {
.json = concat_expr_json,
.clone = compound_expr_clone,
.destroy = concat_expr_destroy,
+ .build_udata = concat_expr_build_udata,
+ .parse_udata = concat_expr_parse_udata,
};
struct expr *concat_expr_alloc(const struct location *loc)
@@ -1185,9 +1323,9 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
}
}
-const struct expr_ops *expr_ops(const struct expr *e)
+static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
{
- switch (e->etype) {
+ switch (etype) {
case EXPR_INVALID:
BUG("Invalid expression ops requested");
break;
@@ -1220,26 +1358,21 @@ const struct expr_ops *expr_ops(const struct expr *e)
case EXPR_XFRM: return &xfrm_expr_ops;
}
- BUG("Unknown expression type %d\n", e->etype);
+ BUG("Unknown expression type %d\n", etype);
}
-const struct expr_ops *expr_ops_by_type(enum expr_types etype)
+const struct expr_ops *expr_ops(const struct expr *e)
{
- switch (etype) {
- case EXPR_PAYLOAD: return &payload_expr_ops;
- case EXPR_EXTHDR: return &exthdr_expr_ops;
- case EXPR_META: return &meta_expr_ops;
- case EXPR_SOCKET: return &socket_expr_ops;
- case EXPR_OSF: return &osf_expr_ops;
- case EXPR_CT: return &ct_expr_ops;
- case EXPR_NUMGEN: return &numgen_expr_ops;
- case EXPR_HASH: return &hash_expr_ops;
- case EXPR_RT: return &rt_expr_ops;
- case EXPR_FIB: return &fib_expr_ops;
- case EXPR_XFRM: return &xfrm_expr_ops;
- default:
- break;
- }
+ return __expr_ops_by_type(e->etype);
+}
- BUG("Unknown expression type %d\n", etype);
+const struct expr_ops *expr_ops_by_type(uint32_t value)
+{
+ /* value might come from unreliable source, such as "udata"
+ * annotation of set keys. Avoid BUG() assertion.
+ */
+ if (value == EXPR_INVALID || value > EXPR_MAX)
+ return NULL;
+
+ return __expr_ops_by_type(value);
}
diff --git a/src/json.c b/src/json.c
index 86028959..ed713181 100644
--- a/src/json.c
+++ b/src/json.c
@@ -240,7 +240,7 @@ static json_t *chain_print_json(const struct chain *chain)
tmp = json_pack("{s:s, s:s, s:i, s:s}",
"type", chain->type,
"hook", hooknum2str(chain->handle.family,
- chain->hooknum),
+ chain->hook.num),
"prio", priority,
"policy", chain_policy2str(policy));
if (chain->dev_expr) {
@@ -415,7 +415,7 @@ static json_t *flowtable_print_json(const struct flowtable *ftable)
"name", ftable->handle.flowtable.name,
"table", ftable->handle.table.name,
"handle", ftable->handle.handle.id,
- "hook", hooknum2str(NFPROTO_NETDEV, ftable->hooknum),
+ "hook", hooknum2str(NFPROTO_NETDEV, ftable->hook.num),
"prio", priority);
for (i = 0; i < ftable->dev_array_len; i++) {
diff --git a/src/libnftables.c b/src/libnftables.c
index cd2fcf2f..668e3fc4 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -12,7 +12,7 @@
#include <parser.h>
#include <utils.h>
#include <iface.h>
-
+#include <cmd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -68,9 +68,7 @@ static int nft_netlink(struct nft_ctx *nft,
list_for_each_entry(cmd, cmds, list) {
if (err->seqnum == cmd->seqnum ||
err->seqnum == batch_seqnum) {
- netlink_io_error(&ctx, &cmd->location,
- "Could not process rule: %s",
- strerror(err->err));
+ nft_cmd_error(&ctx, cmd, err);
errno = err->err;
if (err->seqnum == cmd->seqnum) {
mnl_err_list_free(err);
@@ -421,8 +419,12 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
if (nft->state->nerrs)
return -1;
- list_for_each_entry(cmd, cmds, list)
+ list_for_each_entry(cmd, cmds, list) {
+ if (cmd->op != CMD_ADD)
+ continue;
+
nft_cmd_expand(cmd);
+ }
return 0;
}
diff --git a/src/main.c b/src/main.c
index 6ab1b89f..d830c7a2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,9 +24,23 @@
static struct nft_ctx *nft;
+/*
+ * These options are grouped separately in the help, so we give them named
+ * indices for use there.
+ */
+enum opt_indices {
+ IDX_HELP,
+ IDX_VERSION,
+ IDX_VERSION_LONG,
+ IDX_CHECK,
+ IDX_FILE,
+ IDX_INTERACTIVE,
+};
+
enum opt_vals {
OPT_HELP = 'h',
OPT_VERSION = 'v',
+ OPT_VERSION_LONG = 'V',
OPT_CHECK = 'c',
OPT_FILE = 'f',
OPT_INTERACTIVE = 'i',
@@ -46,122 +60,174 @@ enum opt_vals {
OPT_TERSE = 't',
OPT_INVALID = '?',
};
-#define OPTSTRING "+hvd:cf:iI:jvnsNaeSupypTt"
-static const struct option options[] = {
- {
- .name = "help",
- .val = OPT_HELP,
- },
- {
- .name = "version",
- .val = OPT_VERSION,
- },
- {
- .name = "check",
- .val = OPT_CHECK,
- },
- {
- .name = "file",
- .val = OPT_FILE,
- .has_arg = 1,
- },
- {
- .name = "interactive",
- .val = OPT_INTERACTIVE,
- },
- {
- .name = "numeric",
- .val = OPT_NUMERIC,
- },
- {
- .name = "stateless",
- .val = OPT_STATELESS,
- },
- {
- .name = "reversedns",
- .val = OPT_IP2NAME,
- },
- {
- .name = "service",
- .val = OPT_SERVICE,
- },
- {
- .name = "includepath",
- .val = OPT_INCLUDEPATH,
- .has_arg = 1,
- },
- {
- .name = "debug",
- .val = OPT_DEBUG,
- .has_arg = 1,
- },
- {
- .name = "handle",
- .val = OPT_HANDLE_OUTPUT,
- },
- {
- .name = "echo",
- .val = OPT_ECHO,
- },
- {
- .name = "json",
- .val = OPT_JSON,
- },
- {
- .name = "guid",
- .val = OPT_GUID,
- },
- {
- .name = "numeric-priority",
- .val = OPT_NUMERIC_PRIO,
- },
- {
- .name = "numeric-protocol",
- .val = OPT_NUMERIC_PROTO,
- },
- {
- .name = "numeric-time",
- .val = OPT_NUMERIC_TIME,
- },
- {
- .name = "terse",
- .val = OPT_TERSE,
- },
- {
- .name = NULL
- }
+struct nft_opt {
+ const char *name;
+ enum opt_vals val;
+ const char *arg;
+ const char *help;
+};
+
+#define NFT_OPT(n, v, a, h) \
+ (struct nft_opt) { .name = n, .val = v, .arg = a, .help = h }
+
+static const struct nft_opt nft_options[] = {
+ NFT_OPT("help", OPT_HELP, NULL,
+ "Show this help"),
+ NFT_OPT("version", OPT_VERSION, NULL,
+ "Show version information"),
+ NFT_OPT(NULL, OPT_VERSION_LONG, NULL,
+ "Show extended version information"),
+ NFT_OPT("check", OPT_CHECK, NULL,
+ "Check commands validity without actually applying the changes."),
+ NFT_OPT("file", OPT_FILE, "<filename>",
+ "Read input from <filename>"),
+ NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
+ "Read input from interactive CLI"),
+ NFT_OPT("numeric", OPT_NUMERIC, NULL,
+ "Print fully numerical output."),
+ NFT_OPT("stateless", OPT_STATELESS, NULL,
+ "Omit stateful information of ruleset."),
+ NFT_OPT("reversedns", OPT_IP2NAME, NULL,
+ "Translate IP addresses to names."),
+ NFT_OPT("service", OPT_SERVICE, NULL,
+ "Translate ports to service names as described in /etc/services."),
+ NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
+ "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
+ NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
+ "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
+ NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL,
+ "Output rule handle."),
+ NFT_OPT("echo", OPT_ECHO, NULL,
+ "Echo what has been added, inserted or replaced."),
+ NFT_OPT("json", OPT_JSON, NULL,
+ "Format output in JSON"),
+ NFT_OPT("guid", OPT_GUID, NULL,
+ "Print UID/GID as defined in /etc/passwd and /etc/group."),
+ NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL,
+ "Print chain priority numerically."),
+ NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL,
+ "Print layer 4 protocols numerically."),
+ NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL,
+ "Print time values numerically."),
+ NFT_OPT("terse", OPT_TERSE, NULL,
+ "Omit contents of sets."),
};
+#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
+
+static const char *get_optstring(void)
+{
+ static char optstring[2 * NR_NFT_OPTIONS + 2];
+
+ if (!optstring[0]) {
+ size_t i, j;
+
+ optstring[0] = '+';
+ for (i = 0, j = 1; i < NR_NFT_OPTIONS && j < sizeof(optstring); i++)
+ j += snprintf(optstring + j, sizeof(optstring) - j, "%c%s",
+ nft_options[i].val,
+ nft_options[i].arg ? ":" : "");
+
+ assert(j < sizeof(optstring));
+ }
+ return optstring;
+}
+
+static const struct option *get_options(void)
+{
+ static struct option options[NR_NFT_OPTIONS + 1];
+
+ if (!options[0].name) {
+ size_t i, j;
+
+ for (i = 0, j = 0; i < NR_NFT_OPTIONS; ++i) {
+ if (nft_options[i].name) {
+ options[j].name = nft_options[i].name;
+ options[j].val = nft_options[i].val;
+ options[j].has_arg = nft_options[i].arg != NULL;
+ j++;
+ }
+ }
+ }
+ return options;
+}
+
+static void print_option(const struct nft_opt *opt)
+{
+ char optbuf[33] = "";
+ int i;
+
+ i = snprintf(optbuf, sizeof(optbuf), " -%c", opt->val);
+ if (opt->name)
+ i += snprintf(optbuf + i, sizeof(optbuf) - i, ", %s", opt->name);
+ if (opt->arg)
+ i += snprintf(optbuf + i, sizeof(optbuf) - i, " %s", opt->arg);
+
+ printf("%-32s%s\n", optbuf, opt->help);
+}
+
static void show_help(const char *name)
{
- printf(
-"Usage: %s [ options ] [ cmds... ]\n"
-"\n"
-"Options:\n"
-" -h, --help Show this help\n"
-" -v, --version Show version information\n"
-"\n"
-" -c, --check Check commands validity without actually applying the changes.\n"
-" -f, --file <filename> Read input from <filename>\n"
-" -i, --interactive Read input from interactive CLI\n"
-"\n"
-" -j, --json Format output in JSON\n"
-" -n, --numeric Print fully numerical output.\n"
-" -s, --stateless Omit stateful information of ruleset.\n"
-" -t, --terse Omit contents of sets.\n"
-" -u, --guid Print UID/GID as defined in /etc/passwd and /etc/group.\n"
-" -N Translate IP addresses to names.\n"
-" -S, --service Translate ports to service names as described in /etc/services.\n"
-" -p, --numeric-protocol Print layer 4 protocols numerically.\n"
-" -y, --numeric-priority Print chain priority numerically.\n"
-" -T, --numeric-time Print time values numerically.\n"
-" -a, --handle Output rule handle.\n"
-" -e, --echo Echo what has been added, inserted or replaced.\n"
-" -I, --includepath <directory> Add <directory> to the paths searched for include files. Default is: %s\n"
-" --debug <level [,level...]> Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n"
-"\n",
- name, DEFAULT_INCLUDE_PATH);
+ printf("Usage: %s [ options ] [ cmds... ]\n"
+ "\n"
+ "Options:\n", name);
+
+ print_option(&nft_options[IDX_HELP]);
+ print_option(&nft_options[IDX_VERSION]);
+ print_option(&nft_options[IDX_VERSION_LONG]);
+
+ fputs("\n", stdout);
+
+ print_option(&nft_options[IDX_CHECK]);
+ print_option(&nft_options[IDX_FILE]);
+ print_option(&nft_options[IDX_INTERACTIVE]);
+
+ fputs("\n", stdout);
+
+ for (size_t i = IDX_INTERACTIVE + 1; i < NR_NFT_OPTIONS; ++i)
+ print_option(&nft_options[i]);
+
+ fputs("\n", stdout);
+}
+
+static void show_version(void)
+{
+ const char *cli, *minigmp, *json, *xt;
+
+#if defined(HAVE_LIBREADLINE)
+ cli = "readline";
+#elif defined(HAVE_LIBLINENOISE)
+ cli = "linenoise";
+#else
+ cli = "no";
+#endif
+
+#if defined(HAVE_MINIGMP)
+ minigmp = "yes";
+#else
+ minigmp = "no";
+#endif
+
+#if defined(HAVE_JSON)
+ json = "yes";
+#else
+ json = "no";
+#endif
+
+#if defined(HAVE_XTABLES)
+ xt = "yes";
+#else
+ xt = "no";
+#endif
+
+ printf("%s v%s (%s)\n"
+ " cli: %s\n"
+ " json: %s\n"
+ " minigmp: %s\n"
+ " libxtables: %s\n",
+ PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME,
+ cli, json, minigmp, xt);
}
static const struct {
@@ -247,6 +313,8 @@ static bool nft_options_check(int argc, char * const argv[])
int main(int argc, char * const *argv)
{
+ const struct option *options = get_options();
+ const char *optstring = get_optstring();
char *buf = NULL, *filename = NULL;
unsigned int output_flags = 0;
bool interactive = false;
@@ -260,7 +328,7 @@ int main(int argc, char * const *argv)
nft = nft_ctx_new(NFT_CTX_DEFAULT);
while (1) {
- val = getopt_long(argc, argv, OPTSTRING, options, NULL);
+ val = getopt_long(argc, argv, optstring, options, NULL);
if (val == -1)
break;
@@ -272,6 +340,9 @@ int main(int argc, char * const *argv)
printf("%s v%s (%s)\n",
PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
exit(EXIT_SUCCESS);
+ case OPT_VERSION_LONG:
+ show_version();
+ exit(EXIT_SUCCESS);
case OPT_CHECK:
nft_ctx_set_dry_run(nft, true);
break;
diff --git a/src/mnl.c b/src/mnl.c
index 340380ba..19f66641 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -26,6 +26,7 @@
#include <mnl.h>
#include <string.h>
+#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
@@ -37,6 +38,7 @@
struct mnl_socket *nft_mnl_socket_open(void)
{
struct mnl_socket *nf_sock;
+ int one = 1;
nf_sock = mnl_socket_open(NETLINK_NETFILTER);
if (!nf_sock)
@@ -45,6 +47,8 @@ struct mnl_socket *nft_mnl_socket_open(void)
if (fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK))
netlink_init_error();
+ mnl_socket_setsockopt(nf_sock, NETLINK_EXT_ACK, &one, sizeof(one));
+
return nf_sock;
}
@@ -204,11 +208,13 @@ void mnl_batch_reset(struct nftnl_batch *batch)
}
static void mnl_err_list_node_add(struct list_head *err_list, int error,
- int seqnum)
+ int seqnum, uint32_t offset,
+ const char *errmsg)
{
struct mnl_err *err = xmalloc(sizeof(struct mnl_err));
err->seqnum = seqnum;
+ err->offset = offset;
err->err = error;
list_add_tail(&err->head, err_list);
}
@@ -305,6 +311,61 @@ static ssize_t mnl_nft_socket_sendmsg(struct netlink_ctx *ctx,
return sendmsg(mnl_socket_get_fd(ctx->nft->nf_sock), msg, 0);
}
+static int err_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ uint16_t type;
+
+ if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ type = mnl_attr_get_type(attr);
+ switch (type) {
+ case NLMSGERR_ATTR_OFFS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int mnl_batch_extack_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct netlink_cb_data *cb_data = data;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ unsigned int hlen = sizeof(*err);
+ const char *msg = NULL;
+ uint32_t off = 0;
+ int errval;
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr)))
+ return MNL_CB_ERROR;
+
+ if (err->error < 0)
+ errval = -err->error;
+ else
+ errval = err->error;
+
+ if (errval == 0)
+ return MNL_CB_STOP;
+
+ if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+ hlen += mnl_nlmsg_get_payload_len(&err->msg);
+
+ if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
+ return MNL_CB_ERROR;
+
+ if (tb[NLMSGERR_ATTR_OFFS])
+ off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+
+ mnl_err_list_node_add(cb_data->err_list, errval,
+ nlh->nlmsg_seq, off, msg);
+ return MNL_CB_ERROR;
+}
+
#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024)
int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
@@ -326,6 +387,13 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
unsigned int rcvbufsiz;
size_t batch_size;
fd_set readfds;
+ static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_ERROR] = mnl_batch_extack_cb,
+ };
+ struct netlink_cb_data cb_data = {
+ .err_list = err_list,
+ .nl_ctx = ctx,
+ };
mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
@@ -361,18 +429,15 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
if (ret == -1)
return -1;
- ret = mnl_cb_run(rcv_buf, ret, 0, portid, &netlink_echo_callback, ctx);
/* Continue on error, make sure we get all acknowledgments */
- if (ret == -1) {
- struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buf;
-
- mnl_err_list_node_add(err_list, errno, nlh->nlmsg_seq);
- }
+ ret = mnl_cb_run2(rcv_buf, ret, 0, portid,
+ netlink_echo_callback, &cb_data,
+ cb_ctl_array, MNL_ARRAY_SIZE(cb_ctl_array));
}
return 0;
}
-int mnl_nft_rule_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct rule *rule = cmd->rule;
@@ -385,8 +450,6 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, const struct cmd *cmd,
memory_allocation_error();
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
- nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, h->table.name);
- nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, h->chain.name);
if (h->position.id)
nftnl_rule_set_u64(nlr, NFTNL_RULE_POSITION, h->position.id);
if (h->rule_id)
@@ -399,6 +462,12 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, const struct cmd *cmd,
NFT_MSG_NEWRULE,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -407,7 +476,7 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_rule_replace(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct rule *rule = cmd->rule;
struct handle *h = &rule->handle;
@@ -423,15 +492,20 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, const struct cmd *cmd)
memory_allocation_error();
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
- nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, h->table.name);
- nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, h->chain.name);
- nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, h->handle.id);
netlink_linearize_rule(ctx, nlr, rule);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWRULE,
cmd->handle.family,
NLM_F_REPLACE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -440,9 +514,9 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, const struct cmd *cmd)
return 0;
}
-int mnl_nft_rule_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
- const struct handle *h = &cmd->handle;
+ struct handle *h = &cmd->handle;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
@@ -451,16 +525,23 @@ int mnl_nft_rule_del(struct netlink_ctx *ctx, const struct cmd *cmd)
memory_allocation_error();
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
- nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, h->table.name);
- if (h->chain.name)
- nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, h->chain.name);
- if (h->handle.id)
- nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, h->handle.id);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELRULE,
nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY),
0, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
+ if (h->chain.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+ }
+ if (h->handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
+ }
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -524,12 +605,14 @@ err:
/*
* Chain
*/
-int mnl_nft_chain_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
int priority, policy, i = 0;
struct nftnl_chain *nlc;
+ unsigned int ifname_len;
const char **dev_array;
+ char ifname[IFNAMSIZ];
struct nlmsghdr *nlh;
struct expr *expr;
int dev_array_len;
@@ -539,13 +622,15 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, const struct cmd *cmd,
memory_allocation_error();
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, cmd->handle.table.name);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, cmd->handle.chain.name);
if (cmd->chain) {
+ if (cmd->chain->flags & CHAIN_F_HW_OFFLOAD) {
+ nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS,
+ CHAIN_F_HW_OFFLOAD);
+ }
if (cmd->chain->flags & CHAIN_F_BASECHAIN) {
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_HOOKNUM,
- cmd->chain->hooknum);
+ cmd->chain->hook.num);
mpz_export_data(&priority,
cmd->chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
@@ -553,16 +638,16 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, const struct cmd *cmd,
nftnl_chain_set_str(nlc, NFTNL_CHAIN_TYPE,
cmd->chain->type);
}
- if (cmd->chain->policy) {
- mpz_export_data(&policy, cmd->chain->policy->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_chain_set_u32(nlc, NFTNL_CHAIN_POLICY, policy);
- }
if (cmd->chain->dev_expr) {
dev_array = xmalloc(sizeof(char *) * 8);
dev_array_len = 8;
list_for_each_entry(expr, &cmd->chain->dev_expr->expressions, list) {
- dev_array[i++] = expr->identifier;
+ ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
+ memset(ifname, 0, sizeof(ifname));
+ mpz_export_data(ifname, expr->value,
+ BYTEORDER_HOST_ENDIAN,
+ ifname_len);
+ dev_array[i++] = xstrdup(ifname);
if (i == dev_array_len) {
dev_array_len *= 2;
dev_array = xrealloc(dev_array,
@@ -577,6 +662,10 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, const struct cmd *cmd,
nftnl_chain_set_data(nlc, NFTNL_CHAIN_DEVICES, dev_array,
sizeof(char *) * dev_array_len);
+ i = 0;
+ while (dev_array[i] != NULL)
+ xfree(dev_array[i++]);
+
xfree(dev_array);
}
}
@@ -586,6 +675,19 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, const struct cmd *cmd,
NFT_MSG_NEWCHAIN,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+
+ if (cmd->chain && cmd->chain->policy) {
+ mpz_export_data(&policy, cmd->chain->policy->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->policy->location);
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(policy));
+ }
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
nftnl_chain_free(nlc);
@@ -624,7 +726,7 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_chain_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nftnl_chain *nlc;
struct nlmsghdr *nlh;
@@ -634,18 +736,23 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, const struct cmd *cmd)
memory_allocation_error();
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, cmd->handle.table.name);
- if (cmd->handle.chain.name)
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME,
- cmd->handle.chain.name);
- else if (cmd->handle.handle.id)
- nftnl_chain_set_u64(nlc, NFTNL_CHAIN_HANDLE,
- cmd->handle.handle.id);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELCHAIN,
cmd->handle.family,
0, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
+ if (cmd->handle.chain.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+ } else if (cmd->handle.handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ mnl_attr_put_u64(nlh, NFTA_CHAIN_HANDLE,
+ htobe64(cmd->handle.handle.id));
+ }
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
nftnl_chain_free(nlc);
@@ -705,7 +812,7 @@ err:
/*
* Table
*/
-int mnl_nft_table_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct nftnl_table *nlt;
@@ -716,7 +823,6 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, const struct cmd *cmd,
memory_allocation_error();
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, cmd->handle.table.name);
if (cmd->table)
nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags);
else
@@ -726,6 +832,9 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, const struct cmd *cmd,
NFT_MSG_NEWTABLE,
cmd->handle.family,
flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
nftnl_table_nlmsg_build_payload(nlh, nlt);
nftnl_table_free(nlt);
@@ -734,7 +843,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_table_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -744,17 +853,20 @@ int mnl_nft_table_del(struct netlink_ctx *ctx, const struct cmd *cmd)
memory_allocation_error();
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- if (cmd->handle.table.name)
- nftnl_table_set_str(nlt, NFTNL_TABLE_NAME,
- cmd->handle.table.name);
- else if (cmd->handle.handle.id)
- nftnl_table_set_u64(nlt, NFTNL_TABLE_HANDLE,
- cmd->handle.handle.id);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELTABLE,
cmd->handle.family,
0, ctx->seqnum);
+
+ if (cmd->handle.table.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
+ } else if (cmd->handle.handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ mnl_attr_put_u64(nlh, NFTA_TABLE_NAME,
+ htobe64(cmd->handle.handle.id));
+ }
nftnl_table_nlmsg_build_payload(nlh, nlt);
nftnl_table_free(nlt);
@@ -834,10 +946,10 @@ static void set_key_expression(struct netlink_ctx *ctx,
/*
* Set
*/
-int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
- const struct handle *h = &cmd->handle;
+ struct handle *h = &cmd->handle;
struct nftnl_udata_buf *udbuf;
struct set *set = cmd->set;
struct nftnl_set *nls;
@@ -902,8 +1014,11 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
memory_allocation_error();
set_key_expression(ctx, set->key, set->flags, udbuf, NFTNL_UDATA_SET_KEY_TYPEOF);
- if (set->data)
+ if (set->data) {
set_key_expression(ctx, set->data, set->flags, udbuf, NFTNL_UDATA_SET_DATA_TYPEOF);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATA_INTERVAL,
+ !!(set->data->flags & EXPR_F_INTERVAL));
+ }
if (set->desc.field_len[0]) {
nftnl_set_set_data(nls, NFTNL_SET_DESC_CONCAT,
@@ -916,12 +1031,26 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
nftnl_udata_buf_len(udbuf));
nftnl_udata_buf_free(udbuf);
+ if (set->stmt) {
+ nftnl_set_set_data(nls, NFTNL_SET_EXPR,
+ netlink_gen_stmt_stateful(set->stmt), 0);
+ }
+
netlink_dump_set(nls, ctx);
+ nftnl_set_unset(nls, NFTNL_SET_TABLE);
+ nftnl_set_unset(nls, NFTNL_SET_NAME);
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWSET,
h->family,
NLM_F_CREATE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ mnl_attr_put_strz(nlh, NFTA_SET_TABLE, h->table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &h->set.location);
+ mnl_attr_put_strz(nlh, NFTA_SET_NAME, h->set.name);
+
nftnl_set_nlmsg_build_payload(nlh, nls);
nftnl_set_free(nls);
@@ -930,7 +1059,7 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_set_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
const struct handle *h = &cmd->handle;
struct nftnl_set *nls;
@@ -941,16 +1070,23 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, const struct cmd *cmd)
memory_allocation_error();
nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family);
- nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name);
- if (h->set.name)
- nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name);
- else if (h->handle.id)
- nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELSET,
h->family,
0, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_SET_TABLE, cmd->handle.table.name);
+ if (h->set.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.set.location);
+ mnl_attr_put_strz(nlh, NFTA_SET_NAME, cmd->handle.set.name);
+ } else if (h->handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ mnl_attr_put_u64(nlh, NFTA_SET_HANDLE,
+ htobe64(cmd->handle.handle.id));
+ }
+
nftnl_set_nlmsg_build_payload(nlh, nls);
nftnl_set_free(nls);
@@ -1016,7 +1152,7 @@ err:
return NULL;
}
-int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct obj *obj = cmd->object;
@@ -1028,8 +1164,6 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
memory_allocation_error();
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
- nftnl_obj_set_str(nlo, NFTNL_OBJ_TABLE, cmd->handle.table.name);
- nftnl_obj_set_str(nlo, NFTNL_OBJ_NAME, cmd->handle.obj.name);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, obj->type);
switch (obj->type) {
@@ -1107,6 +1241,12 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWOBJ, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
+
nftnl_obj_nlmsg_build_payload(nlh, nlo);
nftnl_obj_free(nlo);
@@ -1115,7 +1255,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_obj_del(struct netlink_ctx *ctx, const struct cmd *cmd, int type)
+int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
{
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1125,16 +1265,24 @@ int mnl_nft_obj_del(struct netlink_ctx *ctx, const struct cmd *cmd, int type)
memory_allocation_error();
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
- nftnl_obj_set_str(nlo, NFTNL_OBJ_TABLE, cmd->handle.table.name);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, type);
- if (cmd->handle.obj.name)
- nftnl_obj_set_str(nlo, NFTNL_OBJ_NAME, cmd->handle.obj.name);
- else if (cmd->handle.handle.id)
- nftnl_obj_set_u64(nlo, NFTNL_OBJ_HANDLE, cmd->handle.handle.id);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELOBJ, cmd->handle.family,
0, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
+
+ if (cmd->handle.obj.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
+ } else if (cmd->handle.handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ mnl_attr_put_u64(nlh, NFTA_OBJ_HANDLE,
+ htobe64(cmd->handle.handle.id));
+ }
+
nftnl_obj_nlmsg_build_payload(nlh, nlo);
nftnl_obj_free(nlo);
@@ -1442,14 +1590,48 @@ err:
return NULL;
}
-int mnl_nft_flowtable_add(struct netlink_ctx *ctx, const struct cmd *cmd,
+static const char **nft_flowtable_dev_array(struct cmd *cmd)
+{
+ unsigned int ifname_len;
+ const char **dev_array;
+ char ifname[IFNAMSIZ];
+ int i = 0, len = 1;
+ struct expr *expr;
+
+ list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
+ len++;
+
+ dev_array = xmalloc(sizeof(char *) * len);
+
+ list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list) {
+ ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
+ memset(ifname, 0, sizeof(ifname));
+ mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN,
+ ifname_len);
+ dev_array[i++] = xstrdup(ifname);
+ }
+
+ dev_array[i] = NULL;
+
+ return dev_array;
+}
+
+static void nft_flowtable_dev_array_free(const char **dev_array)
+{
+ int i = 0;
+
+ while (dev_array[i] != NULL)
+ xfree(dev_array[i++]);
+
+ free(dev_array);
+}
+
+int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct nftnl_flowtable *flo;
const char **dev_array;
struct nlmsghdr *nlh;
- int i = 0, len = 1;
- struct expr *expr;
int priority;
flo = nftnl_flowtable_alloc();
@@ -1458,33 +1640,39 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, const struct cmd *cmd,
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
- nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_TABLE,
- cmd->handle.table.name);
- nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_NAME,
- cmd->handle.flowtable.name);
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
- cmd->flowtable->hooknum);
- mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
- len++;
+ if (cmd->flowtable->hook.name) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
+ cmd->flowtable->hook.num);
+ mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
+ } else {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+ }
- dev_array = calloc(len, sizeof(char *));
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
- dev_array[i++] = expr->identifier;
+ if (cmd->flowtable->dev_expr) {
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
- dev_array[i] = NULL;
- nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
- dev_array, sizeof(char *) * len);
- free(dev_array);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FLAGS,
+ cmd->flowtable->flags);
netlink_dump_flowtable(flo, ctx);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWFLOWTABLE, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.flowtable.location);
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, cmd->handle.flowtable.name);
+
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
nftnl_flowtable_free(flo);
@@ -1493,9 +1681,10 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
}
-int mnl_nft_flowtable_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nftnl_flowtable *flo;
+ const char **dev_array;
struct nlmsghdr *nlh;
flo = nftnl_flowtable_alloc();
@@ -1504,18 +1693,35 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, const struct cmd *cmd)
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
- nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_TABLE,
- cmd->handle.table.name);
- if (cmd->handle.flowtable.name)
- nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_NAME,
- cmd->handle.flowtable.name);
- else if (cmd->handle.handle.id)
- nftnl_flowtable_set_u64(flo, NFTNL_FLOWTABLE_HANDLE,
- cmd->handle.handle.id);
+
+ if (cmd->flowtable && cmd->flowtable->dev_expr) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELFLOWTABLE, cmd->handle.family,
0, ctx->seqnum);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
+
+ if (cmd->handle.flowtable.name) {
+ cmd_add_loc(cmd, nlh->nlmsg_len,
+ &cmd->handle.flowtable.location);
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME,
+ cmd->handle.flowtable.name);
+ } else if (cmd->handle.handle.id) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE,
+ htobe64(cmd->handle.handle.id));
+ }
+
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
nftnl_flowtable_free(flo);
diff --git a/src/monitor.c b/src/monitor.c
index 142cc929..bb269c02 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -906,7 +906,8 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
int netlink_echo_callback(const struct nlmsghdr *nlh, void *data)
{
- struct netlink_ctx *ctx = data;
+ struct netlink_cb_data *nl_cb_data = data;
+ struct netlink_ctx *ctx = nl_cb_data->nl_ctx;
struct netlink_mon_handler echo_monh = {
.format = NFTNL_OUTPUT_DEFAULT,
.ctx = ctx,
diff --git a/src/netlink.c b/src/netlink.c
index e4128963..fb0a17ba 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -138,6 +138,9 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
if (elem->expiration)
nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_EXPIRATION,
elem->expiration);
+ if (elem->stmt)
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_EXPR,
+ netlink_gen_stmt_stateful(elem->stmt), 0);
if (elem->comment || expr->elem_flags) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
@@ -169,7 +172,12 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_CHAIN,
nld.chain, strlen(nld.chain));
break;
+ case EXPR_CONCAT:
+ assert(nld.len > 0);
+ /* fallthrough */
case EXPR_VALUE:
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_DATA,
nld.value, nld.len);
break;
@@ -290,6 +298,38 @@ static void netlink_gen_verdict(const struct expr *expr,
}
}
+static void netlink_gen_range(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = div_round_up(expr->left->len, BITS_PER_BYTE) * 2;
+ unsigned char data[len];
+ unsigned int offset = 0;
+
+ memset(data, 0, len);
+ offset = netlink_export_pad(data, expr->left->value, expr->left);
+ netlink_export_pad(data + offset, expr->right->value, expr->right);
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
+static void netlink_gen_prefix(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2;
+ unsigned char data[len];
+ int offset;
+ mpz_t v;
+
+ offset = netlink_export_pad(data, expr->prefix->value, expr);
+ mpz_init_bitmask(v, expr->len - expr->prefix_len);
+ mpz_add(v, expr->prefix->value, v);
+ netlink_export_pad(data + offset, v, expr->prefix);
+ mpz_clear(v);
+
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
{
switch (expr->etype) {
@@ -299,6 +339,10 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
return netlink_gen_concat_data(expr, data);
case EXPR_VERDICT:
return netlink_gen_verdict(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
default:
BUG("invalid data expression type %s\n", expr_name(expr));
}
@@ -432,15 +476,17 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE));
chain->handle.handle.id =
nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE);
+ if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS))
+ chain->flags = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FLAGS);
if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_HOOKNUM) &&
nftnl_chain_is_set(nlc, NFTNL_CHAIN_PRIO) &&
nftnl_chain_is_set(nlc, NFTNL_CHAIN_TYPE) &&
nftnl_chain_is_set(nlc, NFTNL_CHAIN_POLICY)) {
- chain->hooknum =
+ chain->hook.num =
nftnl_chain_get_u32(nlc, NFTNL_CHAIN_HOOKNUM);
- chain->hookstr =
- hooknum2str(chain->handle.family, chain->hooknum);
+ chain->hook.name =
+ hooknum2str(chain->handle.family, chain->hook.num);
priority = nftnl_chain_get_s32(nlc, NFTNL_CHAIN_PRIO);
chain->priority.expr =
constant_expr_alloc(&netlink_location,
@@ -610,6 +656,7 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
case NFTNL_UDATA_SET_KEYBYTEORDER:
case NFTNL_UDATA_SET_DATABYTEORDER:
case NFTNL_UDATA_SET_MERGE_ELEMENTS:
+ case NFTNL_UDATA_SET_DATA_INTERVAL:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -693,6 +740,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
struct expr *typeof_expr_key, *typeof_expr_data;
uint32_t flags, key, objtype = 0;
const struct datatype *dtype;
+ uint32_t data_interval = 0;
bool automerge = false;
const char *udata;
struct set *set;
@@ -716,6 +764,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
GET_U32_UDATA(keybyteorder, NFTNL_UDATA_SET_KEYBYTEORDER);
GET_U32_UDATA(databyteorder, NFTNL_UDATA_SET_DATABYTEORDER);
GET_U32_UDATA(automerge, NFTNL_UDATA_SET_MERGE_ELEMENTS);
+ GET_U32_UDATA(data_interval, NFTNL_UDATA_SET_DATA_INTERVAL);
#undef GET_U32_UDATA
typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]);
@@ -758,6 +807,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME));
set->automerge = automerge;
+ if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) {
+ const struct nftnl_expr *nle;
+
+ nle = nftnl_set_get(nls, NFTNL_SET_EXPR);
+ set->stmt = netlink_parse_set_expr(set, &ctx->nft->cache, nle);
+ }
+
if (datatype) {
dtype = set_datatype_alloc(datatype, databyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE;
@@ -777,6 +833,9 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
typeof_expr_key = NULL;
}
+ if (data_interval)
+ set->data->flags |= EXPR_F_INTERVAL;
+
if (dtype != datatype)
datatype_free(datatype);
}
@@ -870,6 +929,69 @@ void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
}
}
+static bool mpz_bitmask_is_prefix(mpz_t bitmask, uint32_t len)
+{
+ unsigned long n1, n2;
+
+ n1 = mpz_scan0(bitmask, 0);
+ if (n1 == ULONG_MAX)
+ return false;
+
+ n2 = mpz_scan1(bitmask, n1 + 1);
+ if (n2 < len)
+ return false;
+
+ return true;
+}
+
+static uint32_t mpz_bitmask_to_prefix(mpz_t bitmask, uint32_t len)
+{
+ return len - mpz_scan0(bitmask, 0);
+}
+
+struct expr *range_expr_to_prefix(struct expr *range)
+{
+ struct expr *left = range->left, *right = range->right, *prefix;
+ uint32_t len = left->len, prefix_len;
+ mpz_t bitmask;
+
+ mpz_init2(bitmask, len);
+ mpz_xor(bitmask, left->value, right->value);
+
+ if (mpz_bitmask_is_prefix(bitmask, len)) {
+ prefix_len = mpz_bitmask_to_prefix(bitmask, len);
+ prefix = prefix_expr_alloc(&range->location, expr_get(left),
+ prefix_len);
+ mpz_clear(bitmask);
+ expr_free(range);
+
+ return prefix;
+ }
+ mpz_clear(bitmask);
+
+ return range;
+}
+
+static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
+ struct expr *expr)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ struct expr *range, *left, *right;
+ char data[len];
+
+ mpz_export_data(data, expr->value, dtype->byteorder, len);
+ left = constant_expr_alloc(&internal_location, dtype,
+ dtype->byteorder,
+ (len / 2) * BITS_PER_BYTE, &data[0]);
+ right = constant_expr_alloc(&internal_location, dtype,
+ dtype->byteorder,
+ (len / 2) * BITS_PER_BYTE, &data[len / 2]);
+ range = range_expr_alloc(&expr->location, left, right);
+ expr_free(expr);
+
+ return range_expr_to_prefix(range);
+}
+
static struct expr *netlink_parse_concat_elem(const struct datatype *dtype,
struct expr *data)
{
@@ -1005,6 +1127,12 @@ key_end:
NFT_REG_VERDICT : NFT_REG_1);
datatype_set(data, set->data->dtype);
data->byteorder = set->data->byteorder;
+
+ if (set->data->flags & EXPR_F_INTERVAL)
+ data = netlink_parse_interval_elem(set->data->dtype, data);
+ else if (set->data->dtype->subtypes)
+ data = netlink_parse_concat_elem(set->data->dtype, data);
+
if (data->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE);
@@ -1108,8 +1236,10 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
netlink_dump_set(nls, ctx);
nls_out = mnl_nft_setelem_get_one(ctx, nls);
- if (!nls_out)
+ if (!nls_out) {
+ nftnl_set_free(nls);
return -1;
+ }
ctx->set = set;
set->init = set_expr_alloc(loc, set);
@@ -1321,8 +1451,10 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
sizeof(int) *
BITS_PER_BYTE,
&priority);
- flowtable->hooknum =
+ flowtable->hook.num =
nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM);
+ flowtable->flags =
+ nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS);
return flowtable;
}
@@ -1428,38 +1560,50 @@ static void trace_print_policy(const struct nftnl_trace *nlt,
expr_free(expr);
}
-static void trace_print_rule(const struct nftnl_trace *nlt,
- struct output_ctx *octx, struct nft_cache *cache)
+static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt,
+ uint64_t rule_handle,
+ struct nft_cache *cache)
{
- const struct table *table;
- uint64_t rule_handle;
struct chain *chain;
- struct rule *rule;
+ struct table *table;
struct handle h;
h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY);
- h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE);
- h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN);
+ h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE);
+ h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN);
if (!h.table.name)
- return;
+ return NULL;
table = table_lookup(&h, cache);
if (!table)
- return;
+ return NULL;
chain = chain_lookup(table, &h);
if (!chain)
- return;
+ return NULL;
+
+ return rule_lookup(chain, rule_handle);
+}
+
+static void trace_print_rule(const struct nftnl_trace *nlt,
+ struct output_ctx *octx, struct nft_cache *cache)
+{
+ uint64_t rule_handle;
+ struct rule *rule;
rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE);
- rule = rule_lookup(chain, rule_handle);
- if (!rule)
- return;
+ rule = trace_lookup_rule(nlt, rule_handle, cache);
trace_print_hdr(nlt, octx);
- nft_print(octx, "rule ");
- rule_print(rule, octx);
+
+ if (rule) {
+ nft_print(octx, "rule ");
+ rule_print(rule, octx);
+ } else {
+ nft_print(octx, "unknown rule handle %" PRIu64, rule_handle);
+ }
+
nft_print(octx, " (");
trace_print_verdict(nlt, octx);
nft_print(octx, ")\n");
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 7d9c7646..7f7ad262 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -15,6 +15,7 @@
#include <limits.h>
#include <linux/netfilter/nf_tables.h>
#include <arpa/inet.h>
+#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter.h>
#include <net/ethernet.h>
#include <netlink.h>
@@ -440,6 +441,7 @@ static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
nld.value = nftnl_expr_get(nle, NFTNL_EXPR_BITWISE_DATA, &nld.len);
right = netlink_alloc_value(loc, &nld);
+ right->byteorder = BYTEORDER_HOST_ENDIAN;
expr = binop_expr_alloc(loc, op, left, right);
expr->len = left->len;
@@ -561,8 +563,7 @@ static void netlink_parse_payload_stmt(struct netlink_parse_ctx *ctx,
payload_init_raw(expr, base, offset, len);
stmt = payload_stmt_alloc(loc, expr, val);
-
- list_add_tail(&stmt->list, &ctx->rule->stmts);
+ rule_stmt_append(ctx->rule, stmt);
}
static void netlink_parse_payload(struct netlink_parse_ctx *ctx,
@@ -613,7 +614,7 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
expr_set_type(val, expr->dtype, expr->byteorder);
stmt = exthdr_stmt_alloc(loc, expr, val);
- list_add_tail(&stmt->list, &ctx->rule->stmts);
+ rule_stmt_append(ctx->rule, stmt);
}
}
@@ -978,6 +979,67 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
+static bool is_nat_addr_map(const struct expr *addr, uint8_t family)
+{
+ const struct expr *mappings, *data;
+ const struct set *set;
+
+ if (!addr ||
+ expr_ops(addr)->type != EXPR_MAP)
+ return false;
+
+ mappings = addr->right;
+ if (expr_ops(mappings)->type != EXPR_SET_REF)
+ return false;
+
+ set = mappings->set;
+ data = set->data;
+
+ if (!(data->flags & EXPR_F_INTERVAL))
+ return false;
+
+ /* if we're dealing with an address:address map,
+ * the length will be bit_sizeof(addr) + 32 (one register).
+ */
+ switch (family) {
+ case NFPROTO_IPV4:
+ return data->len == 32 + 32;
+ case NFPROTO_IPV6:
+ return data->len == 128 + 128;
+ }
+
+ return false;
+}
+
+static bool is_nat_proto_map(const struct expr *addr, uint8_t family)
+{
+ const struct expr *mappings, *data;
+ const struct set *set;
+
+ if (!addr ||
+ expr_ops(addr)->type != EXPR_MAP)
+ return false;
+
+ mappings = addr->right;
+ if (expr_ops(mappings)->type != EXPR_SET_REF)
+ return false;
+
+ set = mappings->set;
+ data = set->data;
+
+ /* if we're dealing with an address:inet_service map,
+ * the length will be bit_sizeof(addr) + 32 (one register).
+ */
+ switch (family) {
+ case NFPROTO_IPV4:
+ return data->len == 32 + 32;
+ case NFPROTO_IPV6:
+ return data->len == 128 + 32;
+ }
+
+ return false;
+}
+
static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -998,6 +1060,10 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
if (nftnl_expr_is_set(nle, NFTNL_EXPR_NAT_FLAGS))
stmt->nat.flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_NAT_FLAGS);
+ if (stmt->nat.flags & NF_NAT_RANGE_NETMAP)
+ stmt->nat.type_flags |= STMT_NAT_F_PREFIX;
+
+ addr = NULL;
reg1 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MIN);
if (reg1) {
addr = netlink_get_register(ctx, loc, reg1);
@@ -1015,6 +1081,13 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
stmt->nat.addr = addr;
}
+ if (is_nat_addr_map(addr, family)) {
+ stmt->nat.family = family;
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+ ctx->stmt = stmt;
+ return;
+ }
+
reg2 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX);
if (reg2 && reg2 != reg1) {
addr = netlink_get_register(ctx, loc, reg2);
@@ -1029,11 +1102,20 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
else
expr_set_type(addr, &ip6addr_type,
BYTEORDER_BIG_ENDIAN);
- if (stmt->nat.addr != NULL)
+ if (stmt->nat.addr != NULL) {
addr = range_expr_alloc(loc, stmt->nat.addr, addr);
+ addr = range_expr_to_prefix(addr);
+ }
stmt->nat.addr = addr;
}
+ if (is_nat_proto_map(addr, family)) {
+ stmt->nat.family = family;
+ stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
+ ctx->stmt = stmt;
+ return;
+ }
+
reg1 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_PROTO_MIN);
if (reg1) {
proto = netlink_get_register(ctx, loc, reg1);
@@ -1424,6 +1506,7 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
}
if (expr_data != NULL) {
+ expr_set_type(expr_data, set->data->dtype, set->data->byteorder);
stmt = map_stmt_alloc(loc);
stmt->map.set = set_ref_expr_alloc(loc, set);
stmt->map.key = expr;
@@ -1588,7 +1671,7 @@ static int netlink_parse_rule_expr(struct nftnl_expr *nle, void *arg)
if (err < 0)
return err;
if (ctx->stmt != NULL) {
- list_add_tail(&ctx->stmt->list, &ctx->rule->stmts);
+ rule_stmt_append(ctx->rule, ctx->stmt);
ctx->stmt = NULL;
}
return 0;
@@ -2214,6 +2297,8 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
case EXPR_RANGE:
expr_postprocess(ctx, &expr->left);
expr_postprocess(ctx, &expr->right);
+ case EXPR_PREFIX:
+ expr_postprocess(ctx, &expr->prefix);
break;
case EXPR_SET_ELEM:
expr_postprocess(ctx, &expr->key);
@@ -2519,6 +2604,7 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
mpz_init_bitmask(bitmask, payload->len);
mpz_xor(bitmask, bitmask, value->value);
mpz_set(value->value, bitmask);
+ mpz_clear(bitmask);
break;
case OP_OR: /* IIb */
break;
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index b542aa3b..08f7f89f 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -822,9 +822,7 @@ static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
-static struct nftnl_expr *
-netlink_gen_connlimit_stmt(struct netlink_linearize_ctx *ctx,
- const struct stmt *stmt)
+static struct nftnl_expr *netlink_gen_connlimit_stmt(const struct stmt *stmt)
{
struct nftnl_expr *nle;
@@ -837,9 +835,7 @@ netlink_gen_connlimit_stmt(struct netlink_linearize_ctx *ctx,
return nle;
}
-static struct nftnl_expr *
-netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
- const struct stmt *stmt)
+static struct nftnl_expr *netlink_gen_counter_stmt(const struct stmt *stmt)
{
struct nftnl_expr *nle;
@@ -856,9 +852,7 @@ netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
return nle;
}
-static struct nftnl_expr *
-netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
- const struct stmt *stmt)
+static struct nftnl_expr *netlink_gen_limit_stmt(const struct stmt *stmt)
{
struct nftnl_expr *nle;
@@ -874,9 +868,7 @@ netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
return nle;
}
-static struct nftnl_expr *
-netlink_gen_quota_stmt(struct netlink_linearize_ctx *ctx,
- const struct stmt *stmt)
+static struct nftnl_expr *netlink_gen_quota_stmt(const struct stmt *stmt)
{
struct nftnl_expr *nle;
@@ -888,19 +880,17 @@ netlink_gen_quota_stmt(struct netlink_linearize_ctx *ctx,
return nle;
}
-static struct nftnl_expr *
-netlink_gen_stmt_stateful(struct netlink_linearize_ctx *ctx,
- const struct stmt *stmt)
+struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt)
{
switch (stmt->ops->type) {
case STMT_CONNLIMIT:
- return netlink_gen_connlimit_stmt(ctx, stmt);
+ return netlink_gen_connlimit_stmt(stmt);
case STMT_COUNTER:
- return netlink_gen_counter_stmt(ctx, stmt);
+ return netlink_gen_counter_stmt(stmt);
case STMT_LIMIT:
- return netlink_gen_limit_stmt(ctx, stmt);
+ return netlink_gen_limit_stmt(stmt);
case STMT_QUOTA:
- return netlink_gen_quota_stmt(ctx, stmt);
+ return netlink_gen_quota_stmt(stmt);
default:
BUG("unknown stateful statement type %s\n", stmt->ops->name);
}
@@ -1052,14 +1042,25 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
+static unsigned int nat_addrlen(uint8_t family)
+{
+ switch (family) {
+ case NFPROTO_IPV4: return 32;
+ case NFPROTO_IPV6: return 128;
+ }
+
+ BUG("invalid nat family %u\n", family);
+ return 0;
+}
+
static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
struct nftnl_expr *nle;
enum nft_registers amin_reg, amax_reg;
enum nft_registers pmin_reg, pmax_reg;
+ uint8_t family = 0;
int registers = 0;
- int family;
int nftnl_flag_attr;
int nftnl_reg_pmin, nftnl_reg_pmax;
@@ -1116,8 +1117,34 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
netlink_gen_expr(ctx, stmt->nat.addr, amin_reg);
netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MIN,
amin_reg);
+ if (stmt->nat.addr->etype == EXPR_MAP &&
+ stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL) {
+ amax_reg = get_register(ctx, NULL);
+ registers++;
+ amin_reg += netlink_register_space(nat_addrlen(family));
+ netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
+ amin_reg);
+ }
}
+ if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ /* nat_stmt evaluation step doesn't allow
+ * STMT_NAT_F_CONCAT && stmt->nat.proto.
+ */
+ assert(stmt->nat.proto == NULL);
+
+ pmin_reg = amin_reg;
+
+ /* if STMT_NAT_F_CONCAT is set, the mapped type is a
+ * concatenation of 'addr . inet_service'.
+ * The map lookup will then return the
+ * concatenated value, so we need to skip
+ * the address and use the register that
+ * will hold the inet_service part.
+ */
+ pmin_reg += netlink_register_space(nat_addrlen(family));
+ netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
+ }
}
if (stmt->nat.proto) {
@@ -1349,7 +1376,7 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
if (stmt->set.stmt)
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
- netlink_gen_stmt_stateful(ctx, stmt->set.stmt), 0);
+ netlink_gen_stmt_stateful(stmt->set.stmt), 0);
}
static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
@@ -1379,7 +1406,7 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
if (stmt->map.stmt)
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
- netlink_gen_stmt_stateful(ctx, stmt->map.stmt), 0);
+ netlink_gen_stmt_stateful(stmt->map.stmt), 0);
nftnl_rule_add_expr(ctx->nlr, nle);
}
@@ -1411,7 +1438,7 @@ static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
- netlink_gen_stmt_stateful(ctx, stmt->meter.stmt), 0);
+ netlink_gen_stmt_stateful(stmt->meter.stmt), 0);
nftnl_rule_add_expr(ctx->nlr, nle);
}
@@ -1457,7 +1484,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_COUNTER:
case STMT_LIMIT:
case STMT_QUOTA:
- nle = netlink_gen_stmt_stateful(ctx, stmt);
+ nle = netlink_gen_stmt_stateful(stmt);
nftnl_rule_add_expr(ctx->nlr, nle);
break;
case STMT_NOTRACK:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index f5d7026a..461d9bf2 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -373,6 +373,7 @@ int nft_lex(void *, void *, void *);
%token FLAGS "flags"
%token CPI "cpi"
+%token PORT "port"
%token UDP "udp"
%token SPORT "sport"
%token DPORT "dport"
@@ -979,7 +980,7 @@ add_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
- $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
'{' flowtable_block '}'
@@ -1076,7 +1077,7 @@ create_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
- $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
'{' flowtable_block '}'
@@ -1168,7 +1169,7 @@ delete_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec
{
@@ -1178,6 +1179,13 @@ delete_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
}
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
| COUNTER obj_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
@@ -1226,7 +1234,7 @@ delete_cmd : TABLE table_spec
get_cmd : ELEMENT set_spec set_block_expr
{
- $$ = cmd_alloc(CMD_GET, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ $$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
;
@@ -1666,6 +1674,7 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
| chain_block stmt_separator
| chain_block hook_spec stmt_separator
| chain_block policy_spec stmt_separator
+ | chain_block flags_spec stmt_separator
| chain_block rule stmt_separator
{
list_add_tail(&$2->list, &$1->rules);
@@ -1731,6 +1740,11 @@ set_block : /* empty */ { $$ = $<set>-1; }
$1->gc_int = $3;
$$ = $1;
}
+ | set_block COUNTER stmt_separator
+ {
+ $1->stmt = counter_stmt_alloc(&@$);
+ $$ = $1;
+ }
| set_block ELEMENTS '=' set_block_expr
{
$1->init = $4;
@@ -1785,6 +1799,17 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->flags |= NFT_SET_MAP;
$$ = $1;
}
+ | map_block TYPE
+ data_type_expr COLON INTERVAL data_type_expr
+ stmt_separator
+ {
+ $1->key = $3;
+ $1->data = $6;
+ $1->data->flags |= EXPR_F_INTERVAL;
+
+ $1->flags |= NFT_SET_MAP;
+ $$ = $1;
+ }
| map_block TYPEOF
typeof_expr COLON typeof_expr
stmt_separator
@@ -1796,6 +1821,18 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->flags |= NFT_SET_MAP;
$$ = $1;
}
+ | map_block TYPEOF
+ typeof_expr COLON INTERVAL typeof_expr
+ stmt_separator
+ {
+ $1->key = $3;
+ datatype_set($1->key, $3->dtype);
+ $1->data = $6;
+ $1->data->flags |= EXPR_F_INTERVAL;
+
+ $1->flags |= NFT_SET_MAP;
+ $$ = $1;
+ }
| map_block TYPE
data_type_expr COLON COUNTER
stmt_separator
@@ -1870,9 +1907,10 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
| flowtable_block stmt_separator
| flowtable_block HOOK STRING prio_spec stmt_separator
{
- $$->hookstr = chain_hookname_lookup($3);
- if ($$->hookstr == NULL) {
- erec_queue(error(&@3, "unknown chain hook %s", $3),
+ $$->hook.loc = @3;
+ $$->hook.name = chain_hookname_lookup($3);
+ if ($$->hook.name == NULL) {
+ erec_queue(error(&@3, "unknown chain hook"),
state->msgs);
xfree($3);
YYERROR;
@@ -1885,6 +1923,10 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
{
$$->dev_expr = $4;
}
+ | flowtable_block COUNTER
+ {
+ $$->flags |= NFT_FLOWTABLE_COUNTER;
+ }
;
flowtable_expr : '{' flowtable_list_expr '}'
@@ -1909,9 +1951,9 @@ flowtable_list_expr : flowtable_expr_member
flowtable_expr_member : STRING
{
- $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $1);
+ $$ = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen($1) * BITS_PER_BYTE, $1);
xfree($1);
}
;
@@ -1980,7 +2022,11 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
}
;
-ct_timeout_block : /*empty */ { $$ = $<obj>-1; }
+ct_timeout_block : /*empty */
+ {
+ $$ = $<obj>-1;
+ init_list_head(&$$->ct_timeout.timeout_list);
+ }
| ct_timeout_block common_block
| ct_timeout_block stmt_separator
| ct_timeout_block ct_timeout_config
@@ -2037,7 +2083,7 @@ hook_spec : TYPE STRING HOOK STRING dev_spec prio_spec
const char *chain_type = chain_type_name_lookup($2);
if (chain_type == NULL) {
- erec_queue(error(&@2, "unknown chain type %s", $2),
+ erec_queue(error(&@2, "unknown chain type"),
state->msgs);
xfree($2);
YYERROR;
@@ -2045,9 +2091,11 @@ hook_spec : TYPE STRING HOOK STRING dev_spec prio_spec
$<chain>0->type = xstrdup(chain_type);
xfree($2);
- $<chain>0->hookstr = chain_hookname_lookup($4);
- if ($<chain>0->hookstr == NULL) {
- erec_queue(error(&@4, "unknown chain hook %s", $4),
+ $<chain>0->loc = @$;
+ $<chain>0->hook.loc = @4;
+ $<chain>0->hook.name = chain_hookname_lookup($4);
+ if ($<chain>0->hook.name == NULL) {
+ erec_queue(error(&@4, "unknown chain hook"),
state->msgs);
xfree($4);
YYERROR;
@@ -2126,6 +2174,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
+ xfree($1);
$$ = spec;
}
;
@@ -2141,6 +2190,7 @@ dev_spec : DEVICE string
expr = constant_expr_alloc(&@$, &string_type,
BYTEORDER_HOST_ENDIAN,
strlen($2) * BITS_PER_BYTE, $2);
+ xfree($2);
$$ = compound_expr_alloc(&@$, EXPR_LIST);
compound_expr_add($$, expr);
@@ -2152,6 +2202,12 @@ dev_spec : DEVICE string
| /* empty */ { $$ = NULL; }
;
+flags_spec : FLAGS OFFLOAD
+ {
+ $<chain>0->flags |= CHAIN_F_HW_OFFLOAD;
+ }
+ ;
+
policy_spec : POLICY policy_expr
{
if ($<chain>0->policy) {
@@ -2160,7 +2216,8 @@ policy_spec : POLICY policy_expr
expr_free($2);
YYERROR;
}
- $<chain>0->policy = $2;
+ $<chain>0->policy = $2;
+ $<chain>0->policy->location = @$;
}
;
@@ -2231,7 +2288,7 @@ tableid_spec : family_spec HANDLE NUM
memset(&$$, 0, sizeof($$));
$$.family = $1;
$$.handle.id = $3;
- $$.handle.location = @$;
+ $$.handle.location = @3;
}
;
@@ -2246,7 +2303,7 @@ chain_spec : table_spec identifier
chainid_spec : table_spec HANDLE NUM
{
$$ = $1;
- $$.handle.location = @$;
+ $$.handle.location = @3;
$$.handle.id = $3;
}
;
@@ -2270,7 +2327,7 @@ set_spec : table_spec identifier
setid_spec : table_spec HANDLE NUM
{
$$ = $1;
- $$.handle.location = @$;
+ $$.handle.location = @3;
$$.handle.id = $3;
}
;
@@ -2294,7 +2351,7 @@ flowtable_spec : table_spec identifier
flowtableid_spec : table_spec HANDLE NUM
{
$$ = $1;
- $$.handle.location = @$;
+ $$.handle.location = @3;
$$.handle.id = $3;
}
;
@@ -2318,7 +2375,7 @@ obj_spec : table_spec identifier
objid_spec : table_spec HANDLE NUM
{
$$ = $1;
- $$.handle.location = @$;
+ $$.handle.location = @3;
$$.handle.id = $3;
}
;
@@ -2334,7 +2391,7 @@ obj_identifier : identifier
handle_spec : HANDLE NUM
{
memset(&$$, 0, sizeof($$));
- $$.handle.location = @$;
+ $$.handle.location = @2;
$$.handle.id = $2;
}
;
@@ -3012,7 +3069,7 @@ shift_stmt_expr : primary_stmt_expr
{
$$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
}
- | shift_stmt_expr RSHIFT primary_rhs_expr
+ | shift_stmt_expr RSHIFT primary_stmt_expr
{
$$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
}
@@ -3139,6 +3196,40 @@ nat_stmt_args : stmt_expr
{
$<stmt>0->nat.flags = $2;
}
+ | nf_key_proto ADDR DOT PORT TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $6;
+ $<stmt>0->nat.type_flags = STMT_NAT_F_CONCAT;
+ }
+ | nf_key_proto INTERVAL TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $4;
+ $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
+ }
+ | INTERVAL TO stmt_expr
+ {
+ $<stmt>0->nat.addr = $3;
+ $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
+ }
+ | nf_key_proto PREFIX TO stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $4;
+ $<stmt>0->nat.type_flags =
+ STMT_NAT_F_PREFIX |
+ STMT_NAT_F_INTERVAL;
+ $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
+ }
+ | PREFIX TO stmt_expr
+ {
+ $<stmt>0->nat.addr = $3;
+ $<stmt>0->nat.type_flags =
+ STMT_NAT_F_PREFIX |
+ STMT_NAT_F_INTERVAL;
+ $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
+ }
;
masq_stmt : masq_stmt_alloc masq_stmt_args
@@ -3655,7 +3746,7 @@ meter_key_expr_alloc : concat_expr
;
set_elem_expr : set_elem_expr_alloc
- | set_elem_expr_alloc set_elem_options
+ | set_elem_expr_alloc set_elem_expr_options
;
set_elem_expr_alloc : set_lhs_expr
@@ -3685,6 +3776,40 @@ set_elem_option : TIMEOUT time_spec
}
;
+set_elem_expr_options : set_elem_expr_option
+ {
+ $<expr>$ = $<expr>0;
+ }
+ | set_elem_expr_options set_elem_expr_option
+ ;
+
+set_elem_expr_option : TIMEOUT time_spec
+ {
+ $<expr>0->timeout = $2;
+ }
+ | EXPIRES time_spec
+ {
+ $<expr>0->expiration = $2;
+ }
+ | COUNTER
+ {
+ $<expr>0->stmt = counter_stmt_alloc(&@$);
+ }
+ | COUNTER PACKETS NUM BYTES NUM
+ {
+ struct stmt *stmt;
+
+ stmt = counter_stmt_alloc(&@$);
+ stmt->counter.packets = $3;
+ stmt->counter.bytes = $5;
+ $<expr>0->stmt = stmt;
+ }
+ | comment_spec
+ {
+ $<expr>0->comment = $1;
+ }
+ ;
+
set_lhs_expr : concat_rhs_expr
| wildcard_expr
;
@@ -3785,6 +3910,7 @@ ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator
erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
YYERROR;
}
+ xfree($2);
ct->l4proto = $4;
}
@@ -3834,8 +3960,8 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
struct ct_timeout *ct;
ct = &$<obj>0->ct_timeout;
- init_list_head(&ct->timeout_list);
list_splice_tail($4, &ct->timeout_list);
+ xfree($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
@@ -4434,6 +4560,7 @@ ct_key : L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; }
| LABEL { $$ = NFT_CT_LABELS; }
| EVENT { $$ = NFT_CT_EVENTMASK; }
| SECMARK { $$ = NFT_CT_SECMARK; }
+ | ID { $$ = NFT_CT_ID; }
| ct_key_dir_optional
;
diff --git a/src/parser_json.c b/src/parser_json.c
index 85082cce..9fdef691 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -40,6 +40,7 @@
#define CTX_F_MANGLE (1 << 5)
#define CTX_F_SES (1 << 6) /* set_elem_expr_stmt */
#define CTX_F_MAP (1 << 7) /* LHS of map_expr */
+#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */
struct json_ctx {
struct input_descriptor indesc;
@@ -99,6 +100,7 @@ static struct expr *json_parse_primary_expr(struct json_ctx *ctx, json_t *root);
static struct expr *json_parse_set_rhs_expr(struct json_ctx *ctx, json_t *root);
static struct expr *json_parse_set_elem_expr_stmt(struct json_ctx *ctx, json_t *root);
static struct expr *json_parse_map_lhs_expr(struct json_ctx *ctx, json_t *root);
+static struct expr *json_parse_concat_elem_expr(struct json_ctx *ctx, json_t *root);
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root);
/* parsing helpers */
@@ -1058,7 +1060,7 @@ static struct expr *json_parse_concat_expr(struct json_ctx *ctx,
}
json_array_foreach(root, index, value) {
- tmp = json_parse_primary_expr(ctx, value);
+ tmp = json_parse_concat_elem_expr(ctx, value);
if (!tmp) {
json_error(ctx, "Parsing expr at index %zd failed.", index);
expr_free(expr);
@@ -1354,28 +1356,28 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "set", json_parse_set_expr, CTX_F_RHS | CTX_F_STMT }, /* allow this as stmt expr because that allows set references */
{ "map", json_parse_map_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS },
/* below three are multiton_rhs_expr */
- { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_STMT },
- { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_STMT },
- { "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
- { "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES },
- { "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES },
- { "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
- { "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP },
- { "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP },
- { "socket", json_parse_socket_expr, CTX_F_PRIMARY },
- { "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "ct", json_parse_ct_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
- { "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
+ { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT },
+ { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT },
+ { "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
+ { "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
+ { "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
+ { "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
+ { "socket", json_parse_socket_expr, CTX_F_PRIMARY | CTX_F_CONCAT },
+ { "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "ct", json_parse_ct_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
/* below two are hash expr */
- { "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { ">>", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
- { "<<", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
+ { "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { ">>", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "<<", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "accept", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS },
{ "drop", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS },
{ "continue", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS },
@@ -1500,6 +1502,11 @@ static struct expr *json_parse_map_lhs_expr(struct json_ctx *ctx, json_t *root)
return json_parse_flagged_expr(ctx, CTX_F_MAP, root);
}
+static struct expr *json_parse_concat_elem_expr(struct json_ctx *ctx, json_t *root)
+{
+ return json_parse_flagged_expr(ctx, CTX_F_CONCAT, root);
+}
+
static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root)
{
if (json_is_string(root)) {
@@ -2621,8 +2628,8 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE,
&prio);
- chain->hookstr = chain_hookname_lookup(hookstr);
- if (!chain->hookstr) {
+ chain->hook.name = chain_hookname_lookup(hookstr);
+ if (!chain->hook.name) {
json_error(ctx, "Invalid chain hook '%s'.", hookstr);
chain_free(chain);
return NULL;
@@ -2724,8 +2731,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
return NULL;
}
- rule->num_stmts++;
- list_add_tail(&stmt->list, &rule->stmts);
+ rule_stmt_append(rule, stmt);
}
if (op == CMD_ADD)
@@ -3010,7 +3016,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
}
flowtable = flowtable_alloc(int_loc);
- flowtable->hookstr = hookstr;
+ flowtable->hook.name = hookstr;
flowtable->priority.expr =
constant_expr_alloc(int_loc, &integer_type,
BYTEORDER_HOST_ENDIAN,
@@ -3287,7 +3293,7 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
{ "rule", CMD_OBJ_RULE, json_parse_cmd_add_rule },
{ "set", CMD_OBJ_SET, json_parse_cmd_add_set },
{ "map", CMD_OBJ_SET, json_parse_cmd_add_set },
- { "element", CMD_OBJ_SETELEM, json_parse_cmd_add_element },
+ { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element },
{ "flowtable", CMD_OBJ_FLOWTABLE, json_parse_cmd_add_flowtable },
{ "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
@@ -3397,8 +3403,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
return NULL;
}
- rule->num_stmts++;
- list_add_tail(&stmt->list, &rule->stmts);
+ rule_stmt_append(rule, stmt);
}
if (op == CMD_REPLACE)
@@ -3842,12 +3847,15 @@ static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh)
}
int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh)
{
- json_t *tmp, *json = seqnum_to_json(nlh->nlmsg_seq);
uint64_t handle = handle_from_nlmsg(nlh);
+ json_t *tmp, *json;
void *iter;
- /* might be anonymous set, ignore message */
- if (!json || !handle)
+ if (!handle)
+ return MNL_CB_OK;
+
+ json = seqnum_to_json(nlh->nlmsg_seq);
+ if (!json)
return MNL_CB_OK;
tmp = json_object_get(json, "add");
diff --git a/src/rule.c b/src/rule.c
index 337a66bb..21a52157 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -355,6 +355,7 @@ void set_free(struct set *set)
if (set->init != NULL)
expr_free(set->init);
handle_free(&set->handle);
+ stmt_free(set->stmt);
expr_free(set->key);
expr_free(set->data);
xfree(set);
@@ -461,6 +462,9 @@ static void set_print_key_and_data(const struct set *set, struct output_ctx *oct
if (set_is_datamap(set->flags)) {
nft_print(octx, " : ");
+ if (set->data->flags & EXPR_F_INTERVAL)
+ nft_print(octx, "interval ");
+
if (use_typeof)
expr_print(set->data, octx);
else
@@ -544,6 +548,15 @@ static void set_print_declaration(const struct set *set,
}
nft_print(octx, "%s", opts->stmt_separator);
}
+
+ if (set->stmt) {
+ nft_print(octx, "%s%s", opts->tab, opts->tab);
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+ stmt_print(set->stmt, octx);
+ octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+ nft_print(octx, "%s", opts->stmt_separator);
+ }
+
if (set->automerge)
nft_print(octx, "%s%sauto-merge%s", opts->tab, opts->tab,
opts->stmt_separator);
@@ -673,6 +686,19 @@ struct rule *rule_lookup_by_index(const struct chain *chain, uint64_t index)
return NULL;
}
+void rule_stmt_append(struct rule *rule, struct stmt *stmt)
+{
+ list_add_tail(&stmt->list, &rule->stmts);
+ rule->num_stmts++;
+}
+
+void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
+ struct stmt *stmt)
+{
+ list_add_tail(&nstmt->list, &stmt->list);
+ rule->num_stmts++;
+}
+
struct scope *scope_alloc(void)
{
struct scope *scope = xzalloc(sizeof(struct scope));
@@ -1155,7 +1181,7 @@ static void chain_print_declaration(const struct chain *chain,
nft_print(octx, "\n");
if (chain->flags & CHAIN_F_BASECHAIN) {
nft_print(octx, "\t\ttype %s hook %s", chain->type,
- hooknum2str(chain->handle.family, chain->hooknum));
+ hooknum2str(chain->handle.family, chain->hook.num));
if (chain->dev_array_len == 1) {
nft_print(octx, " device \"%s\"", chain->dev_array[0]);
} else if (chain->dev_array_len > 1) {
@@ -1169,7 +1195,7 @@ static void chain_print_declaration(const struct chain *chain,
}
nft_print(octx, " priority %s;",
prio2str(octx, priobuf, sizeof(priobuf),
- chain->handle.family, chain->hooknum,
+ chain->handle.family, chain->hook.num,
chain->priority.expr));
if (chain->policy) {
mpz_export_data(&policy, chain->policy->value,
@@ -1177,6 +1203,9 @@ static void chain_print_declaration(const struct chain *chain,
nft_print(octx, " policy %s;",
chain_policy2str(policy));
}
+ if (chain->flags & CHAIN_F_HW_OFFLOAD)
+ nft_print(octx, " flags offload;");
+
nft_print(octx, "\n");
}
}
@@ -1207,9 +1236,9 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx)
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
nft_print(octx, " { type %s hook %s priority %s; policy %s; }",
- chain->type, chain->hookstr,
+ chain->type, chain->hook.name,
prio2str(octx, priobuf, sizeof(priobuf),
- chain->handle.family, chain->hooknum,
+ chain->handle.family, chain->hook.num,
chain->priority.expr),
chain_policy2str(policy));
}
@@ -1377,14 +1406,22 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
return cmd;
}
+void cmd_add_loc(struct cmd *cmd, uint16_t offset, struct location *loc)
+{
+ assert(cmd->num_attrs < NFT_NLATTR_LOC_MAX);
+ cmd->attr[cmd->num_attrs].offset = offset;
+ cmd->attr[cmd->num_attrs].location = loc;
+ cmd->num_attrs++;
+}
+
void nft_cmd_expand(struct cmd *cmd)
{
struct list_head new_cmds;
+ struct set *set, *newset;
struct flowtable *ft;
struct table *table;
struct chain *chain;
struct rule *rule;
- struct set *set;
struct obj *obj;
struct cmd *new;
struct handle h;
@@ -1440,6 +1477,18 @@ void nft_cmd_expand(struct cmd *cmd)
}
list_splice(&new_cmds, &cmd->list);
break;
+ case CMD_OBJ_SET:
+ set = cmd->set;
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &set->handle);
+ newset = set_clone(set);
+ newset->handle.set_id = set->handle.set_id;
+ newset->init = set->init;
+ set->init = NULL;
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
+ &set->location, newset);
+ list_add(&new->list, &cmd->list);
+ break;
default:
break;
}
@@ -1484,10 +1533,11 @@ void cmd_free(struct cmd *cmd)
handle_free(&cmd->handle);
if (cmd->data != NULL) {
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
expr_free(cmd->expr);
break;
case CMD_OBJ_SET:
+ case CMD_OBJ_SETELEMS:
set_free(cmd->set);
break;
case CMD_OBJ_RULE:
@@ -1539,7 +1589,8 @@ static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set,
if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0)
return -1;
- if (set->init != NULL &&
+ if (!set_is_anonymous(set->flags) &&
+ set->init != NULL && set->init != expr &&
set->flags & NFT_SET_INTERVAL &&
set->desc.field_count <= 1) {
interval_map_decompose(expr);
@@ -1571,8 +1622,8 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
return __do_add_setelems(ctx, set, init, flags);
}
-static int do_add_set(struct netlink_ctx *ctx, const struct cmd *cmd,
- uint32_t flags)
+static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
+ uint32_t flags, bool add)
{
struct set *set = cmd->set;
@@ -1583,7 +1634,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct cmd *cmd,
&ctx->nft->output) < 0)
return -1;
}
- if (mnl_nft_set_add(ctx, cmd, flags) < 0)
+ if (add && mnl_nft_set_add(ctx, cmd, flags) < 0)
return -1;
if (set->init != NULL) {
return __do_add_setelems(ctx, set, set->init, flags);
@@ -1606,8 +1657,10 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
case CMD_OBJ_RULE:
return mnl_nft_rule_add(ctx, cmd, flags | NLM_F_APPEND);
case CMD_OBJ_SET:
- return do_add_set(ctx, cmd, flags);
- case CMD_OBJ_SETELEM:
+ return do_add_set(ctx, cmd, flags, true);
+ case CMD_OBJ_SETELEMS:
+ return do_add_set(ctx, cmd, flags, false);
+ case CMD_OBJ_ELEMENTS:
return do_add_setelems(ctx, cmd, flags);
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
@@ -1686,7 +1739,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
return mnl_nft_rule_del(ctx, cmd);
case CMD_OBJ_SET:
return mnl_nft_set_del(ctx, cmd);
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
return do_delete_setelems(ctx, cmd);
case CMD_OBJ_COUNTER:
return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_COUNTER);
@@ -2214,18 +2267,24 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%shook %s priority %s%s",
opts->tab, opts->tab,
- hooknum2str(NFPROTO_NETDEV, flowtable->hooknum),
+ hooknum2str(NFPROTO_NETDEV, flowtable->hook.num),
prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV,
- flowtable->hooknum, flowtable->priority.expr),
+ flowtable->hook.num, flowtable->priority.expr),
opts->stmt_separator);
- nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
- for (i = 0; i < flowtable->dev_array_len; i++) {
- nft_print(octx, "%s", flowtable->dev_array[i]);
- if (i + 1 != flowtable->dev_array_len)
- nft_print(octx, ", ");
+ if (flowtable->dev_array_len > 0) {
+ nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
+ for (i = 0; i < flowtable->dev_array_len; i++) {
+ nft_print(octx, "%s", flowtable->dev_array[i]);
+ if (i + 1 != flowtable->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " }%s", opts->stmt_separator);
}
- nft_print(octx, " }%s", opts->stmt_separator);
+
+ if (flowtable->flags & NFT_FLOWTABLE_COUNTER)
+ nft_print(octx, "%s%scounter%s", opts->tab, opts->tab,
+ opts->stmt_separator);
}
static void do_flowtable_print(const struct flowtable *flowtable,
@@ -2546,7 +2605,7 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
table = table_lookup(&cmd->handle, &ctx->nft->cache);
switch (cmd->obj) {
- case CMD_OBJ_SETELEM:
+ case CMD_OBJ_ELEMENTS:
return do_get_setelems(ctx, cmd, table);
default:
BUG("invalid command object type %u\n", cmd->obj);
diff --git a/src/scanner.l b/src/scanner.l
index ed29833b..45699c85 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -471,6 +471,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"udplite" { return UDPLITE; }
"sport" { return SPORT; }
"dport" { return DPORT; }
+"port" { return PORT; }
"tcp" { return TCP; }
"ackseq" { return ACKSEQ; }
@@ -678,7 +679,7 @@ static void scanner_push_indesc(struct parser_state *state,
static void scanner_pop_indesc(struct parser_state *state)
{
- if (!list_empty(&state->indesc_list)) {
+ if (!list_is_first(&state->indesc->list, &state->indesc_list)) {
state->indesc = list_entry(state->indesc->list.prev,
struct input_descriptor, list);
} else {
diff --git a/src/segtree.c b/src/segtree.c
index 8d79332d..266a2b4d 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -190,7 +190,8 @@ static bool segtree_debug(unsigned int debug_mask)
static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
struct elementary_interval *new, bool merge)
{
- struct elementary_interval *lei, *rei;
+ struct elementary_interval *lei, *rei, *ei;
+ struct expr *new_expr, *expr;
mpz_t p;
mpz_init2(p, tree->keylen);
@@ -205,8 +206,10 @@ static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
pr_gmp_debug("insert: [%Zx %Zx]\n", new->left, new->right);
if (lei != NULL && rei != NULL && lei == rei) {
- if (!merge)
+ if (!merge) {
+ ei = lei;
goto err;
+ }
/*
* The new interval is entirely contained in the same interval,
* split it into two parts:
@@ -228,8 +231,10 @@ static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
ei_destroy(lei);
} else {
if (lei != NULL) {
- if (!merge)
+ if (!merge) {
+ ei = lei;
goto err;
+ }
/*
* Left endpoint is within lei, adjust it so we have:
*
@@ -248,8 +253,10 @@ static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
}
}
if (rei != NULL) {
- if (!merge)
+ if (!merge) {
+ ei = rei;
goto err;
+ }
/*
* Right endpoint is within rei, adjust it so we have:
*
@@ -276,7 +283,15 @@ static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
return 0;
err:
errno = EEXIST;
- return expr_binary_error(msgs, lei->expr, new->expr,
+ if (new->expr->etype == EXPR_MAPPING) {
+ new_expr = new->expr->left;
+ expr = ei->expr->left;
+ } else {
+ new_expr = new->expr;
+ expr = ei->expr;
+ }
+
+ return expr_binary_error(msgs, new_expr, expr,
"conflicting intervals specified");
}
@@ -679,67 +694,32 @@ static struct expr *get_set_interval_find(const struct table *table,
{
struct expr *range = NULL;
struct set *set;
- mpz_t low, high;
struct expr *i;
+ mpz_t val;
set = set_lookup(table, set_name);
- mpz_init2(low, set->key->len);
- mpz_init2(high, set->key->len);
+ mpz_init2(val, set->key->len);
list_for_each_entry(i, &set->init->expressions, list) {
switch (i->key->etype) {
+ case EXPR_PREFIX:
case EXPR_RANGE:
- range_expr_value_low(low, i);
- range_expr_value_high(high, i);
- if (mpz_cmp(left->key->value, low) >= 0 &&
- mpz_cmp(right->key->value, high) <= 0) {
- range = range_expr_alloc(&internal_location,
- expr_clone(left->key),
- expr_clone(right->key));
- goto out;
- }
- break;
- default:
- break;
- }
- }
-out:
- mpz_clear(low);
- mpz_clear(high);
-
- return range;
-}
-
-static struct expr *get_set_interval_end(const struct table *table,
- const char *set_name,
- struct expr *left)
-{
- struct expr *i, *range = NULL;
- struct set *set;
- mpz_t low, high;
+ range_expr_value_low(val, i);
+ if (left && mpz_cmp(left->key->value, val))
+ break;
- set = set_lookup(table, set_name);
- mpz_init2(low, set->key->len);
- mpz_init2(high, set->key->len);
+ range_expr_value_high(val, i);
+ if (right && mpz_cmp(right->key->value, val))
+ break;
- list_for_each_entry(i, &set->init->expressions, list) {
- switch (i->key->etype) {
- case EXPR_RANGE:
- range_expr_value_low(low, i);
- if (mpz_cmp(low, left->key->value) == 0) {
- range = range_expr_alloc(&internal_location,
- expr_clone(left->key),
- expr_clone(i->key->right));
- goto out;
- }
- break;
+ range = expr_clone(i->key);
+ goto out;
default:
break;
}
}
out:
- mpz_clear(low);
- mpz_clear(high);
+ mpz_clear(val);
return range;
}
@@ -764,14 +744,16 @@ int get_set_decompose(struct table *table, struct set *set)
errno = ENOENT;
return -1;
}
+ expr_free(left);
+ expr_free(i);
compound_expr_add(new_init, range);
left = NULL;
} else {
if (left) {
- range = get_set_interval_end(table,
- set->handle.set.name,
- left);
+ range = get_set_interval_find(table,
+ set->handle.set.name,
+ left, NULL);
if (range)
compound_expr_add(new_init, range);
else
@@ -782,7 +764,8 @@ int get_set_decompose(struct table *table, struct set *set)
}
}
if (left) {
- range = get_set_interval_end(table, set->handle.set.name, left);
+ range = get_set_interval_find(table, set->handle.set.name,
+ left, NULL);
if (range)
compound_expr_add(new_init, range);
else
@@ -1084,7 +1067,7 @@ void interval_map_decompose(struct expr *set)
prefix->comment = xstrdup(low->comment);
if (low->timeout)
prefix->timeout = low->timeout;
- if (low->left->expiration)
+ if (low->expiration)
prefix->expiration = low->expiration;
}
diff --git a/src/statement.c b/src/statement.c
index be35bcef..21a1bc8d 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -607,6 +607,13 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
break;
}
+ if (stmt->nat.type_flags & STMT_NAT_F_CONCAT)
+ nft_print(octx, " addr . port");
+ else if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
+ nft_print(octx, " prefix");
+ else if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL)
+ nft_print(octx, " interval");
+
nft_print(octx, " to");
}
diff --git a/src/xt.c b/src/xt.c
index b0f5a30c..f39acf30 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -238,7 +238,7 @@ void netlink_parse_match(struct netlink_parse_ctx *ctx,
stmt->xt.name = strdup(name);
stmt->xt.type = NFT_XT_MATCH;
#endif
- list_add_tail(&stmt->list, &ctx->rule->stmts);
+ rule_stmt_append(ctx->rule, stmt);
}
void netlink_parse_target(struct netlink_parse_ctx *ctx,
@@ -283,7 +283,7 @@ void netlink_parse_target(struct netlink_parse_ctx *ctx,
stmt->xt.name = strdup(name);
stmt->xt.type = NFT_XT_TARGET;
#endif
- list_add_tail(&stmt->list, &ctx->rule->stmts);
+ rule_stmt_append(ctx->rule, stmt);
}
#ifdef HAVE_LIBXTABLES