diff options
author | Patrick McHardy <kaber@trash.net> | 2015-06-13 14:48:56 +0200 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2015-06-13 14:48:56 +0200 |
commit | 077509fdea3aa009823491be7096749c84795874 (patch) | |
tree | 07287fb0971df4011b33135d25ef8b0c0848e099 /src/netlink_delinearize.c | |
parent | 45cabc474e46c74c27b645582d37a55d5d076051 (diff) | |
parent | a93bc1795b272174a10d90961a248f2c620bfa2c (diff) |
Merge remote-tracking branch 'origin/next-4.1'
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r-- | src/netlink_delinearize.c | 130 |
1 files changed, 117 insertions, 13 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 2865e8ed..9ee43ba4 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; } @@ -95,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) @@ -166,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); @@ -185,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) @@ -198,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)) { @@ -715,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); @@ -1004,6 +1091,23 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) list_for_each_entry(i, &expr->expressions, list) expr_postprocess(ctx, &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, &expr->arg); expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder); |