diff options
43 files changed, 948 insertions, 156 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); @@ -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); @@ -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; @@ -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 068c3bba..3bdd98d4 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -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 ']' { @@ -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 |