summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--include/expression.h2
-rw-r--r--include/mnl.h9
-rw-r--r--include/parser.h4
-rw-r--r--include/rule.h16
-rw-r--r--src/cmd.c2
-rw-r--r--src/evaluate.c13
-rw-r--r--src/intervals.c76
-rw-r--r--src/json.c7
-rw-r--r--src/libnftables.c19
-rw-r--r--src/mergesort.c2
-rw-r--r--src/mnl.c28
-rw-r--r--src/netlink_delinearize.c3
-rw-r--r--src/optimize.c205
-rw-r--r--src/parser_bison.y35
-rw-r--r--src/rule.c105
-rw-r--r--src/scanner.l29
-rw-r--r--src/segtree.c2
-rw-r--r--tests/py/inet/ipsec.t2
-rw-r--r--tests/py/inet/ipsec.t.json21
-rw-r--r--tests/py/inet/ipsec.t.payload6
-rwxr-xr-xtests/shell/run-tests.sh1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_reject.nft13
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmaps.nft7
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_merge.nft23
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_non_eq.nft6
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.nft7
-rwxr-xr-xtests/shell/testcases/optimizations/merge_reject26
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts6
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmaps5
-rwxr-xr-xtests/shell/testcases/optimizations/ruleset168
-rwxr-xr-xtests/shell/testcases/optimizations/skip_merge34
-rwxr-xr-xtests/shell/testcases/optimizations/skip_non_eq12
-rwxr-xr-xtests/shell/testcases/optimizations/skip_unsupported14
-rwxr-xr-xtests/shell/testcases/optimizations/variables15
-rwxr-xr-xtests/shell/testcases/parsing/log10
-rwxr-xr-xtests/shell/testcases/sets/automerge_064
-rw-r--r--tests/shell/testcases/sets/dumps/set_eval_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/sets_with_ifnames.nft11
-rwxr-xr-xtests/shell/testcases/sets/errors_041
-rwxr-xr-xtests/shell/testcases/sets/exact_overlap_022
-rwxr-xr-xtests/shell/testcases/sets/set_eval_017
-rwxr-xr-xtests/shell/testcases/sets/sets_with_ifnames1
43 files changed, 949 insertions, 157 deletions
diff --git a/configure.ac b/configure.ac
index e3c980a7..aed479c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([nftables], [1.0.2], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Lester Gooch"], [Release name])
+AC_INIT([nftables], [1.0.4], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Lester Gooch #3"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
@@ -55,7 +55,7 @@ AS_IF([test "x$enable_man_doc" = "xyes"], [
])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.4])
-PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.1])
+PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.2])
AC_ARG_WITH([mini-gmp], [AS_HELP_STRING([--with-mini-gmp],
[Use builtin mini-gmp (for embedded builds)])],
diff --git a/include/expression.h b/include/expression.h
index 2c3818e8..cf7319b6 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -243,6 +243,7 @@ struct expr {
enum expr_types etype:8;
enum ops op:8;
unsigned int len;
+ struct cmd *cmd;
union {
struct {
@@ -480,6 +481,7 @@ extern struct expr *compound_expr_alloc(const struct location *loc,
extern void compound_expr_add(struct expr *compound, struct expr *expr);
extern void compound_expr_remove(struct expr *compound, struct expr *expr);
extern void list_expr_sort(struct list_head *head);
+extern void list_splice_sorted(struct list_head *list, struct list_head *head);
extern struct expr *concat_expr_alloc(const struct location *loc);
diff --git a/include/mnl.h b/include/mnl.h
index 4c701d4e..8e0a7e3f 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -60,10 +60,11 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd);
struct nftnl_set_list *mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
const char *table, const char *set);
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags);
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct handle *h,
- const struct expr *init);
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags);
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init);
int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd);
int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls);
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
diff --git a/include/parser.h b/include/parser.h
index f32154cc..2fb037cb 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -26,6 +26,7 @@ struct parser_state {
unsigned int flex_state_pop;
unsigned int startcond_type;
struct list_head *cmds;
+ unsigned int *startcond_active;
};
enum startcond_type {
@@ -35,7 +36,6 @@ enum startcond_type {
PARSER_SC_CT,
PARSER_SC_COUNTER,
PARSER_SC_ETH,
- PARSER_SC_FLAGS,
PARSER_SC_ICMP,
PARSER_SC_IGMP,
PARSER_SC_IP,
@@ -82,6 +82,8 @@ enum startcond_type {
PARSER_SC_STMT_REJECT,
PARSER_SC_STMT_SYNPROXY,
PARSER_SC_STMT_TPROXY,
+
+ __SC_MAX
};
struct mnl_socket;
diff --git a/include/rule.h b/include/rule.h
index e232b97a..ad9f9127 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -681,6 +681,11 @@ void monitor_free(struct monitor *m);
#define NFT_NLATTR_LOC_MAX 32
+struct nlerr_loc {
+ uint16_t offset;
+ const struct location *location;
+};
+
/**
* struct cmd - command statement
*
@@ -700,6 +705,7 @@ struct cmd {
enum cmd_obj obj;
struct handle handle;
uint32_t seqnum;
+ struct list_head collapse_list;
union {
void *data;
struct expr *expr;
@@ -716,11 +722,9 @@ struct cmd {
struct markup *markup;
struct obj *object;
};
- struct {
- uint16_t offset;
- const struct location *location;
- } attr[NFT_NLATTR_LOC_MAX];
- int num_attrs;
+ struct nlerr_loc *attr;
+ uint32_t attr_array_len;
+ uint32_t num_attrs;
const void *arg;
};
@@ -728,6 +732,8 @@ extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
const struct handle *h, const struct location *loc,
void *data);
extern void nft_cmd_expand(struct cmd *cmd);
+extern bool nft_cmd_collapse(struct list_head *cmds);
+extern void nft_cmd_uncollapse(struct list_head *cmds);
extern struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type,
const struct handle *h,
const struct location *loc, struct obj *obj);
diff --git a/src/cmd.c b/src/cmd.c
index f6a8aa11..63692422 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -237,7 +237,7 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
const struct location *loc = NULL;
- int i;
+ uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
if (!cmd->attr[i].offset)
diff --git a/src/evaluate.c b/src/evaluate.c
index 1447a4c2..073bf871 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3996,6 +3996,9 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
+ if (set->key == NULL)
+ return -1;
+
set->existing_set = set;
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
@@ -4005,8 +4008,9 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
cmd->elem.set = set_get(set);
if (set_is_interval(ctx->set->flags) &&
- !(set->flags & NFT_SET_CONCAT))
- return interval_set_eval(ctx, ctx->set, cmd->expr);
+ !(set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
+ return -1;
ctx->set = NULL;
@@ -4184,8 +4188,9 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
}
if (set_is_interval(ctx->set->flags) &&
- !(ctx->set->flags & NFT_SET_CONCAT))
- return interval_set_eval(ctx, ctx->set, set->init);
+ !(ctx->set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, set->init) < 0)
+ return -1;
ctx->set = NULL;
diff --git a/src/intervals.c b/src/intervals.c
index e61adc76..13009ca1 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -118,6 +118,26 @@ static bool merge_ranges(struct set_automerge_ctx *ctx,
return false;
}
+static void set_sort_splice(struct expr *init, struct set *set)
+{
+ struct set *existing_set = set->existing_set;
+
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+
+ if (!existing_set)
+ return;
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ list_splice_sorted(&existing_set->init->expressions,
+ &init->expressions);
+ init_list_head(&existing_set->init->expressions);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+}
+
static void setelem_automerge(struct set_automerge_ctx *ctx)
{
struct expr *i, *next, *prev = NULL;
@@ -216,21 +236,13 @@ int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
struct cmd *purge_cmd;
struct handle h = {};
- if (existing_set) {
- if (existing_set->init) {
- list_splice_init(&existing_set->init->expressions,
- &init->expressions);
- } else {
- existing_set->init = set_expr_alloc(&internal_location,
- set);
- }
+ if (set->flags & NFT_SET_MAP) {
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+ return 0;
}
- set_to_range(init);
- list_expr_sort(&init->expressions);
-
- if (set->flags & NFT_SET_MAP)
- return 0;
+ set_sort_splice(init, set);
ctx.purge = set_expr_alloc(&internal_location, set);
@@ -409,6 +421,10 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
+ } else if (i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
}
prev = NULL;
}
@@ -463,7 +479,11 @@ int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
if (set->automerge)
automerge_delete(msgs, set, init, debug_mask);
- set_to_range(existing_set->init);
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
list_splice_init(&init->expressions, &del_list);
@@ -486,14 +506,14 @@ int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
if (debug_mask & NFT_DEBUG_SEGTREE) {
list_for_each_entry(i, &init->expressions, list)
- gmp_printf("remove: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ pr_gmp_debug("remove: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
list_for_each_entry(i, &add->expressions, list)
- gmp_printf("add: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
list_for_each_entry(i, &existing_set->init->expressions, list)
- gmp_printf("existing: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ pr_gmp_debug("existing: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
}
if (list_empty(&add->expressions)) {
@@ -540,8 +560,7 @@ static int setelem_overlap(struct list_head *msgs, struct set *set,
}
if (mpz_cmp(prev_range.low, range.low) == 0 &&
- mpz_cmp(prev_range.high, range.high) == 0 &&
- (elem->flags & EXPR_F_KERNEL || prev->flags & EXPR_F_KERNEL))
+ mpz_cmp(prev_range.high, range.high) == 0)
goto next;
if (mpz_cmp(prev_range.low, range.low) <= 0 &&
@@ -589,18 +608,7 @@ int set_overlap(struct list_head *msgs, struct set *set, struct expr *init)
struct expr *i, *n, *clone;
int err;
- if (existing_set) {
- if (existing_set->init) {
- list_splice_init(&existing_set->init->expressions,
- &init->expressions);
- } else {
- existing_set->init = set_expr_alloc(&internal_location,
- set);
- }
- }
-
- set_to_range(init);
- list_expr_sort(&init->expressions);
+ set_sort_splice(init, set);
err = setelem_overlap(msgs, set, init);
diff --git a/src/json.c b/src/json.c
index 0b7224c2..a525fd1b 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1587,7 +1587,7 @@ json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array(), *rules = json_array(), *tmp;
struct flowtable *flowtable;
struct chain *chain;
struct rule *rule;
@@ -1617,10 +1617,13 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
- json_array_append_new(root, tmp);
+ json_array_append_new(rules, tmp);
}
}
+ json_array_extend(root, rules);
+ json_decref(rules);
+
return root;
}
diff --git a/src/libnftables.c b/src/libnftables.c
index 6a22ea09..f2a1ef04 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -501,7 +501,9 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
{
struct nft_cache_filter *filter;
struct cmd *cmd, *next;
+ bool collapsed = false;
unsigned int flags;
+ int err = 0;
filter = nft_cache_filter_init();
flags = nft_cache_evaluate(nft, cmds, filter);
@@ -512,17 +514,26 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
nft_cache_filter_fini(filter);
+ if (nft_cmd_collapse(cmds))
+ collapsed = true;
+
list_for_each_entry_safe(cmd, next, cmds, list) {
struct eval_ctx ectx = {
.nft = nft,
.msgs = msgs,
};
+
if (cmd_evaluate(&ectx, cmd) < 0 &&
- ++nft->state->nerrs == nft->parser_max_errors)
- return -1;
+ ++nft->state->nerrs == nft->parser_max_errors) {
+ err = -1;
+ break;
+ }
}
- if (nft->state->nerrs)
+ if (collapsed)
+ nft_cmd_uncollapse(cmds);
+
+ if (err < 0 || nft->state->nerrs)
return -1;
list_for_each_entry(cmd, cmds, list) {
@@ -697,6 +708,8 @@ err:
if (rc)
nft_cache_release(&nft->cache);
+ scope_release(nft->state->scopes[0]);
+
return rc;
}
diff --git a/src/mergesort.c b/src/mergesort.c
index 8e6aac5f..dca71422 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -70,7 +70,7 @@ static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
return ret;
}
-static void list_splice_sorted(struct list_head *list, struct list_head *head)
+void list_splice_sorted(struct list_head *list, struct list_head *head)
{
struct list_head *h = head->next;
struct list_head *l = list->next;
diff --git a/src/mnl.c b/src/mnl.c
index 7dd77be1..e87b0338 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1609,9 +1609,9 @@ static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
fprintf(fp, "\n");
}
-static int mnl_nft_setelem_batch(const struct nftnl_set *nls,
+static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
- enum nf_tables_msg_types cmd,
+ enum nf_tables_msg_types msg_type,
unsigned int flags, uint32_t seqnum,
const struct expr *set,
struct netlink_ctx *ctx)
@@ -1622,14 +1622,14 @@ static int mnl_nft_setelem_batch(const struct nftnl_set *nls,
struct expr *expr = NULL;
int i = 0;
- if (cmd == NFT_MSG_NEWSETELEM)
+ if (msg_type == NFT_MSG_NEWSETELEM)
flags |= NLM_F_CREATE;
if (set)
expr = list_first_entry(&set->expressions, struct expr, list);
next:
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), cmd,
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type,
nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
flags, seqnum);
@@ -1653,7 +1653,12 @@ next:
nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
list_for_each_entry_from(expr, &set->expressions, list) {
nlse = alloc_nftnl_setelem(set, expr);
- nest2 = nftnl_set_elem_nlmsg_build(nlh, nlse, ++i);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location);
+ nest2 = mnl_attr_nest_start(nlh, ++i);
+ nftnl_set_elem_nlmsg_build_payload(nlh, nlse);
+ mnl_attr_nest_end(nlh, nest2);
+
netlink_dump_setelem(nlse, ctx);
nftnl_set_elem_free(nlse);
if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
@@ -1669,8 +1674,9 @@ next:
return 0;
}
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags)
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags)
{
const struct handle *h = &set->handle;
struct nftnl_set *nls;
@@ -1691,7 +1697,7 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_NEWSETELEM,
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
flags, ctx->seqnum, expr, ctx);
nftnl_set_free(nls);
@@ -1728,8 +1734,8 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
return 0;
}
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct handle *h,
- const struct expr *init)
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init)
{
struct nftnl_set *nls;
int err;
@@ -1747,7 +1753,7 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct handle *h,
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_DELSETELEM, 0,
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_DELSETELEM, 0,
ctx->seqnum, init, ctx);
nftnl_set_free(nls);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index a1b00dee..3bdd98d4 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -100,7 +100,7 @@ static void netlink_release_registers(struct netlink_parse_ctx *ctx)
{
int i;
- for (i = 0; i < MAX_REGS; i++)
+ for (i = 0; i <= MAX_REGS; i++)
expr_free(ctx->registers[i]);
}
@@ -200,6 +200,7 @@ static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
len -= netlink_padded_len(expr->len);
reg += netlink_register_space(expr->len);
+ expr_free(expr);
}
return concat;
diff --git a/src/optimize.c b/src/optimize.c
index d6dfffec..2340ef46 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -81,6 +81,46 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
if (expr_a->socket.level != expr_b->socket.level)
return false;
break;
+ case EXPR_OSF:
+ if (expr_a->osf.ttl != expr_b->osf.ttl)
+ return false;
+ if (expr_a->osf.flags != expr_b->osf.flags)
+ return false;
+ break;
+ case EXPR_XFRM:
+ if (expr_a->xfrm.key != expr_b->xfrm.key)
+ return false;
+ if (expr_a->xfrm.direction != expr_b->xfrm.direction)
+ return false;
+ break;
+ case EXPR_FIB:
+ if (expr_a->fib.flags != expr_b->fib.flags)
+ return false;
+ if (expr_a->fib.result != expr_b->fib.result)
+ return false;
+ break;
+ case EXPR_NUMGEN:
+ if (expr_a->numgen.type != expr_b->numgen.type)
+ return false;
+ if (expr_a->numgen.mod != expr_b->numgen.mod)
+ return false;
+ if (expr_a->numgen.offset != expr_b->numgen.offset)
+ return false;
+ break;
+ case EXPR_HASH:
+ if (expr_a->hash.mod != expr_b->hash.mod)
+ return false;
+ if (expr_a->hash.seed_set != expr_b->hash.seed_set)
+ return false;
+ if (expr_a->hash.seed != expr_b->hash.seed)
+ return false;
+ if (expr_a->hash.offset != expr_b->hash.offset)
+ return false;
+ if (expr_a->hash.type != expr_b->hash.type)
+ return false;
+ break;
+ case EXPR_BINOP:
+ return __expr_cmp(expr_a->left, expr_b->left);
default:
return false;
}
@@ -105,7 +145,14 @@ static bool stmt_expr_supported(const struct expr *expr)
return false;
}
-static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+static bool expr_symbol_set(const struct expr *expr)
+{
+ return expr->right->etype == EXPR_SYMBOL &&
+ expr->right->symtype == SYMBOL_SET;
+}
+
+static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
+ bool fully_compare)
{
struct expr *expr_a, *expr_b;
@@ -117,15 +164,29 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
expr_a = stmt_a->expr;
expr_b = stmt_b->expr;
- if (!stmt_expr_supported(expr_a) ||
- !stmt_expr_supported(expr_b))
+ if (expr_a->op != expr_b->op)
+ return false;
+ if (expr_a->op != OP_IMPLICIT && expr_a->op != OP_EQ)
return false;
+ if (fully_compare) {
+ if (!stmt_expr_supported(expr_a) ||
+ !stmt_expr_supported(expr_b))
+ return false;
+
+ if (expr_symbol_set(expr_a) ||
+ expr_symbol_set(expr_b))
+ return false;
+ }
+
return __expr_cmp(expr_a->left, expr_b->left);
case STMT_COUNTER:
case STMT_NOTRACK:
break;
case STMT_VERDICT:
+ if (!fully_compare)
+ break;
+
expr_a = stmt_a->expr;
expr_b = stmt_b->expr;
@@ -136,14 +197,6 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
expr_b->etype == EXPR_MAP)
return __expr_cmp(expr_a->map, expr_b->map);
break;
- case STMT_LIMIT:
- if (stmt_a->limit.rate != stmt_b->limit.rate ||
- stmt_a->limit.unit != stmt_b->limit.unit ||
- stmt_a->limit.burst != stmt_b->limit.burst ||
- stmt_a->limit.type != stmt_b->limit.type ||
- stmt_a->limit.flags != stmt_b->limit.flags)
- return false;
- break;
case STMT_LOG:
if (stmt_a->log.snaplen != stmt_b->log.snaplen ||
stmt_a->log.group != stmt_b->log.group ||
@@ -165,13 +218,19 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
return false;
break;
case STMT_REJECT:
- if (stmt_a->reject.expr || stmt_b->reject.expr)
- return false;
-
if (stmt_a->reject.family != stmt_b->reject.family ||
stmt_a->reject.type != stmt_b->reject.type ||
stmt_a->reject.icmp_code != stmt_b->reject.icmp_code)
return false;
+
+ if (!!stmt_a->reject.expr ^ !!stmt_b->reject.expr)
+ return false;
+
+ if (!stmt_a->reject.expr)
+ return true;
+
+ if (__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr))
+ return false;
break;
case STMT_NAT:
if (stmt_a->nat.type != stmt_b->nat.type ||
@@ -237,30 +296,43 @@ static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b
return false;
}
-static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
-{
- if (!stmt_a && !stmt_b)
- return true;
- else if (!stmt_a)
- return false;
- else if (!stmt_b)
- return false;
-
- return __stmt_type_eq(stmt_a, stmt_b);
-}
-
static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
{
+ bool unsupported_exists = false;
uint32_t i;
for (i = 0; i < ctx->num_stmts; i++) {
- if (__stmt_type_eq(stmt, ctx->stmt[i]))
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ unsupported_exists = true;
+
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return true;
+ }
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ case STMT_VERDICT:
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ case STMT_LOG:
+ case STMT_NAT:
+ case STMT_REJECT:
+ break;
+ default:
+ /* add unsupported statement only once to statement matrix. */
+ if (unsupported_exists)
return true;
+ break;
}
return false;
}
+static struct stmt_ops unsupported_stmt_ops = {
+ .type = STMT_INVALID,
+ .name = "unsupported",
+};
+
static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
{
struct stmt *stmt, *clone;
@@ -269,25 +341,23 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
if (stmt_type_find(ctx, stmt))
continue;
- if (stmt->ops->type == STMT_VERDICT &&
- stmt->expr->etype == EXPR_MAP)
- continue;
-
/* No refcounter available in statement objects, clone it to
* to store in the array of selectors.
*/
clone = stmt_alloc(&internal_location, stmt->ops);
switch (stmt->ops->type) {
case STMT_EXPRESSION:
+ if (stmt->expr->op != OP_IMPLICIT &&
+ stmt->expr->op != OP_EQ) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
case STMT_VERDICT:
clone->expr = expr_get(stmt->expr);
break;
case STMT_COUNTER:
case STMT_NOTRACK:
break;
- case STMT_LIMIT:
- memcpy(&clone->limit, &stmt->limit, sizeof(clone->limit));
- break;
case STMT_LOG:
memcpy(&clone->log, &stmt->log, sizeof(clone->log));
if (stmt->log.prefix)
@@ -303,9 +373,16 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
clone->nat.flags = stmt->nat.flags;
clone->nat.type_flags = stmt->nat.type_flags;
break;
+ case STMT_REJECT:
+ if (stmt->reject.expr)
+ clone->reject.expr = expr_get(stmt->reject.expr);
+ clone->reject.type = stmt->reject.type;
+ clone->reject.icmp_code = stmt->reject.icmp_code;
+ clone->reject.family = stmt->reject.family;
+ break;
default:
- stmt_free(clone);
- continue;
+ clone->ops = &unsupported_stmt_ops;
+ break;
}
ctx->stmt[ctx->num_stmts++] = clone;
@@ -316,18 +393,34 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
return 0;
}
+static int unsupported_in_stmt_matrix(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ return i;
+ }
+ /* this should not happen. */
+ return -1;
+}
+
static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *stmt)
{
uint32_t i;
for (i = 0; i < ctx->num_stmts; i++) {
- if (__stmt_type_eq(stmt, ctx->stmt[i]))
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
return i;
}
- /* should not ever happen. */
- return 0;
+
+ return -1;
}
+static struct stmt unsupported_stmt = {
+ .ops = &unsupported_stmt_ops,
+};
+
static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
struct rule *rule, uint32_t *i)
{
@@ -336,6 +429,12 @@ static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
list_for_each_entry(stmt, &rule->stmts, list) {
k = cmd_stmt_find_in_stmt_matrix(ctx, stmt);
+ if (k < 0) {
+ k = unsupported_in_stmt_matrix(ctx);
+ assert(k >= 0);
+ ctx->stmt_matrix[*i][k] = &unsupported_stmt;
+ continue;
+ }
ctx->stmt_matrix[*i][k] = stmt;
}
ctx->rule[(*i)++] = rule;
@@ -833,7 +932,8 @@ static enum stmt_types merge_stmt_type(const struct optimize_ctx *ctx)
}
}
- return STMT_INVALID;
+ /* actually no verdict, this assumes rules have the same verdict. */
+ return STMT_VERDICT;
}
static void merge_rules(const struct optimize_ctx *ctx,
@@ -872,6 +972,13 @@ static void merge_rules(const struct optimize_ctx *ctx,
assert(0);
}
+ if (ctx->rule[from]->comment) {
+ xfree(ctx->rule[from]->comment);
+ ctx->rule[from]->comment = NULL;
+ }
+
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
fprintf(octx->error_fp, "Merging:\n");
rule_optimize_print(octx, ctx->rule[from]);
@@ -884,6 +991,20 @@ static void merge_rules(const struct optimize_ctx *ctx,
fprintf(octx->error_fp, "into:\n\t");
rule_print(ctx->rule[from], octx);
fprintf(octx->error_fp, "\n");
+
+ octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+}
+
+static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ if (!stmt_a && !stmt_b)
+ return true;
+ else if (!stmt_a)
+ return false;
+ else if (!stmt_b)
+ return false;
+
+ return __stmt_type_eq(stmt_a, stmt_b, true);
}
static bool rules_eq(const struct optimize_ctx *ctx, int i, int j)
@@ -964,6 +1085,10 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
case STMT_EXPRESSION:
merge[k].stmt[merge[k].num_stmts++] = m;
break;
+ case STMT_VERDICT:
+ if (ctx->stmt_matrix[i][m]->expr->etype == EXPR_MAP)
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
default:
break;
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index ca5c488c..ae14eb1a 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -942,7 +942,6 @@ close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); }
close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); };
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
-close_scope_flags : { scanner_pop_start_cond(nft->scanner, PARSER_SC_FLAGS); };
close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
@@ -1679,7 +1678,7 @@ table_block_alloc : /* empty */
}
;
-table_options : FLAGS STRING close_scope_flags
+table_options : FLAGS STRING
{
if (strcmp($2, "dormant") == 0) {
$<table>0->flags |= TABLE_F_DORMANT;
@@ -1946,7 +1945,7 @@ set_block : /* empty */ { $$ = $<set>-1; }
datatype_set($1->key, $3->dtype);
$$ = $1;
}
- | set_block FLAGS set_flag_list stmt_separator close_scope_flags
+ | set_block FLAGS set_flag_list stmt_separator
{
$1->flags = $3;
$$ = $1;
@@ -2016,7 +2015,7 @@ map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
| LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
| SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; }
- | SYNPROXY { $$ = NFT_OBJECT_SYNPROXY; }
+ | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
;
map_block : /* empty */ { $$ = $<set>-1; }
@@ -2080,7 +2079,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->flags |= NFT_SET_OBJECT;
$$ = $1;
}
- | map_block FLAGS set_flag_list stmt_separator close_scope_flags
+ | map_block FLAGS set_flag_list stmt_separator
{
$1->flags |= $3;
$$ = $1;
@@ -2153,7 +2152,7 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
{
$$->flags |= NFT_FLOWTABLE_COUNTER;
}
- | flowtable_block FLAGS OFFLOAD stmt_separator close_scope_flags
+ | flowtable_block FLAGS OFFLOAD stmt_separator
{
$$->flags |= FLOWTABLE_F_HW_OFFLOAD;
}
@@ -2520,7 +2519,7 @@ dev_spec : DEVICE string
| /* empty */ { $$ = NULL; }
;
-flags_spec : FLAGS OFFLOAD close_scope_flags
+flags_spec : FLAGS OFFLOAD
{
$<chain>0->flags |= CHAIN_F_HW_OFFLOAD;
}
@@ -2915,7 +2914,7 @@ verdict_map_list_expr : verdict_map_list_member_expr
verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -3126,7 +3125,7 @@ log_arg : PREFIX string
$<stmt>0->log.level = $2;
$<stmt>0->log.flags |= STMT_LOG_LEVEL;
}
- | FLAGS log_flags close_scope_flags
+ | FLAGS log_flags
{
$<stmt>0->log.logflags |= $2;
}
@@ -3828,13 +3827,13 @@ queue_stmt : queue_stmt_compat close_scope_queue
{
$$ = queue_stmt_alloc(&@$, $3, 0);
}
- | QUEUE FLAGS queue_stmt_flags close_scope_flags TO queue_stmt_expr close_scope_queue
+ | QUEUE FLAGS queue_stmt_flags TO queue_stmt_expr close_scope_queue
{
- $$ = queue_stmt_alloc(&@$, $6, $3);
+ $$ = queue_stmt_alloc(&@$, $5, $3);
}
- | QUEUE FLAGS queue_stmt_flags close_scope_flags QUEUENUM queue_stmt_expr_simple close_scope_queue
+ | QUEUE FLAGS queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue
{
- $$ = queue_stmt_alloc(&@$, $6, $3);
+ $$ = queue_stmt_alloc(&@$, $5, $3);
}
;
@@ -4263,7 +4262,7 @@ set_list_member_expr : opt_newline set_expr opt_newline
}
| opt_newline set_elem_expr COLON set_rhs_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -5501,7 +5500,7 @@ comp_hdr_expr : COMP comp_hdr_field close_scope_comp
;
comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; }
- | FLAGS close_scope_flags { $$ = COMPHDR_FLAGS; }
+ | FLAGS { $$ = COMPHDR_FLAGS; }
| CPI { $$ = COMPHDR_CPI; }
;
@@ -5562,7 +5561,7 @@ tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
| ACKSEQ { $$ = TCPHDR_ACKSEQ; }
| DOFF { $$ = TCPHDR_DOFF; }
| RESERVED { $$ = TCPHDR_RESERVED; }
- | FLAGS close_scope_flags { $$ = TCPHDR_FLAGS; }
+ | FLAGS { $$ = TCPHDR_FLAGS; }
| WINDOW { $$ = TCPHDR_WINDOW; }
| CHECKSUM { $$ = TCPHDR_CHECKSUM; }
| URGPTR { $$ = TCPHDR_URGPTR; }
@@ -5676,7 +5675,7 @@ sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
;
sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; }
- | FLAGS close_scope_flags { $$ = SCTP_CHUNK_COMMON_FLAGS; }
+ | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
| LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
;
@@ -5844,7 +5843,7 @@ rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt
;
rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; }
- | FLAGS close_scope_flags { $$ = RT4HDR_FLAGS; }
+ | FLAGS { $$ = RT4HDR_FLAGS; }
| TAG { $$ = RT4HDR_TAG; }
| SID '[' NUM ']'
{
diff --git a/src/rule.c b/src/rule.c
index 799092eb..79d4b777 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1279,13 +1279,20 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->handle = *h;
cmd->location = *loc;
cmd->data = data;
+ cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX,
+ sizeof(struct nlerr_loc));
+ cmd->attr_array_len = NFT_NLATTR_LOC_MAX;
+ init_list_head(&cmd->collapse_list);
+
return cmd;
}
void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
{
- if (cmd->num_attrs >= NFT_NLATTR_LOC_MAX)
- return;
+ if (cmd->num_attrs >= cmd->attr_array_len) {
+ cmd->attr_array_len *= 2;
+ cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
+ }
cmd->attr[cmd->num_attrs].offset = offset;
cmd->attr[cmd->num_attrs].location = loc;
@@ -1379,6 +1386,79 @@ void nft_cmd_expand(struct cmd *cmd)
}
}
+bool nft_cmd_collapse(struct list_head *cmds)
+{
+ struct cmd *cmd, *next, *elems = NULL;
+ struct expr *expr, *enext;
+ bool collapse = false;
+
+ list_for_each_entry_safe(cmd, next, cmds, list) {
+ if (cmd->op != CMD_ADD &&
+ cmd->op != CMD_CREATE) {
+ elems = NULL;
+ continue;
+ }
+
+ if (cmd->obj != CMD_OBJ_ELEMENTS) {
+ elems = NULL;
+ continue;
+ }
+
+ if (!elems) {
+ elems = cmd;
+ continue;
+ }
+
+ if (cmd->op != elems->op) {
+ elems = cmd;
+ continue;
+ }
+
+ if (strcmp(elems->handle.table.name, cmd->handle.table.name) ||
+ strcmp(elems->handle.set.name, cmd->handle.set.name)) {
+ elems = cmd;
+ continue;
+ }
+
+ collapse = true;
+ list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) {
+ expr->cmd = cmd;
+ list_move_tail(&expr->list, &elems->expr->expressions);
+ }
+ elems->expr->size += cmd->expr->size;
+ list_move_tail(&cmd->list, &elems->collapse_list);
+ }
+
+ return collapse;
+}
+
+void nft_cmd_uncollapse(struct list_head *cmds)
+{
+ struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next;
+ struct expr *expr, *next;
+
+ list_for_each_entry_safe(cmd, cmd_next, cmds, list) {
+ if (list_empty(&cmd->collapse_list))
+ continue;
+
+ assert(cmd->obj == CMD_OBJ_ELEMENTS);
+
+ list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) {
+ if (!expr->cmd)
+ continue;
+
+ list_move_tail(&expr->list, &expr->cmd->expr->expressions);
+ cmd->expr->size--;
+ expr->cmd = NULL;
+ }
+
+ list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) {
+ collapse_cmd->elem.set = set_get(cmd->elem.set);
+ list_add(&collapse_cmd->list, &cmd->list);
+ }
+ }
+}
+
struct markup *markup_alloc(uint32_t format)
{
struct markup *markup;
@@ -1462,6 +1542,7 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
+ xfree(cmd->attr);
xfree(cmd->arg);
xfree(cmd);
}
@@ -1469,11 +1550,11 @@ void cmd_free(struct cmd *cmd)
#include <netlink.h>
#include <mnl.h>
-static int __do_add_elements(struct netlink_ctx *ctx, struct set *set,
- struct expr *expr, uint32_t flags)
+static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct set *set, struct expr *expr, uint32_t flags)
{
expr->set_flags |= set->flags;
- if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0)
+ if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0)
return -1;
return 0;
@@ -1489,7 +1570,7 @@ static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
set_to_intervals(set, init, true) < 0)
return -1;
- return __do_add_elements(ctx, set, init, flags);
+ return __do_add_elements(ctx, cmd, set, init, flags);
}
static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1497,7 +1578,7 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
{
struct set *set = cmd->set;
- return __do_add_elements(ctx, set, set->init, flags);
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
}
static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1591,7 +1672,7 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
set_to_intervals(set, expr, false) < 0)
return -1;
- if (mnl_nft_setelem_del(ctx, &cmd->handle, cmd->elem.expr) < 0)
+ if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0)
return -1;
return 0;
@@ -2309,13 +2390,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
table_print_declaration(table, &ctx->nft->output);
- list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- if (chain->handle.family != cmd->handle.family ||
- strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0)
- continue;
-
+ chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (chain)
chain_print(chain, &ctx->nft->output);
- }
nft_print(&ctx->nft->output, "}\n");
diff --git a/src/scanner.l b/src/scanner.l
index 2154281e..5741261a 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -201,7 +201,6 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
-%s SCANSTATE_FLAGS
%s SCANSTATE_ICMP
%s SCANSTATE_IGMP
%s SCANSTATE_IP
@@ -339,7 +338,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"jump" { return JUMP; }
"goto" { return GOTO; }
"return" { return RETURN; }
-<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_FLAGS,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_FLAGS and SCANSTATE_IP here are workarounds */
+<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */
"inet" { return INET; }
"netdev" { return NETDEV; }
@@ -363,14 +362,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"index" { return INDEX; }
"comment" { return COMMENT; }
-<SCANSTATE_FLAGS>{
- "constant" { return CONSTANT; }
- "dynamic" { return DYNAMIC; }
-
- /* log flags */
- "all" { return ALL; }
-}
+"constant" { return CONSTANT; }
"interval" { return INTERVAL; }
+"dynamic" { return DYNAMIC; }
"auto-merge" { return AUTOMERGE; }
"timeout" { return TIMEOUT; }
"gc-interval" { return GC_INTERVAL; }
@@ -418,7 +412,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
-<SCANSTATE_FLAGS,SCANSTATE_EXPR_QUEUE>{
+<SCANSTATE_EXPR_QUEUE>{
"num" { return QUEUENUM;}
"bypass" { return BYPASS;}
"fanout" { return FANOUT;}
@@ -612,7 +606,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_EXPR_COMP>{
"cpi" { return CPI; }
}
-"flags" { scanner_push_start_cond(yyscanner, SCANSTATE_FLAGS); return FLAGS; }
+"flags" { return FLAGS; }
"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
@@ -781,6 +775,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"notrack" { return NOTRACK; }
+"all" { return ALL; }
+
<SCANSTATE_CMD_EXPORT,SCANSTATE_CMD_IMPORT,SCANSTATE_CMD_MONITOR>{
"xml" { return XML; }
"json" { return JSON; }
@@ -1148,6 +1144,8 @@ void *scanner_init(struct parser_state *state)
yylex_init_extra(state, &scanner);
yyset_out(NULL, scanner);
+ state->startcond_active = xzalloc_array(__SC_MAX,
+ sizeof(*state->startcond_active));
return scanner;
}
@@ -1177,6 +1175,8 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
+ xfree(state->startcond_active);
+
yylex_destroy(nft->scanner);
}
@@ -1185,6 +1185,7 @@ static void scanner_push_start_cond(void *scanner, enum startcond_type type)
struct parser_state *state = yyget_extra(scanner);
state->startcond_type = type;
+ state->startcond_active[type]++;
yy_push_state((int)type, scanner);
}
@@ -1193,6 +1194,8 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
{
struct parser_state *state = yyget_extra(scanner);
+ state->startcond_active[t]--;
+
if (state->startcond_type != t) {
state->flex_state_pop++;
return; /* Can't pop just yet! */
@@ -1202,6 +1205,10 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
state->flex_state_pop--;
state->startcond_type = yy_top_state(scanner);
yy_pop_state(scanner);
+
+ t = state->startcond_type;
+ if (state->startcond_active[t])
+ return;
}
state->startcond_type = yy_top_state(scanner);
diff --git a/src/segtree.c b/src/segtree.c
index f9cac373..c36497ce 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -575,7 +575,7 @@ void interval_map_decompose(struct expr *set)
if (!mpz_cmp_ui(range, 0)) {
if (expr_basetype(low)->type == TYPE_STRING)
- mpz_switch_byteorder(expr_value(low)->value, low->len / BITS_PER_BYTE);
+ mpz_switch_byteorder(expr_value(low)->value, expr_value(low)->len / BITS_PER_BYTE);
low->flags |= EXPR_F_KERNEL;
compound_expr_add(set, expr_get(low));
} else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
diff --git a/tests/py/inet/ipsec.t b/tests/py/inet/ipsec.t
index e924e9bc..b18df395 100644
--- a/tests/py/inet/ipsec.t
+++ b/tests/py/inet/ipsec.t
@@ -19,3 +19,5 @@ ipsec in ip6 daddr dead::beef;ok
ipsec out ip6 saddr dead::feed;ok
ipsec in spnum 256 reqid 1;fail
+
+counter ipsec out ip daddr 192.168.1.2;ok
diff --git a/tests/py/inet/ipsec.t.json b/tests/py/inet/ipsec.t.json
index d7d3a03c..18a64f35 100644
--- a/tests/py/inet/ipsec.t.json
+++ b/tests/py/inet/ipsec.t.json
@@ -134,3 +134,24 @@
}
}
]
+
+# counter ipsec out ip daddr 192.168.1.2
+[
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "ipsec": {
+ "dir": "out",
+ "family": "ip",
+ "key": "daddr",
+ "spnum": 0
+ }
+ },
+ "op": "==",
+ "right": "192.168.1.2"
+ }
+ }
+]
diff --git a/tests/py/inet/ipsec.t.payload b/tests/py/inet/ipsec.t.payload
index c46a2263..9648255d 100644
--- a/tests/py/inet/ipsec.t.payload
+++ b/tests/py/inet/ipsec.t.payload
@@ -37,3 +37,9 @@ ip ipsec-ip4 ipsec-forw
[ xfrm load out 0 saddr6 => reg 1 ]
[ cmp eq reg 1 0x0000adde 0x00000000 0x00000000 0xedfe0000 ]
+# counter ipsec out ip daddr 192.168.1.2
+ip ipsec-ip4 ipsec-forw
+ [ counter pkts 0 bytes 0 ]
+ [ xfrm load out 0 daddr4 => reg 1 ]
+ [ cmp eq reg 1 0x0201a8c0 ]
+
diff --git a/tests/shell/run-tests.sh b/tests/shell/run-tests.sh
index f77d850e..33006d2c 100755
--- a/tests/shell/run-tests.sh
+++ b/tests/shell/run-tests.sh
@@ -87,6 +87,7 @@ kernel_cleanup() {
nft_fib nft_fib_ipv4 nft_fib_ipv6 nft_fib_inet \
nft_hash nft_ct nft_compat nft_rt nft_objref \
nft_set_hash nft_set_rbtree nft_set_bitmap \
+ nft_synproxy nft_connlimit \
nft_chain_nat \
nft_chain_route_ipv4 nft_chain_route_ipv6 \
nft_dup_netdev nft_fwd_netdev \
diff --git a/tests/shell/testcases/optimizations/dumps/merge_reject.nft b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
new file mode 100644
index 00000000..c29ad6d5
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
@@ -0,0 +1,13 @@
+table ip x {
+ chain y {
+ ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop
+ meta l4proto . ip daddr . tcp dport { tcp . 172.30.238.117 . 8080, tcp . 172.30.33.71 . 3306, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject
+ ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}
+table ip6 x {
+ chain y {
+ meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::3 . 8080, tcp . aaaa::2 . 3306, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject
+ ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
index 05b9e575..c981acf0 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
@@ -1,4 +1,10 @@
table ip x {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
chain filter_in_tcp {
}
@@ -6,6 +12,7 @@ table ip x {
}
chain y {
+ update @s { ip saddr limit rate 12/minute burst 30 packets } accept
tcp dport vmap { 80 : accept, 81 : accept, 443 : accept, 8000-8100 : accept, 24000-25000 : accept }
meta l4proto vmap { tcp : goto filter_in_tcp, udp : goto filter_in_udp }
log
diff --git a/tests/shell/testcases/optimizations/dumps/skip_merge.nft b/tests/shell/testcases/optimizations/dumps/skip_merge.nft
new file mode 100644
index 00000000..9c10b74b
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_merge.nft
@@ -0,0 +1,23 @@
+table inet filter {
+ set udp_accepted {
+ type inet_service
+ elements = { 500, 4500 }
+ }
+
+ set tcp_accepted {
+ type inet_service
+ elements = { 80, 443 }
+ }
+
+ chain udp_input {
+ udp dport 1-128 accept
+ udp dport @udp_accepted accept
+ udp dport 53 accept
+ }
+
+ chain tcp_input {
+ tcp dport { 1-128, 8888-9999 } accept
+ tcp dport @tcp_accepted accept
+ tcp dport 1024-65535 accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft b/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft
new file mode 100644
index 00000000..6df38655
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft
@@ -0,0 +1,6 @@
+table inet x {
+ chain y {
+ iifname "eth0" oifname != "eth0" counter packets 0 bytes 0 accept
+ iifname "eth0" oifname "eth0" counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft b/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft
new file mode 100644
index 00000000..43b6578d
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft
@@ -0,0 +1,7 @@
+table inet x {
+ chain y {
+ ip saddr 1.2.3.4 tcp dport 80 meta mark set 0x0000000a accept
+ ip saddr 1.2.3.4 tcp dport 81 meta mark set 0x0000000b accept
+ ip saddr . tcp dport { 1.2.3.5 . 81, 1.2.3.5 . 82 } accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/merge_reject b/tests/shell/testcases/optimizations/merge_reject
new file mode 100755
index 00000000..c0ef9cac
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_reject
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ meta l4proto tcp ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop
+ meta l4proto tcp ip daddr 172.30.33.71 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.238.117 tcp dport 8080 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.254.251 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
+
+RULESET="table ip6 x {
+ chain y {
+ meta l4proto tcp ip6 daddr aaaa::2 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::3 tcp dport 8080 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::4 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_stmts b/tests/shell/testcases/optimizations/merge_stmts
index 0c35636e..ec7a9dd6 100755
--- a/tests/shell/testcases/optimizations/merge_stmts
+++ b/tests/shell/testcases/optimizations/merge_stmts
@@ -4,9 +4,9 @@ set -e
RULESET="table ip x {
chain y {
- ip daddr 192.168.0.1 counter accept
- ip daddr 192.168.0.2 counter accept
- ip daddr 192.168.0.3 counter accept
+ ip daddr 192.168.0.1 counter accept comment "test1"
+ ip daddr 192.168.0.2 counter accept comment "test2"
+ ip daddr 192.168.0.3 counter accept comment "test3"
}
}"
diff --git a/tests/shell/testcases/optimizations/merge_vmaps b/tests/shell/testcases/optimizations/merge_vmaps
index 0922a221..e2e4be15 100755
--- a/tests/shell/testcases/optimizations/merge_vmaps
+++ b/tests/shell/testcases/optimizations/merge_vmaps
@@ -3,11 +3,16 @@
set -e
RULESET="table ip x {
+ set s {
+ type ipv4_addr
+ flags dynamic
+ }
chain filter_in_tcp {
}
chain filter_in_udp {
}
chain y {
+ update @s { ip saddr limit rate 12/minute burst 30 packets } accept
tcp dport vmap {
80 : accept,
81 : accept,
diff --git a/tests/shell/testcases/optimizations/ruleset b/tests/shell/testcases/optimizations/ruleset
new file mode 100755
index 00000000..ef2652db
--- /dev/null
+++ b/tests/shell/testcases/optimizations/ruleset
@@ -0,0 +1,168 @@
+#!/bin/bash
+
+RULESET="table inet uni {
+ chain gtfo {
+ reject with icmpx type host-unreachable
+ drop
+ }
+
+ chain filter_in_tcp {
+ tcp dport vmap {
+ 80 : accept,
+ 81 : accept,
+ 443 : accept,
+ 931 : accept,
+ 5001 : accept,
+ 5201 : accept,
+ }
+ tcp dport vmap {
+ 6800-6999 : accept,
+ 33434-33499 : accept,
+ }
+
+ drop
+ }
+
+ chain filter_in_udp {
+ udp dport vmap {
+ 53 : accept,
+ 123 : accept,
+ 846 : accept,
+ 849 : accept,
+ 5001 : accept,
+ 5201 : accept,
+ }
+ udp dport vmap {
+ 5300-5399 : accept,
+ 6800-6999 : accept,
+ 33434-33499 : accept,
+ }
+
+ drop
+ }
+
+ chain filter_in {
+ type filter hook input priority 0; policy drop;
+
+ ct state vmap {
+ invalid : drop,
+ established : accept,
+ related : accept,
+ untracked : accept,
+ }
+
+ ct status vmap {
+ dnat : accept,
+ snat : accept,
+ }
+
+ iif lo accept
+
+ meta iifgroup {100-199} accept
+
+ meta l4proto tcp goto filter_in_tcp
+ meta l4proto udp goto filter_in_udp
+
+ icmp type vmap {
+ echo-request : accept,
+ }
+ ip6 nexthdr icmpv6 icmpv6 type vmap {
+ echo-request : accept,
+ }
+ }
+
+ chain filter_fwd_ifgroup {
+ meta iifgroup . oifgroup vmap {
+ 100 . 10 : accept,
+ 100 . 100 : accept,
+ 100 . 101 : accept,
+ 101 . 101 : accept,
+ }
+ goto gtfo
+ }
+
+ chain filter_fwd {
+ type filter hook forward priority 0; policy drop;
+
+ fib daddr type broadcast drop
+
+ ct state vmap {
+ invalid : drop,
+ established : accept,
+ related : accept,
+ untracked : accept,
+ }
+
+ ct status vmap {
+ dnat : accept,
+ snat : accept,
+ }
+
+ meta iifgroup {100-199} goto filter_fwd_ifgroup
+ }
+
+ chain nat_fwd_tun {
+ meta l4proto tcp redirect to :15
+ udp dport 53 redirect to :13
+ goto gtfo
+ }
+
+ chain nat_dns_dnstc { meta l4proto udp redirect to :5300 ; drop ; }
+ chain nat_dns_this_5301 { meta l4proto udp redirect to :5301 ; drop ; }
+ chain nat_dns_moon_5301 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5301 ; drop ; }
+ chain nat_dns_moon_5302 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5302 ; drop ; }
+ chain nat_dns_moon_5303 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5303 ; drop ; }
+
+ chain nat_dns_acme {
+ udp length 47-63 @th,160,128 0x0e373135363130333131303735353203 \
+ goto nat_dns_dnstc
+
+ udp length 62-78 @th,160,128 0x0e31393032383939353831343037320e \
+ goto nat_dns_this_5301
+
+ udp length 62-78 @th,160,128 0x0e31363436323733373931323934300e \
+ goto nat_dns_moon_5301
+
+ udp length 62-78 @th,160,128 0x0e32393535373539353636383732310e \
+ goto nat_dns_moon_5302
+
+ udp length 62-78 @th,160,128 0x0e38353439353637323038363633390e \
+ goto nat_dns_moon_5303
+
+ drop
+ }
+
+ chain nat_prerouting {
+ type nat hook prerouting priority -100; policy accept;
+
+ iifgroup 10 udp dport 53 goto nat_dns_acme
+ iifgroup 10 accept
+
+ ip daddr 198.19.0.0/16 goto nat_fwd_tun
+ ip6 daddr fc00::/8 goto nat_fwd_tun
+
+ tcp dport 53 redirect to :25302
+ udp dport 53 redirect to :25302
+ }
+
+ chain nat_output {
+ type nat hook output priority -100; policy accept;
+
+ ip daddr 198.19.0.0/16 goto nat_fwd_tun
+ ip6 daddr fc00::/8 goto nat_fwd_tun
+ }
+
+ chain nat_postrouting {
+ type nat hook postrouting priority 100; policy accept;
+
+ oif != lo masquerade
+ }
+
+ chain mangle_forward {
+ type filter hook forward priority -150; policy accept;
+
+ tcp flags & (syn | rst) == syn tcp option maxseg size set rt mtu
+ }
+}"
+
+$NFT -o -c -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/skip_merge b/tests/shell/testcases/optimizations/skip_merge
new file mode 100755
index 00000000..8af976ca
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_merge
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet filter {
+ set udp_accepted {
+ type inet_service;
+ elements = {
+ isakmp, ipsec-nat-t
+ }
+ }
+
+ set tcp_accepted {
+ type inet_service;
+ elements = {
+ http, https
+ }
+ }
+
+ chain udp_input {
+ udp dport 1-128 accept
+ udp dport @udp_accepted accept
+ udp dport domain accept
+ }
+
+ chain tcp_input {
+ tcp dport 1-128 accept
+ tcp dport 8888-9999 accept
+ tcp dport @tcp_accepted accept
+ tcp dport 1024-65535 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/skip_non_eq b/tests/shell/testcases/optimizations/skip_non_eq
new file mode 100755
index 00000000..431ed0ad
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_non_eq
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet x {
+ chain y {
+ iifname "eth0" oifname != "eth0" counter packets 0 bytes 0 accept
+ iifname "eth0" oifname "eth0" counter packets 0 bytes 0 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/skip_unsupported b/tests/shell/testcases/optimizations/skip_unsupported
new file mode 100755
index 00000000..9313c302
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_unsupported
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet x {
+ chain y {
+ ip saddr 1.2.3.4 tcp dport 80 meta mark set 10 accept
+ ip saddr 1.2.3.4 tcp dport 81 meta mark set 11 accept
+ ip saddr 1.2.3.5 tcp dport 81 accept comment \"test\"
+ ip saddr 1.2.3.5 tcp dport 82 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/variables b/tests/shell/testcases/optimizations/variables
new file mode 100755
index 00000000..fa986065
--- /dev/null
+++ b/tests/shell/testcases/optimizations/variables
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+RULESET="define addrv4_vpnnet = 10.1.0.0/16
+
+table ip nat {
+ chain postrouting {
+ type nat hook postrouting priority 0; policy accept;
+
+ ip saddr \$addrv4_vpnnet counter masquerade fully-random comment \"masquerade ipv4\"
+ }
+}"
+
+$NFT -c -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/parsing/log b/tests/shell/testcases/parsing/log
new file mode 100755
index 00000000..0b89d589
--- /dev/null
+++ b/tests/shell/testcases/parsing/log
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+$NFT add table t || exit 1
+$NFT add chain t c || exit 1
+$NFT add rule t c 'iif != lo ip daddr 127.0.0.1/8 counter limit rate 1/second log flags all prefix "nft_lo4 " drop' || exit 1
+$NFT add rule t c 'iif != lo ip daddr 127.0.0.1/8 counter limit rate 1/second log flags all level debug drop' || exit 1
+$NFT delete table t || exit 1
+
+exit 0
+
diff --git a/tests/shell/testcases/sets/automerge_0 b/tests/shell/testcases/sets/automerge_0
new file mode 100755
index 00000000..c9fb6095
--- /dev/null
+++ b/tests/shell/testcases/sets/automerge_0
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet x {
+ set y {
+ type inet_service
+ flags interval
+ auto-merge
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+tmpfile=$(mktemp)
+echo -n "add element inet x y { " > $tmpfile
+for ((i=0;i<65535;i+=2))
+do
+ echo -n "$i, " >> $tmpfile
+ if [ $i -eq 65534 ]
+ then
+ echo -n "$i" >> $tmpfile
+ fi
+done
+echo "}" >> $tmpfile
+
+$NFT -f $tmpfile
+
+tmpfile2=$(mktemp)
+for ((i=1;i<65535;i+=2))
+do
+ echo "$i" >> $tmpfile2
+done
+
+tmpfile3=$(mktemp)
+shuf $tmpfile2 > $tmpfile3
+i=0
+cat $tmpfile3 | while read line && [ $i -lt 10 ]
+do
+ $NFT add element inet x y { $line }
+ i=$((i+1))
+done
+
+for ((i=0;i<10;i++))
+do
+ from=$(($RANDOM%65535))
+ to=$(($from+100))
+ $NFT add element inet x y { $from-$to }
+ if [ $? -ne 0 ]
+ then
+ echo "failed to add $from-$to"
+ exit 1
+ fi
+ $NFT get element inet x y { $from-$to }
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $from-$to"
+ exit 1
+ fi
+done
+
+rm -f $tmpfile
+rm -f $tmpfile2
+rm -f $tmpfile3
diff --git a/tests/shell/testcases/sets/dumps/set_eval_0.nft b/tests/shell/testcases/sets/dumps/set_eval_0.nft
new file mode 100644
index 00000000..a45462b8
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/set_eval_0.nft
@@ -0,0 +1,11 @@
+table ip nat {
+ set set_with_interval {
+ type ipv4_addr
+ flags interval
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto { tcp, udp } th dport 443 dnat to 10.0.0.1
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
index 6b073ae2..77a8baf5 100644
--- a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
+++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
@@ -29,11 +29,19 @@ table inet testifsets {
12.2.2.0/24 . "abcdef*" }
}
+ map map_wild {
+ type ifname : verdict
+ flags interval
+ elements = { "abcdef*" : jump do_nothing,
+ "eth0" : jump do_nothing }
+ }
+
chain v4icmp {
iifname @simple counter packets 0 bytes 0
iifname @simple_wild counter packets 0 bytes 0
iifname { "eth0", "abcdef0" } counter packets 0 bytes 0
iifname { "abcdef*", "eth0" } counter packets 0 bytes 0
+ iifname vmap @map_wild
}
chain v4icmpc {
@@ -48,4 +56,7 @@ table inet testifsets {
ip protocol icmp jump v4icmp
ip protocol icmp goto v4icmpc
}
+
+ chain do_nothing {
+ }
}
diff --git a/tests/shell/testcases/sets/errors_0 b/tests/shell/testcases/sets/errors_0
new file mode 100755
index 00000000..569f4ab8
--- /dev/null
+++ b/tests/shell/testcases/sets/errors_0
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ }
+}
+
+delete element ip x y { 2.3.4.5 }"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ }
+}
+
+add element x y { 1.1.1.1/24 }
+delete element x y { 1.1.1.1/24 }
+add element x y { 1.1.1.1/24 }
+delete element x y { 2.2.2.2/24 }"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="flush ruleset
+create table inet filter
+set inet filter foo {}
+add element inet filter foo { foobar }"
+
+$NFT -f - <<< $RULESET || exit 0
diff --git a/tests/shell/testcases/sets/exact_overlap_0 b/tests/shell/testcases/sets/exact_overlap_0
new file mode 100755
index 00000000..1ce9304a
--- /dev/null
+++ b/tests/shell/testcases/sets/exact_overlap_0
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+RULESET="add table t
+add set t s { type ipv4_addr; flags interval; }
+add element t s { 1.0.1.0/24 }
+add element t s { 1.0.2.0/23 }
+add element t s { 1.0.8.0/21 }
+add element t s { 1.0.32.0/19 }
+add element t s { 1.1.0.0/24 }
+add element t s { 1.1.2.0/23 }
+add element t s { 1.1.4.0/22 }
+add element t s { 1.1.8.0/24 }
+add element t s { 1.1.9.0/24 }
+add element t s { 1.1.10.0/23 }
+add element t s { 1.1.12.0/22 }
+add element t s { 1.1.16.0/20 }
+add element t s { 1.1.32.0/19 }
+add element t s { 1.0.1.0/24 }"
+
+$NFT -f - <<< $RULESET || exit 1
+
+$NFT add element t s { 1.0.1.0/24 }
diff --git a/tests/shell/testcases/sets/set_eval_0 b/tests/shell/testcases/sets/set_eval_0
new file mode 100755
index 00000000..82b6d3bc
--- /dev/null
+++ b/tests/shell/testcases/sets/set_eval_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip nat {
+ set set_with_interval {
+ type ipv4_addr
+ flags interval
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto { tcp, udp } th dport 443 dnat to 10.0.0.1
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/sets_with_ifnames b/tests/shell/testcases/sets/sets_with_ifnames
index f4ef4db5..9531c856 100755
--- a/tests/shell/testcases/sets/sets_with_ifnames
+++ b/tests/shell/testcases/sets/sets_with_ifnames
@@ -13,6 +13,7 @@ ns2="nft2ifname-$rnd"
cleanup()
{
ip netns del "$ns1"
+ ip netns del "$ns2"
}
trap cleanup EXIT