diff options
Diffstat (limited to 'src/netlink_linearize.c')
-rw-r--r-- | src/netlink_linearize.c | 492 |
1 files changed, 376 insertions, 116 deletions
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 08f7f89f..6204d8fd 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -9,9 +9,11 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ +#include <nft.h> + #include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_log.h> -#include <string.h> #include <rule.h> #include <statement.h> #include <expression.h> @@ -23,11 +25,34 @@ #include <linux/netfilter.h> #include <libnftnl/udata.h> +struct nft_expr_loc *nft_expr_loc_find(const struct nftnl_expr *nle, + struct netlink_linearize_ctx *ctx) +{ + struct nft_expr_loc *eloc; + uint32_t hash; + + hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE; + list_for_each_entry(eloc, &ctx->expr_loc_htable[hash], hlist) { + if (eloc->nle == nle) + return eloc; + } + + return NULL; +} -struct netlink_linearize_ctx { - struct nftnl_rule *nlr; - unsigned int reg_low; -}; +static void nft_expr_loc_add(const struct nftnl_expr *nle, + const struct location *loc, + struct netlink_linearize_ctx *ctx) +{ + struct nft_expr_loc *eloc; + uint32_t hash; + + eloc = xmalloc(sizeof(*eloc)); + eloc->nle = nle; + eloc->loc = loc; + hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE; + list_add_tail(&eloc->hlist, &ctx->expr_loc_htable[hash]); +} static void netlink_put_register(struct nftnl_expr *nle, uint32_t attr, uint32_t reg) @@ -104,6 +129,14 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx, } } +static void nft_rule_add_expr(struct netlink_linearize_ctx *ctx, + struct nftnl_expr *nle, + const struct location *loc) +{ + nft_expr_loc_add(nle, loc, ctx); + nftnl_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_fib(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) @@ -115,7 +148,7 @@ static void netlink_gen_fib(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_RESULT, expr->fib.result); nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_FLAGS, expr->fib.flags); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_hash(struct netlink_linearize_ctx *ctx, @@ -143,12 +176,11 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_SEED, expr->hash.seed); nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_OFFSET, expr->hash.offset); nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_TYPE, expr->hash.type); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } -static void netlink_gen_payload(struct netlink_linearize_ctx *ctx, - const struct expr *expr, - enum nft_registers dreg) +static struct nftnl_expr * +__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg) { struct nftnl_expr *nle; @@ -161,26 +193,92 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN, div_round_up(expr->len, BITS_PER_BYTE)); - nftnl_rule_add_expr(ctx->nlr, nle); + return nle; +} + +static struct nftnl_expr * +__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("meta"); + netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key); + + return nle; +} + +static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr, + enum nft_registers dreg) +{ + struct expr *_expr = (struct expr *)expr; + struct nftnl_expr *nle; + + switch (expr->etype) { + case EXPR_PAYLOAD: + if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1) + _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1; + + nle = __netlink_gen_payload(expr, dreg); + break; + case EXPR_META: + nle = __netlink_gen_meta(expr, dreg); + break; + default: + assert(0); + break; + } + + return nle; +} + +static void netlink_gen_inner(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg, + const struct proto_desc *desc) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("inner"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type); + nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_payload(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + if (expr->payload.inner_desc) { + netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc); + return; + } + + nle = __netlink_gen_payload(expr, dreg); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) { - unsigned int offset = expr->exthdr.tmpl->offset + expr->exthdr.offset; + unsigned int offset = expr->exthdr.offset; struct nftnl_expr *nle; nle = alloc_nft_expr("exthdr"); netlink_put_register(nle, NFTNL_EXPR_EXTHDR_DREG, dreg); nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, - expr->exthdr.desc->type); + expr->exthdr.raw_type); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN, div_round_up(expr->len, BITS_PER_BYTE)); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_FLAGS, expr->exthdr.flags); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_meta(struct netlink_linearize_ctx *ctx, @@ -189,10 +287,13 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx, { struct nftnl_expr *nle; - nle = alloc_nft_expr("meta"); - netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg); - nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key); - nftnl_rule_add_expr(ctx->nlr, nle); + if (expr->meta.inner_desc) { + netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc); + return; + } + + nle = __netlink_gen_meta(expr, dreg); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_rt(struct netlink_linearize_ctx *ctx, @@ -204,7 +305,7 @@ static void netlink_gen_rt(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("rt"); netlink_put_register(nle, NFTNL_EXPR_RT_DREG, dreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_RT_KEY, expr->rt.key); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_socket(struct netlink_linearize_ctx *ctx, @@ -216,7 +317,8 @@ static void netlink_gen_socket(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("socket"); netlink_put_register(nle, NFTNL_EXPR_SOCKET_DREG, dreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_KEY, expr->socket.key); - nftnl_rule_add_expr(ctx->nlr, nle); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_LEVEL, expr->socket.level); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_osf(struct netlink_linearize_ctx *ctx, @@ -229,7 +331,7 @@ static void netlink_gen_osf(struct netlink_linearize_ctx *ctx, netlink_put_register(nle, NFTNL_EXPR_OSF_DREG, dreg); nftnl_expr_set_u8(nle, NFTNL_EXPR_OSF_TTL, expr->osf.ttl); nftnl_expr_set_u32(nle, NFTNL_EXPR_OSF_FLAGS, expr->osf.flags); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx, @@ -243,7 +345,7 @@ static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_TYPE, expr->numgen.type); nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_MODULUS, expr->numgen.mod); nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_OFFSET, expr->numgen.offset); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_ct(struct netlink_linearize_ctx *ctx, @@ -259,7 +361,7 @@ static void netlink_gen_ct(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR, expr->ct.direction); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_map(struct netlink_linearize_ctx *ctx, @@ -297,7 +399,7 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx, if (dreg == NFT_REG_VERDICT) release_register(ctx, expr->map); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, @@ -323,7 +425,7 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_FLAGS, NFT_LOOKUP_F_INV); release_register(ctx, expr->left); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op) @@ -358,7 +460,8 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx, mpz_init(mask); mpz_prefixmask(mask, expr->right->len, expr->right->prefix_len); netlink_gen_raw_data(mask, expr->right->byteorder, - expr->right->len / BITS_PER_BYTE, &nld); + div_round_up(expr->right->len, BITS_PER_BYTE), + &nld); mpz_clear(mask); zero.len = nld.len; @@ -369,7 +472,7 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, nld.len); nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld.value, nld.len); nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &zero.value, zero.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); return expr->right->prefix; } @@ -399,7 +502,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, netlink_gen_data(range->right, &nld); nftnl_expr_set(nle, NFTNL_EXPR_RANGE_TO_DATA, nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); break; case OP_EQ: case OP_IMPLICIT: @@ -409,7 +512,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, netlink_gen_cmp_op(OP_GTE)); netlink_gen_data(range->left, &nld); nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); nle = alloc_nft_expr("cmp"); netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); @@ -417,7 +520,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, netlink_gen_cmp_op(OP_LTE)); netlink_gen_data(range->right, &nld); nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); break; default: BUG("invalid range operation %u\n", expr->op); @@ -448,19 +551,31 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld); netlink_gen_data(expr->right, &nld2); - nle = alloc_nft_expr("bitwise"); - netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg); - netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg); - nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len); - nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len); - nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + if (expr->left->etype == EXPR_BINOP) { + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld2.value, nld2.len); + nft_rule_add_expr(ctx, nle, &expr->location); + } else { + nle = alloc_nft_expr("bitwise"); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); - nle = alloc_nft_expr("cmp"); - netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); - nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ); - nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + if (expr->op == OP_NEG) + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); + else + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ); + + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + } mpz_clear(zero); release_register(ctx, expr->left); @@ -486,6 +601,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, case OP_GT: case OP_LTE: case OP_GTE: + case OP_NEG: break; default: BUG("invalid relational operation %u\n", expr->op); @@ -501,7 +617,10 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, return netlink_gen_flagcmp(ctx, expr, dreg); case EXPR_PREFIX: sreg = get_register(ctx, expr->left); - if (expr_basetype(expr->left)->type != TYPE_STRING) { + if (expr_basetype(expr->left)->type != TYPE_STRING && + (expr->right->byteorder != BYTEORDER_BIG_ENDIAN || + !expr->right->prefix_len || + expr->right->prefix_len % BITS_PER_BYTE)) { len = div_round_up(expr->right->len, BITS_PER_BYTE); netlink_gen_expr(ctx, expr->left, sreg); right = netlink_gen_prefix(ctx, expr, sreg); @@ -513,7 +632,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, } break; default: - if (expr->op == OP_IMPLICIT && + if ((expr->op == OP_IMPLICIT || expr->op == OP_NEG) && expr->right->dtype->basetype != NULL && expr->right->dtype->basetype->type == TYPE_BITMASK) return netlink_gen_flagcmp(ctx, expr, dreg); @@ -533,7 +652,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, len); release_register(ctx, expr->left); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void combine_binop(mpz_t mask, mpz_t xor, const mpz_t m, const mpz_t x) @@ -569,17 +688,17 @@ static void netlink_gen_shift(struct netlink_linearize_ctx *ctx, nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_DATA, nld.value, nld.len); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) { + struct expr *binops[NFT_MAX_EXPR_RECURSION]; struct nftnl_expr *nle; struct nft_data_linearize nld; struct expr *left, *i; - struct expr *binops[16]; mpz_t mask, xor, val, tmp; unsigned int len; int n = 0; @@ -591,16 +710,18 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx, binops[n++] = left = (struct expr *) expr; while (left->etype == EXPR_BINOP && left->left != NULL && - (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR)) + (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR)) { + if (n == array_size(binops)) + BUG("NFT_MAX_EXPR_RECURSION limit reached"); binops[n++] = left = left->left; - n--; + } - netlink_gen_expr(ctx, binops[n--], dreg); + netlink_gen_expr(ctx, binops[--n], dreg); mpz_bitmask(mask, expr->len); mpz_set_ui(xor, 0); - for (; n >= 0; n--) { - i = binops[n]; + while (n > 0) { + i = binops[--n]; mpz_set(val, i->right->value); switch (i->op) { @@ -639,7 +760,7 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx, mpz_clear(xor); mpz_clear(mask); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_binop(struct netlink_linearize_ctx *ctx, @@ -676,6 +797,8 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx, struct nftnl_expr *nle; int byte_size; + assert(div_round_up(expr->arg->len, BITS_PER_BYTE) != 1); + if ((expr->arg->len % 64) == 0) byte_size = 8; else if ((expr->arg->len % 32) == 0) @@ -689,20 +812,21 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx, netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_SREG, dreg); netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_DREG, dreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_LEN, - expr->len / BITS_PER_BYTE); + div_round_up(expr->len, BITS_PER_BYTE)); nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_SIZE, byte_size); nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP, netlink_gen_unary_op(expr->op)); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) { - struct nftnl_expr *nle; + const struct location *loc = &expr->location; struct nft_data_linearize nld; + struct nftnl_expr *nle; nle = alloc_nft_expr("immediate"); netlink_put_register(nle, NFTNL_EXPR_IMM_DREG, dreg); @@ -712,17 +836,20 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx, nftnl_expr_set(nle, NFTNL_EXPR_IMM_DATA, nld.value, nld.len); break; case EXPR_VERDICT: - if ((expr->chain != NULL) && - !nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_CHAIN)) { + if (expr->chain) { nftnl_expr_set_str(nle, NFTNL_EXPR_IMM_CHAIN, nld.chain); + loc = &expr->chain->location; + } else if (expr->chain_id) { + nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_CHAIN_ID, + nld.chain_id); } nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_VERDICT, nld.verdict); break; default: break; } - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, loc); } static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx, @@ -736,7 +863,7 @@ static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_KEY, expr->xfrm.key); nftnl_expr_set_u8(nle, NFTNL_EXPR_XFRM_DIR, expr->xfrm.direction); nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_SPNUM, expr->xfrm.spnum); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, @@ -819,7 +946,7 @@ static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx, default: BUG("unsupported expression %u\n", expr->etype); } - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static struct nftnl_expr *netlink_gen_connlimit_stmt(const struct stmt *stmt) @@ -880,6 +1007,17 @@ static struct nftnl_expr *netlink_gen_quota_stmt(const struct stmt *stmt) return nle; } +static struct nftnl_expr *netlink_gen_last_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("last"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LAST_SET, stmt->last.set); + nftnl_expr_set_u64(nle, NFTNL_EXPR_LAST_MSECS, stmt->last.used); + + return nle; +} + struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt) { switch (stmt->ops->type) { @@ -891,6 +1029,8 @@ struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt) return netlink_gen_limit_stmt(stmt); case STMT_QUOTA: return netlink_gen_quota_stmt(stmt); + case STMT_LAST: + return netlink_gen_last_stmt(stmt); default: BUG("unknown stateful statement type %s\n", stmt->ops->name); } @@ -928,17 +1068,17 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx, expr = stmt->exthdr.expr; - offset = expr->exthdr.tmpl->offset + expr->exthdr.offset; + offset = expr->exthdr.offset; nle = alloc_nft_expr("exthdr"); netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg); nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, - expr->exthdr.desc->type); + expr->exthdr.raw_type); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN, div_round_up(expr->len, BITS_PER_BYTE)); nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx, @@ -971,16 +1111,17 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx, expr->len / BITS_PER_BYTE); if (csum_off) { nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_TYPE, - NFT_PAYLOAD_CSUM_INET); + desc->checksum_type); nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET, csum_off / BITS_PER_BYTE); } - if (expr->payload.base == PROTO_BASE_NETWORK_HDR && desc && - payload_needs_l4csum_update_pseudohdr(expr, desc)) + if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc && + payload_needs_l4csum_update_pseudohdr(expr, desc)) || + expr->payload.base == PROTO_BASE_INNER_HDR) nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS, NFT_PAYLOAD_L4CSUM_PSEUDOHDR); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx, @@ -996,7 +1137,7 @@ static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("meta"); netlink_put_register(nle, NFTNL_EXPR_META_SREG, sreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, stmt->meta.key); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx, @@ -1006,8 +1147,10 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("log"); if (stmt->log.prefix != NULL) { - nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, - stmt->log.prefix); + char prefix[NF_LOG_PREFIXLEN] = {}; + + expr_to_string(stmt->log.prefix, prefix); + nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix); } if (stmt->log.flags & STMT_LOG_GROUP) { nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group); @@ -1025,7 +1168,7 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_FLAGS, stmt->log.logflags); } - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx, @@ -1039,7 +1182,7 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u8(nle, NFTNL_EXPR_REJECT_CODE, stmt->reject.icmp_code); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static unsigned int nat_addrlen(uint8_t family) @@ -1119,11 +1262,14 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, amin_reg); if (stmt->nat.addr->etype == EXPR_MAP && stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL) { - amax_reg = get_register(ctx, NULL); - registers++; amin_reg += netlink_register_space(nat_addrlen(family)); - netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, - amin_reg); + if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) { + netlink_put_register(nle, nftnl_reg_pmin, + amin_reg); + } else { + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, + amin_reg); + } } } @@ -1135,6 +1281,12 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, pmin_reg = amin_reg; + if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) { + pmin_reg += netlink_register_space(nat_addrlen(family)); + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, + pmin_reg); + } + /* if STMT_NAT_F_CONCAT is set, the mapped type is a * concatenation of 'addr . inet_service'. * The map lookup will then return the @@ -1143,7 +1295,10 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, * will hold the inet_service part. */ pmin_reg += netlink_register_space(nat_addrlen(family)); - netlink_put_register(nle, nftnl_reg_pmin, pmin_reg); + if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) + netlink_put_register(nle, nftnl_reg_pmax, pmin_reg); + else + netlink_put_register(nle, nftnl_reg_pmin, pmin_reg); } } @@ -1170,7 +1325,7 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, registers--; } - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx, @@ -1209,7 +1364,7 @@ static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx, registers--; } - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx, @@ -1224,7 +1379,7 @@ static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS, stmt->synproxy.flags); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx, @@ -1255,7 +1410,7 @@ static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx, if (stmt->dup.to != NULL) release_register(ctx, stmt->dup.to); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx, @@ -1282,32 +1437,62 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_FWD_NFPROTO, stmt->fwd.family); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_optstrip_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle = alloc_nft_expr("exthdr"); + struct expr *expr = stmt->optstrip.expr; + + nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, + expr->exthdr.raw_type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); + nft_rule_add_expr(ctx, nle, &expr->location); } static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { + enum nft_registers sreg = 0; struct nftnl_expr *nle; uint16_t total_queues; + struct expr *expr; mpz_t low, high; mpz_init2(low, 16); mpz_init2(high, 16); - if (stmt->queue.queue != NULL) { - range_expr_value_low(low, stmt->queue.queue); - range_expr_value_high(high, stmt->queue.queue); + + expr = stmt->queue.queue; + + if (expr) { + if (expr->etype == EXPR_RANGE || expr->etype == EXPR_VALUE) { + range_expr_value_low(low, stmt->queue.queue); + range_expr_value_high(high, stmt->queue.queue); + } else { + sreg = get_register(ctx, expr); + netlink_gen_expr(ctx, expr, sreg); + release_register(ctx, expr); + } } + total_queues = mpz_get_uint16(high) - mpz_get_uint16(low) + 1; nle = alloc_nft_expr("queue"); - nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low)); - nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues); + + if (sreg) { + netlink_put_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM, sreg); + } else { + nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low)); + nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues); + } + if (stmt->queue.flags) nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_FLAGS, stmt->queue.flags); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); mpz_clear(low); mpz_clear(high); @@ -1330,7 +1515,7 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR, stmt->ct.direction); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx, @@ -1339,7 +1524,7 @@ static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx, struct nftnl_expr *nle; nle = alloc_nft_expr("notrack"); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx, @@ -1350,15 +1535,17 @@ static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("flow_offload"); nftnl_expr_set_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME, stmt->flow.table_name); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); } static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { struct set *set = stmt->meter.set->set; - struct nftnl_expr *nle; enum nft_registers sreg_key; + struct nftnl_expr *nle; + int num_stmts = 0; + struct stmt *this; sreg_key = get_register(ctx, stmt->set.key->key); netlink_gen_expr(ctx, stmt->set.key->key, sreg_key); @@ -1372,11 +1559,24 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->set.op); nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name); nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); - if (stmt->set.stmt) - nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, - netlink_gen_stmt_stateful(stmt->set.stmt), 0); + list_for_each_entry(this, &stmt->set.stmt_list, list) + num_stmts++; + + if (num_stmts == 1) { + list_for_each_entry(this, &stmt->set.stmt_list, list) { + nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, + netlink_gen_stmt_stateful(this), 0); + } + } else if (num_stmts > 1) { + list_for_each_entry(this, &stmt->set.stmt_list, list) { + nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS, + netlink_gen_stmt_stateful(this)); + } + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_FLAGS, + NFT_DYNSET_F_EXPR); + } } static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx, @@ -1386,14 +1586,16 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx, enum nft_registers sreg_data; enum nft_registers sreg_key; struct nftnl_expr *nle; + int num_stmts = 0; + struct stmt *this; - sreg_key = get_register(ctx, stmt->map.key); - netlink_gen_expr(ctx, stmt->map.key, sreg_key); + sreg_key = get_register(ctx, stmt->map.key->key); + netlink_gen_expr(ctx, stmt->map.key->key, sreg_key); sreg_data = get_register(ctx, stmt->map.data); netlink_gen_expr(ctx, stmt->map.data, sreg_data); - release_register(ctx, stmt->map.key); + release_register(ctx, stmt->map.key->key); release_register(ctx, stmt->map.data); nle = alloc_nft_expr("dynset"); @@ -1403,12 +1605,26 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->map.op); nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name); nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); + nft_rule_add_expr(ctx, nle, &stmt->location); - if (stmt->map.stmt) - nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, - netlink_gen_stmt_stateful(stmt->map.stmt), 0); + if (stmt->map.key->timeout > 0) + nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT, + stmt->map.key->timeout); - nftnl_rule_add_expr(ctx->nlr, nle); + list_for_each_entry(this, &stmt->map.stmt_list, list) + num_stmts++; + + if (num_stmts == 1) { + list_for_each_entry(this, &stmt->map.stmt_list, list) { + nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, + netlink_gen_stmt_stateful(this), 0); + } + } else if (num_stmts > 1) { + list_for_each_entry(this, &stmt->map.stmt_list, list) { + nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS, + netlink_gen_stmt_stateful(this)); + } + } } static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx, @@ -1439,7 +1655,13 @@ static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx, nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, netlink_gen_stmt_stateful(stmt->meter.stmt), 0); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_chain_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + return netlink_gen_expr(ctx, stmt->chain.expr, NFT_REG_VERDICT); } static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, @@ -1484,8 +1706,9 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, case STMT_COUNTER: case STMT_LIMIT: case STMT_QUOTA: + case STMT_LAST: nle = netlink_gen_stmt_stateful(stmt); - nftnl_rule_add_expr(ctx->nlr, nle); + nft_rule_add_expr(ctx, nle, &stmt->location); break; case STMT_NOTRACK: return netlink_gen_notrack_stmt(ctx, stmt); @@ -1495,23 +1718,49 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_objref_stmt(ctx, stmt); case STMT_MAP: return netlink_gen_map_stmt(ctx, stmt); + case STMT_CHAIN: + return netlink_gen_chain_stmt(ctx, stmt); + case STMT_OPTSTRIP: + return netlink_gen_optstrip_stmt(ctx, stmt); default: BUG("unknown statement type %s\n", stmt->ops->name); } } -void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr, - const struct rule *rule) +void netlink_linearize_init(struct netlink_linearize_ctx *lctx, + struct nftnl_rule *nlr) { - struct netlink_linearize_ctx lctx; - const struct stmt *stmt; + int i; + + memset(lctx, 0, sizeof(*lctx)); + lctx->reg_low = NFT_REG_1; + lctx->nlr = nlr; + lctx->expr_loc_htable = + xmalloc(sizeof(struct list_head) * NFT_EXPR_LOC_HSIZE); + for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) + init_list_head(&lctx->expr_loc_htable[i]); +} - memset(&lctx, 0, sizeof(lctx)); - lctx.reg_low = NFT_REG_1; - lctx.nlr = nlr; +void netlink_linearize_fini(struct netlink_linearize_ctx *lctx) +{ + struct nft_expr_loc *eloc, *next; + int i; + + for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) { + list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist) + free(eloc); + } + free(lctx->expr_loc_htable); +} + +void netlink_linearize_rule(struct netlink_ctx *ctx, + const struct rule *rule, + struct netlink_linearize_ctx *lctx) +{ + const struct stmt *stmt; list_for_each_entry(stmt, &rule->stmts, list) - netlink_gen_stmt(&lctx, stmt); + netlink_gen_stmt(lctx, stmt); if (rule->comment) { struct nftnl_udata_buf *udata; @@ -1523,12 +1772,23 @@ void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr, if (!nftnl_udata_put_strz(udata, NFTNL_UDATA_RULE_COMMENT, rule->comment)) memory_allocation_error(); - nftnl_rule_set_data(nlr, NFTNL_RULE_USERDATA, + nftnl_rule_set_data(lctx->nlr, NFTNL_RULE_USERDATA, nftnl_udata_buf_data(udata), nftnl_udata_buf_len(udata)); nftnl_udata_buf_free(udata); } - netlink_dump_rule(nlr, ctx); + if (ctx->nft->debug_mask & NFT_DEBUG_NETLINK) { + nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_TABLE, + rule->handle.table.name); + if (rule->handle.chain.name) + nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_CHAIN, + rule->handle.chain.name); + + netlink_dump_rule(lctx->nlr, ctx); + + nftnl_rule_unset(lctx->nlr, NFTNL_RULE_CHAIN); + nftnl_rule_unset(lctx->nlr, NFTNL_RULE_TABLE); + } } |