summaryrefslogtreecommitdiffstats
path: root/src/netlink_delinearize.c
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2015-06-13 14:48:56 +0200
committerPatrick McHardy <kaber@trash.net>2015-06-13 14:48:56 +0200
commit077509fdea3aa009823491be7096749c84795874 (patch)
tree07287fb0971df4011b33135d25ef8b0c0848e099 /src/netlink_delinearize.c
parent45cabc474e46c74c27b645582d37a55d5d076051 (diff)
parenta93bc1795b272174a10d90961a248f2c620bfa2c (diff)
Merge remote-tracking branch 'origin/next-4.1'
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r--src/netlink_delinearize.c130
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);