From 665a26232b2a53e7f47481830c4be79d890b0ff4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 2 Jun 2015 12:16:42 +0200 Subject: eval: prohibit variable sized types in concat expressions Since we need to calculate the length of the entire concat type, we can not support variable sized types where the length can't be determined by the type. This only affects base types since all higher types include a length. Signed-off-by: Patrick McHardy --- src/evaluate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/evaluate.c b/src/evaluate.c index e260a803..d99b38f4 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -620,6 +620,13 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr) "expecting %s", dtype->desc); + if (dtype == NULL && i->dtype->size == 0) + return expr_binary_error(ctx->msgs, i, *expr, + "can not use variable sized " + "data types (%s) in concat " + "expressions", + i->dtype->name); + tmp = concat_subtype_lookup(type, --off); expr_set_context(&ctx->ectx, tmp, tmp->size); -- cgit v1.2.3 From e3ecdda1f6a326b707ed62ca4278034b54a38aef Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:41 +0100 Subject: headers: sync headers for new register values Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_tables.h | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 0e964439..33056dc2 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -4,16 +4,45 @@ #define NFT_CHAIN_MAXNAMELEN 32 #define NFT_USERDATA_MAXLEN 256 +/** + * enum nft_registers - nf_tables registers + * + * nf_tables used to have five registers: a verdict register and four data + * registers of size 16. The data registers have been changed to 16 registers + * of size 4. For compatibility reasons, the NFT_REG_[1-4] registers still + * map to areas of size 16, the 4 byte registers are addressed using + * NFT_REG32_00 - NFT_REG32_15. + */ enum nft_registers { NFT_REG_VERDICT, NFT_REG_1, NFT_REG_2, NFT_REG_3, NFT_REG_4, - __NFT_REG_MAX + __NFT_REG_MAX, + + NFT_REG32_00 = 8, + MFT_REG32_01, + NFT_REG32_02, + NFT_REG32_03, + NFT_REG32_04, + NFT_REG32_05, + NFT_REG32_06, + NFT_REG32_07, + NFT_REG32_08, + NFT_REG32_09, + NFT_REG32_10, + NFT_REG32_11, + NFT_REG32_12, + NFT_REG32_13, + NFT_REG32_14, + NFT_REG32_15, }; #define NFT_REG_MAX (__NFT_REG_MAX - 1) +#define NFT_REG_SIZE 16 +#define NFT_REG32_SIZE 4 + /** * enum nft_verdicts - nf_tables internal verdicts * @@ -358,6 +387,9 @@ enum nft_data_attributes { }; #define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) +/* Maximum length of a value */ +#define NFT_DATA_VALUE_MAXLEN 64 + /** * enum nft_verdict_attributes - nf_tables verdict netlink attributes * -- cgit v1.2.3 From 59221eacafbae721c0e46b75c688f1cd0f251afc Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 20 Apr 2015 14:45:40 +0200 Subject: netlink: pass expression to register allocation/release functions Prepare for taking the expression size into account. Signed-off-by: Patrick McHardy --- src/netlink_linearize.c | 54 +++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index fbc6ae12..beeb0c22 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -30,14 +30,16 @@ static void netlink_put_register(struct nft_rule_expr *nle, nft_rule_expr_set_u32(nle, attr, reg); } -static enum nft_registers get_register(struct netlink_linearize_ctx *ctx) +static enum nft_registers get_register(struct netlink_linearize_ctx *ctx, + const struct expr *expr) { if (ctx->reg_low > NFT_REG_MAX) BUG("register reg_low %u invalid\n", ctx->reg_low); return ctx->reg_low++; } -static void release_register(struct netlink_linearize_ctx *ctx) +static void release_register(struct netlink_linearize_ctx *ctx, + const struct expr *expr) { ctx->reg_low--; } @@ -124,7 +126,7 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx, assert(expr->mappings->ops->type == EXPR_SET_REF); if (dreg == NFT_REG_VERDICT) - sreg = get_register(ctx); + sreg = get_register(ctx, expr->map); else sreg = dreg; @@ -139,7 +141,7 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx, expr->mappings->set->handle.set_id); if (dreg == NFT_REG_VERDICT) - release_register(ctx); + release_register(ctx, expr->map); nft_rule_add_expr(ctx->nlr, nle); } @@ -154,7 +156,7 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, assert(expr->right->ops->type == EXPR_SET_REF); assert(dreg == NFT_REG_VERDICT); - sreg = get_register(ctx); + sreg = get_register(ctx, expr->left); netlink_gen_expr(ctx, expr->left, sreg); nle = alloc_nft_expr("lookup"); @@ -164,7 +166,7 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SET_ID, expr->right->set->handle.set_id); - release_register(ctx); + release_register(ctx, expr->left); nft_rule_add_expr(ctx->nlr, nle); } @@ -206,7 +208,7 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx, if (expr->right->ops->type == EXPR_RANGE) return netlink_gen_range(ctx, expr, dreg); - sreg = get_register(ctx); + sreg = get_register(ctx, expr->left); netlink_gen_expr(ctx, expr->left, sreg); switch (expr->right->ops->type) { @@ -242,7 +244,7 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx, netlink_gen_cmp_op(expr->op)); netlink_gen_data(right, &nld); nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len); - release_register(ctx); + release_register(ctx, expr->left); nft_rule_add_expr(ctx->nlr, nle); } @@ -258,7 +260,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, assert(dreg == NFT_REG_VERDICT); - sreg = get_register(ctx); + sreg = get_register(ctx, expr->left); netlink_gen_expr(ctx, expr->left, sreg); nle = alloc_nft_expr("cmp"); @@ -301,7 +303,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len); nft_rule_add_expr(ctx->nlr, nle); - release_register(ctx); + release_register(ctx, expr->left); } static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, @@ -316,7 +318,7 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, assert(dreg == NFT_REG_VERDICT); - sreg = get_register(ctx); + sreg = get_register(ctx, expr->left); netlink_gen_expr(ctx, expr->left, sreg); len = div_round_up(expr->left->len, BITS_PER_BYTE); @@ -340,7 +342,7 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, nft_rule_add_expr(ctx->nlr, nle); mpz_clear(zero); - release_register(ctx); + release_register(ctx, expr->left); } static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, @@ -565,9 +567,9 @@ static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx, struct nft_rule_expr *nle; enum nft_registers sreg; - sreg = get_register(ctx); + sreg = get_register(ctx, stmt->meta.expr); netlink_gen_expr(ctx, stmt->meta.expr, sreg); - release_register(ctx); + release_register(ctx, stmt->meta.expr); nle = alloc_nft_expr("meta"); netlink_put_register(nle, NFT_EXPR_META_SREG, sreg); @@ -647,11 +649,11 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_FLAGS, stmt->nat.flags); if (stmt->nat.addr) { - amin_reg = get_register(ctx); + amin_reg = get_register(ctx, NULL); registers++; if (stmt->nat.addr->ops->type == EXPR_RANGE) { - amax_reg = get_register(ctx); + amax_reg = get_register(ctx, NULL); registers++; netlink_gen_expr(ctx, stmt->nat.addr->left, amin_reg); @@ -669,11 +671,11 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, } if (stmt->nat.proto) { - pmin_reg = get_register(ctx); + pmin_reg = get_register(ctx, NULL); registers++; if (stmt->nat.proto->ops->type == EXPR_RANGE) { - pmax_reg = get_register(ctx); + pmax_reg = get_register(ctx, NULL); registers++; netlink_gen_expr(ctx, stmt->nat.proto->left, pmin_reg); @@ -690,7 +692,7 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, } while (registers > 0) { - release_register(ctx); + release_register(ctx, NULL); registers--; } @@ -724,11 +726,11 @@ static void netlink_gen_redir_stmt(struct netlink_linearize_ctx *ctx, stmt->redir.flags); if (stmt->redir.proto) { - pmin_reg = get_register(ctx); + pmin_reg = get_register(ctx, NULL); registers++; if (stmt->redir.proto->ops->type == EXPR_RANGE) { - pmax_reg = get_register(ctx); + pmax_reg = get_register(ctx, NULL); registers++; netlink_gen_expr(ctx, stmt->redir.proto->left, @@ -750,7 +752,7 @@ static void netlink_gen_redir_stmt(struct netlink_linearize_ctx *ctx, } while (registers > 0) { - release_register(ctx); + release_register(ctx, NULL); registers--; } @@ -791,9 +793,9 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx, struct nft_rule_expr *nle; enum nft_registers sreg; - sreg = get_register(ctx); + sreg = get_register(ctx, stmt->ct.expr); netlink_gen_expr(ctx, stmt->ct.expr, sreg); - release_register(ctx); + release_register(ctx, stmt->ct.expr); nle = alloc_nft_expr("ct"); netlink_put_register(nle, NFT_EXPR_CT_SREG, sreg); @@ -807,9 +809,9 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx, struct nft_rule_expr *nle; enum nft_registers sreg_key; - sreg_key = get_register(ctx); + sreg_key = get_register(ctx, stmt->set.key); netlink_gen_expr(ctx, stmt->set.key, sreg_key); - release_register(ctx); + release_register(ctx, stmt->set.key); nle = alloc_nft_expr("dynset"); netlink_put_register(nle, NFT_EXPR_DYNSET_SREG_KEY, sreg_key); -- cgit v1.2.3 From 9c286f2d689bbc19889d2f3b69923ad68831b3e7 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:41 +0100 Subject: netlink_linearize: use NFT_REG32 values internally Prepare netlink_linearize for 32 bit register usage: Switch to use 16 data registers of 32 bit each. A helper function takes care of mapping the registers to the NFT_REG32 values and, if the register refers to the beginning of an 128 bit area, the old NFT_REG_1-4 values for compatibility. New register reservation and release helper function take the size into account and reserve the required amount of registers. The reservation and release functions will so far still always allocate 128 bit. If no other expression in a rule uses a 32 bit register directly, these will be mapped to the old register values, meaning everything continues to work with old kernel versions. Signed-off-by: Patrick McHardy --- include/netlink.h | 5 +++++ src/netlink_linearize.c | 43 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/include/netlink.h b/include/netlink.h index 9f24ea5e..9b42fdbd 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -53,6 +53,11 @@ struct nft_data_delinearize { int verdict; }; +static inline unsigned int netlink_register_space(unsigned int size) +{ + return div_round_up(size, NFT_REG32_SIZE * BITS_PER_BYTE); +} + extern void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data); extern void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder, diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index beeb0c22..6930b39d 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -27,21 +27,54 @@ struct netlink_linearize_ctx { static void netlink_put_register(struct nft_rule_expr *nle, uint32_t attr, uint32_t reg) { + /* Convert to 128 bit register numbers if possible for compatibility */ + if (reg != NFT_REG_VERDICT) { + reg -= NFT_REG_1; + if (reg % (NFT_REG_SIZE / NFT_REG32_SIZE) == 0) + reg = NFT_REG_1 + reg / (NFT_REG_SIZE / NFT_REG32_SIZE); + else + reg += NFT_REG32_00; + } + nft_rule_expr_set_u32(nle, attr, reg); } +static enum nft_registers __get_register(struct netlink_linearize_ctx *ctx, + unsigned int size) +{ + unsigned int reg, n; + + n = netlink_register_space(size); + if (ctx->reg_low + n > NFT_REG_1 + NFT_REG32_15 - NFT_REG32_00 + 1) + BUG("register reg_low %u invalid\n", ctx->reg_low); + + reg = ctx->reg_low; + ctx->reg_low += n; + return reg; +} + +static void __release_register(struct netlink_linearize_ctx *ctx, + unsigned int size) +{ + unsigned int n; + + n = netlink_register_space(size); + if (ctx->reg_low < NFT_REG_1 + n) + BUG("register reg_low %u invalid\n", ctx->reg_low); + + ctx->reg_low -= n; +} + static enum nft_registers get_register(struct netlink_linearize_ctx *ctx, const struct expr *expr) { - if (ctx->reg_low > NFT_REG_MAX) - BUG("register reg_low %u invalid\n", ctx->reg_low); - return ctx->reg_low++; + return __get_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); } static void release_register(struct netlink_linearize_ctx *ctx, const struct expr *expr) { - ctx->reg_low--; + __release_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); } static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, @@ -509,6 +542,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) { + assert(dreg < ctx->reg_low); + switch (expr->ops->type) { case EXPR_VERDICT: case EXPR_VALUE: -- cgit v1.2.3 From 1e4bf9dbb9218030ce9a5ee0b30eb33f918ca6bf Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 13 Apr 2015 12:19:12 +0100 Subject: netlink_linearize: generate concat expressions Use the real length for reserving/releasing registers when generating concat expressions. Signed-off-by: Patrick McHardy --- src/netlink_linearize.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 6930b39d..bf1e56be 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -68,13 +68,19 @@ static void __release_register(struct netlink_linearize_ctx *ctx, static enum nft_registers get_register(struct netlink_linearize_ctx *ctx, const struct expr *expr) { - return __get_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); + if (expr && expr->ops->type == EXPR_CONCAT) + return __get_register(ctx, expr->len); + else + return __get_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); } static void release_register(struct netlink_linearize_ctx *ctx, const struct expr *expr) { - __release_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); + if (expr && expr->ops->type == EXPR_CONCAT) + __release_register(ctx, expr->len); + else + __release_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); } static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, @@ -87,8 +93,10 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx, { const struct expr *i; - list_for_each_entry(i, &expr->expressions, list) + list_for_each_entry(i, &expr->expressions, list) { netlink_gen_expr(ctx, i, dreg); + dreg += netlink_register_space(i->len); + } } static void netlink_gen_payload(struct netlink_linearize_ctx *ctx, -- cgit v1.2.3 From 9c641885afea6f46b62f591ed9b0e3006fb23701 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:42 +0100 Subject: netlink: pad constant concat sub-expressions Pad all but the last sub-expressions of a concat expressions. Signed-off-by: Patrick McHardy --- include/netlink.h | 10 ++++++++++ src/datatype.c | 3 ++- src/netlink.c | 14 ++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/netlink.h b/include/netlink.h index 9b42fdbd..185c4357 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -58,6 +58,16 @@ static inline unsigned int netlink_register_space(unsigned int size) return div_round_up(size, NFT_REG32_SIZE * BITS_PER_BYTE); } +static inline unsigned int netlink_padded_len(unsigned int size) +{ + return netlink_register_space(size) * NFT_REG32_SIZE * BITS_PER_BYTE; +} + +static inline unsigned int netlink_padding_len(unsigned int size) +{ + return netlink_padded_len(size) - size; +} + extern void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data); extern void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder, diff --git a/src/datatype.c b/src/datatype.c index f93337b1..a06a58e2 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -943,7 +944,7 @@ const struct datatype *concat_type_alloc(uint32_t type) strncat(desc, i->desc, sizeof(desc) - strlen(desc) - 1); strncat(name, i->name, sizeof(name) - strlen(name) - 1); - size += i->size; + size += netlink_padded_len(i->size); subtypes++; } strncat(desc, ")", sizeof(desc) - strlen(desc) - 1); diff --git a/src/netlink.c b/src/netlink.c index d31387f8..3369d223 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -273,23 +273,21 @@ static void netlink_gen_concat_data(const struct expr *expr, const struct expr *i; unsigned int len, offset; - len = 0; - list_for_each_entry(i, &expr->expressions, list) - len += i->len; - + len = expr->len / BITS_PER_BYTE; if (1) { - unsigned char data[len / BITS_PER_BYTE]; + unsigned char data[len]; + memset(data, 0, sizeof(data)); offset = 0; list_for_each_entry(i, &expr->expressions, list) { assert(i->ops->type == EXPR_VALUE); mpz_export_data(data + offset, i->value, i->byteorder, i->len / BITS_PER_BYTE); - offset += i->len / BITS_PER_BYTE; + offset += netlink_padded_len(i->len) / BITS_PER_BYTE; } - memcpy(nld->value, data, len / BITS_PER_BYTE); - nld->len = len / BITS_PER_BYTE; + memcpy(nld->value, data, len); + nld->len = len; } } -- cgit v1.2.3 From 8ede6005f41afb69e415e1227fc7800308a63819 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:41 +0100 Subject: netlink_delinearize: introduce register translation helper Introduce a helper function to translate register numbers from the kernel from the compat values to the NFT_REG32 values. Internally we use the register numbers 0-16: * 0 is the verdict register in both old and new addressing modes. * 1-16 are the 32 bit data registers The NFT_REG32_00 values are mapped to 1-16, the NFT_REG_1-NFT_REG_4 values are each use up 4 registers starting at 1 (1, 5, 9, 13). Signed-off-by: Patrick McHardy --- src/netlink_delinearize.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index e9ce76e2..91da8597 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -30,7 +30,7 @@ struct netlink_parse_ctx { struct list_head *msgs; struct table *table; struct rule *rule; - struct expr *registers[NFT_REG_MAX + 1]; + struct expr *registers[1 + NFT_REG32_15 - NFT_REG32_00 + 1]; }; static void __fmtstring(3, 4) netlink_error(struct netlink_parse_ctx *ctx, @@ -49,14 +49,23 @@ static void __fmtstring(3, 4) netlink_error(struct netlink_parse_ctx *ctx, static unsigned int netlink_parse_register(const struct nft_rule_expr *nle, unsigned int attr) { - return nft_rule_expr_get_u32(nle, attr); + unsigned int reg; + + reg = nft_rule_expr_get_u32(nle, attr); + /* Translate 128bit registers to corresponding 32bit registers */ + if (reg >= NFT_REG_1 && reg <= NFT_REG_4) + reg = 1 + (reg - NFT_REG_1) * (NFT_REG_SIZE / NFT_REG32_SIZE); + else if (reg >= NFT_REG32_00) + reg = 1 + reg - NFT_REG32_00; + + return reg; } static void netlink_set_register(struct netlink_parse_ctx *ctx, enum nft_registers reg, struct expr *expr) { - if (reg > NFT_REG_MAX) { + if (reg == NFT_REG_VERDICT || reg > 1 + NFT_REG32_15 - NFT_REG32_00) { netlink_error(ctx, &expr->location, "Invalid destination register %u", reg); expr_free(expr); @@ -75,7 +84,7 @@ static struct expr *netlink_get_register(struct netlink_parse_ctx *ctx, { struct expr *expr; - if (reg == NFT_REG_VERDICT || reg > NFT_REG_MAX) { + if (reg == NFT_REG_VERDICT || reg > 1 + NFT_REG32_15 - NFT_REG32_00) { netlink_error(ctx, loc, "Invalid source register %u", reg); return NULL; } -- cgit v1.2.3 From 6377380bc265f8446f912420f393ef978acf6698 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:42 +0100 Subject: netlink_delinearize: handle relational and lookup concat expressions When the RHS length differs from the LHS length (which is only the first expression), both expressions are assumed to be concat expressions. The LHS concat expression is reconstructed from the available register values, advancing by the number of registers required by the subexpressions' register space, until the RHS length has been reached. The RHS concat expression is reconstructed by splitting the data value into multiple subexpressions based on the LHS concat expressions types. Signed-off-by: Patrick McHardy --- src/netlink_delinearize.c | 113 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 9 deletions(-) diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 91da8597..08fd5808 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -104,6 +104,63 @@ static void netlink_release_registers(struct netlink_parse_ctx *ctx) expr_free(ctx->registers[i]); } +static struct expr *netlink_parse_concat_expr(struct netlink_parse_ctx *ctx, + const struct location *loc, + unsigned int reg, + unsigned int len) +{ + struct expr *concat, *expr; + + concat = concat_expr_alloc(loc); + while (len > 0) { + expr = netlink_get_register(ctx, loc, reg); + if (expr == NULL) { + netlink_error(ctx, loc, + "Relational expression size mismatch"); + goto err; + } + compound_expr_add(concat, expr); + + len -= netlink_padded_len(expr->len); + reg += netlink_register_space(expr->len); + } + return concat; + +err: + expr_free(concat); + return NULL; +} + +static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx, + const struct location *loc, + unsigned int reg, + unsigned int len, + struct expr *data) +{ + struct expr *concat, *expr, *i; + + concat = concat_expr_alloc(loc); + while (len > 0) { + expr = netlink_get_register(ctx, loc, reg); + if (expr == NULL) { + netlink_error(ctx, loc, + "Relational expression size mismatch"); + goto err; + } + i = constant_expr_splice(data, expr->len); + data->len -= netlink_padding_len(expr->len); + compound_expr_add(concat, i); + + len -= netlink_padded_len(expr->len); + reg += netlink_register_space(expr->len); + } + return concat; + +err: + expr_free(concat); + return NULL; +} + static void netlink_parse_immediate(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nft_rule_expr *nle) @@ -175,9 +232,18 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx, nld.value = nft_rule_expr_get(nle, NFT_EXPR_CMP_DATA, &nld.len); right = netlink_alloc_value(loc, &nld); - if (left->len != right->len) - return netlink_error(ctx, loc, - "Relational expression size mismatch"); + if (left->len != right->len) { + if (left->len > right->len) + return netlink_error(ctx, loc, + "Relational expression size " + "mismatch"); + left = netlink_parse_concat_expr(ctx, loc, sreg, right->len); + if (left == NULL) + return; + right = netlink_parse_concat_data(ctx, loc, sreg, right->len, right); + if (right == NULL) + return; + } expr = relational_expr_alloc(loc, op, left, right); stmt = expr_stmt_alloc(loc, expr); @@ -194,12 +260,6 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx, struct expr *expr, *left, *right; struct set *set; - sreg = netlink_parse_register(nle, NFT_EXPR_LOOKUP_SREG); - left = netlink_get_register(ctx, loc, sreg); - if (left == NULL) - return netlink_error(ctx, loc, - "Lookup expression has no left hand side"); - name = nft_rule_expr_get_str(nle, NFT_EXPR_LOOKUP_SET); set = set_lookup(ctx->table, name); if (set == NULL) @@ -207,6 +267,18 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx, "Unknown set '%s' in lookup expression", name); + sreg = netlink_parse_register(nle, NFT_EXPR_LOOKUP_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "Lookup expression has no left hand side"); + + if (left->len < set->keylen) { + left = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen); + if (left == NULL) + return; + } + right = set_ref_expr_alloc(loc, set); if (nft_rule_expr_is_set(nle, NFT_EXPR_LOOKUP_DREG)) { @@ -724,6 +796,12 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx, return netlink_error(ctx, loc, "Dynset statement has no key expression"); + if (expr->len < set->keylen) { + expr = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen); + if (expr == NULL) + return; + } + expr = set_elem_expr_alloc(&expr->location, expr); expr->timeout = nft_rule_expr_get_u64(nle, NFT_EXPR_DYNSET_TIMEOUT); @@ -1000,6 +1078,23 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, list_for_each_entry(i, &expr->expressions, list) expr_postprocess(ctx, stmt, &i); break; + case EXPR_CONCAT: { + unsigned int type = expr->dtype->type, ntype = 0; + int off = expr->dtype->subtypes; + const struct datatype *dtype; + + list_for_each_entry(i, &expr->expressions, list) { + if (type) { + dtype = concat_subtype_lookup(type, --off); + expr_set_type(i, dtype, dtype->byteorder); + } + expr_postprocess(ctx, stmt, &i); + + ntype = concat_subtype_add(ntype, i->dtype->type); + } + expr->dtype = concat_type_alloc(ntype); + break; + } case EXPR_UNARY: expr_postprocess(ctx, stmt, &expr->arg); expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder); -- cgit v1.2.3 From a93bc1795b272174a10d90961a248f2c620bfa2c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 12 Apr 2015 21:10:42 +0100 Subject: netlink: handle concat expressions in set data Reconstruct the concat expressions in set data by splicing off the subtype values based on the keytype of the set. Signed-off-by: Patrick McHardy --- src/datatype.c | 2 +- src/netlink.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/datatype.c b/src/datatype.c index a06a58e2..ca834263 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -932,7 +932,7 @@ const struct datatype *concat_type_alloc(uint32_t type) unsigned int size = 0, subtypes = 0, n; n = div_round_up(fls(type), TYPE_BITS); - while (concat_subtype_id(type, --n)) { + while (n > 0 && concat_subtype_id(type, --n)) { i = concat_subtype_lookup(type, n); if (i == NULL) return NULL; diff --git a/src/netlink.c b/src/netlink.c index 3369d223..1167c951 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -1383,6 +1383,36 @@ static int netlink_del_setelems_compat(struct netlink_ctx *ctx, return err; } +static struct expr *netlink_parse_concat_elem(const struct datatype *dtype, + struct expr *data) +{ + const struct datatype *subtype; + struct expr *concat, *expr; + int off = dtype->subtypes; + + concat = concat_expr_alloc(&data->location); + while (off > 0) { + subtype = concat_subtype_lookup(dtype->type, --off); + + expr = constant_expr_splice(data, subtype->size); + expr->dtype = subtype; + expr->byteorder = subtype->byteorder; + + if (expr->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + if (expr->dtype->basetype != NULL && + expr->dtype->basetype->type == TYPE_BITMASK) + expr = bitmask_expr_to_binops(expr); + + compound_expr_add(concat, expr); + data->len -= netlink_padding_len(expr->len); + } + expr_free(data); + + return concat; +} + static int netlink_delinearize_setelem(struct nft_set_elem *nlse, struct set *set) { @@ -1398,6 +1428,8 @@ static int netlink_delinearize_setelem(struct nft_set_elem *nlse, key = netlink_alloc_value(&netlink_location, &nld); key->dtype = set->keytype; key->byteorder = set->keytype->byteorder; + if (set->keytype->subtypes) + key = netlink_parse_concat_elem(set->keytype, key); if (!(set->flags & SET_F_INTERVAL) && key->byteorder == BYTEORDER_HOST_ENDIAN) -- cgit v1.2.3