summaryrefslogtreecommitdiffstats
path: root/src/netlink_delinearize.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r--src/netlink_delinearize.c222
1 files changed, 154 insertions, 68 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 08fd5808..9ee43ba4 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -865,6 +865,7 @@ struct rule_pp_ctx {
struct proto_ctx pctx;
enum proto_bases pbase;
struct stmt *pdep;
+ struct stmt *stmt;
};
/*
@@ -908,53 +909,66 @@ static void integer_type_postprocess(struct expr *expr)
integer_type_postprocess(i);
}
break;
+ case EXPR_SET_ELEM:
+ expr_set_type(expr->key, expr->dtype, expr->byteorder);
+ integer_type_postprocess(expr->key);
+ break;
default:
break;
}
}
-static void payload_match_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt, struct expr *expr)
+static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
{
struct expr *left = expr->left, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
struct stmt *nstmt;
struct expr *nexpr;
+ payload_expr_expand(&list, left, &ctx->pctx);
+ list_for_each_entry(left, &list, list) {
+ tmp = constant_expr_splice(right, left->len);
+ expr_set_type(tmp, left->dtype, left->byteorder);
+ if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
+
+ nexpr = relational_expr_alloc(&expr->location, expr->op,
+ left, tmp);
+ if (expr->op == OP_EQ)
+ left->ops->pctx_update(&ctx->pctx, nexpr);
+
+ nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
+ list_add_tail(&nstmt->list, &ctx->stmt->list);
+
+ /* Remember the first payload protocol expression to
+ * kill it later on if made redundant by a higher layer
+ * payload expression.
+ */
+ if (ctx->pbase == PROTO_BASE_INVALID &&
+ left->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(ctx, nstmt,
+ left->payload.base);
+ else
+ payload_dependency_kill(ctx, nexpr->left);
+ }
+ list_del(&ctx->stmt->list);
+ stmt_free(ctx->stmt);
+ ctx->stmt = NULL;
+}
+
+static void payload_match_postprocess(struct rule_pp_ctx *ctx,
+ struct expr *expr)
+{
switch (expr->op) {
case OP_EQ:
case OP_NEQ:
- payload_expr_expand(&list, left, &ctx->pctx);
- list_for_each_entry(left, &list, list) {
- tmp = constant_expr_splice(right, left->len);
- expr_set_type(tmp, left->dtype, left->byteorder);
- if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
-
- nexpr = relational_expr_alloc(&expr->location, expr->op,
- left, tmp);
- if (expr->op == OP_EQ)
- left->ops->pctx_update(&ctx->pctx, nexpr);
-
- nstmt = expr_stmt_alloc(&stmt->location, nexpr);
- list_add_tail(&nstmt->list, &stmt->list);
-
- /* Remember the first payload protocol expression to
- * kill it later on if made redundant by a higher layer
- * payload expression.
- */
- if (ctx->pbase == PROTO_BASE_INVALID &&
- left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(ctx, nstmt,
- left->payload.base);
- else
- payload_dependency_kill(ctx, nexpr->left);
+ if (expr->right->ops->type == EXPR_VALUE) {
+ payload_match_expand(ctx, expr);
+ break;
}
- list_del(&stmt->list);
- stmt_free(stmt);
- break;
+ /* Fall through */
default:
- payload_expr_complete(left, &ctx->pctx);
+ payload_expr_complete(expr->left, &ctx->pctx);
expr_set_type(expr->right, expr->left->dtype,
expr->left->byteorder);
payload_dependency_kill(ctx, expr->left);
@@ -963,7 +977,6 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
static void meta_match_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt,
const struct expr *expr)
{
struct expr *left = expr->left;
@@ -974,7 +987,8 @@ static void meta_match_postprocess(struct rule_pp_ctx *ctx,
if (ctx->pbase == PROTO_BASE_INVALID &&
left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(ctx, stmt, left->meta.base);
+ payload_dependency_store(ctx, ctx->stmt,
+ left->meta.base);
break;
case OP_LOOKUP:
expr_set_type(expr->right, expr->left->dtype,
@@ -1058,8 +1072,7 @@ static void relational_binop_postprocess(struct expr *expr)
}
}
-static void expr_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt, struct expr **exprp)
+static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp, *i;
@@ -1067,16 +1080,16 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
switch (expr->ops->type) {
case EXPR_MAP:
- expr_postprocess(ctx, stmt, &expr->map);
- expr_postprocess(ctx, stmt, &expr->mappings);
+ expr_postprocess(ctx, &expr->map);
+ expr_postprocess(ctx, &expr->mappings);
break;
case EXPR_MAPPING:
- expr_postprocess(ctx, stmt, &expr->left);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->left);
+ expr_postprocess(ctx, &expr->right);
break;
case EXPR_SET:
list_for_each_entry(i, &expr->expressions, list)
- expr_postprocess(ctx, stmt, &i);
+ expr_postprocess(ctx, &i);
break;
case EXPR_CONCAT: {
unsigned int type = expr->dtype->type, ntype = 0;
@@ -1096,17 +1109,17 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
break;
}
case EXPR_UNARY:
- expr_postprocess(ctx, stmt, &expr->arg);
+ expr_postprocess(ctx, &expr->arg);
expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder);
*exprp = expr_get(expr->arg);
expr_free(expr);
break;
case EXPR_BINOP:
- expr_postprocess(ctx, stmt, &expr->left);
+ expr_postprocess(ctx, &expr->left);
expr_set_type(expr->right, expr->left->dtype,
expr->left->byteorder);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->right);
expr_set_type(expr, expr->left->dtype,
expr->left->byteorder);
@@ -1114,19 +1127,19 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
case EXPR_RELATIONAL:
switch (expr->left->ops->type) {
case EXPR_PAYLOAD:
- payload_match_postprocess(ctx, stmt, expr);
+ payload_match_postprocess(ctx, expr);
return;
default:
- expr_postprocess(ctx, stmt, &expr->left);
+ expr_postprocess(ctx, &expr->left);
break;
}
expr_set_type(expr->right, expr->left->dtype, expr->left->byteorder);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->right);
switch (expr->left->ops->type) {
case EXPR_META:
- meta_match_postprocess(ctx, stmt, expr);
+ meta_match_postprocess(ctx, expr);
break;
case EXPR_BINOP:
relational_binop_postprocess(expr);
@@ -1167,11 +1180,11 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
break;
case EXPR_RANGE:
- expr_postprocess(ctx, stmt, &expr->left);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->left);
+ expr_postprocess(ctx, &expr->right);
break;
case EXPR_SET_ELEM:
- expr_postprocess(ctx, stmt, &expr->key);
+ expr_postprocess(ctx, &expr->key);
break;
case EXPR_SET_REF:
case EXPR_EXTHDR:
@@ -1184,18 +1197,19 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
}
}
-static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
+static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
{
const struct proto_desc *desc, *base;
+ struct stmt *stmt = rctx->stmt;
int protocol;
- switch (rctx.pctx.family) {
+ switch (rctx->pctx.family) {
case NFPROTO_IPV4:
- stmt->reject.family = rctx.pctx.family;
+ stmt->reject.family = rctx->pctx.family;
stmt->reject.expr->dtype = &icmp_code_type;
break;
case NFPROTO_IPV6:
- stmt->reject.family = rctx.pctx.family;
+ stmt->reject.family = rctx->pctx.family;
stmt->reject.expr->dtype = &icmpv6_code_type;
break;
case NFPROTO_INET:
@@ -1203,8 +1217,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
stmt->reject.expr->dtype = &icmpx_code_type;
break;
}
- base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -1221,8 +1235,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
stmt->reject.expr->dtype = &icmpx_code_type;
break;
}
- base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -1242,47 +1256,119 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
}
}
+static bool expr_may_merge_range(struct expr *expr, struct expr *prev,
+ enum ops *op)
+{
+ struct expr *left, *prev_left;
+
+ if (prev->ops->type == EXPR_RELATIONAL &&
+ expr->ops->type == EXPR_RELATIONAL) {
+ /* ct and meta needs an unary to swap byteorder, in this case
+ * we have to explore the inner branch in this tree.
+ */
+ if (expr->left->ops->type == EXPR_UNARY)
+ left = expr->left->arg;
+ else
+ left = expr->left;
+
+ if (prev->left->ops->type == EXPR_UNARY)
+ prev_left = prev->left->arg;
+ else
+ prev_left = prev->left;
+
+ if (left->ops->type == prev_left->ops->type) {
+ if (expr->op == OP_LTE && prev->op == OP_GTE) {
+ *op = OP_EQ;
+ return true;
+ } else if (expr->op == OP_GT && prev->op == OP_LT) {
+ *op = OP_NEQ;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void expr_postprocess_range(struct rule_pp_ctx *ctx, struct stmt *prev,
+ enum ops op)
+{
+ struct stmt *nstmt, *stmt = ctx->stmt;
+ struct expr *nexpr, *rel;
+
+ nexpr = range_expr_alloc(&prev->location, expr_clone(prev->expr->right),
+ expr_clone(stmt->expr->right));
+ expr_set_type(nexpr, stmt->expr->right->dtype,
+ stmt->expr->right->byteorder);
+
+ rel = relational_expr_alloc(&prev->location, op,
+ expr_clone(stmt->expr->left), nexpr);
+
+ nstmt = expr_stmt_alloc(&stmt->location, rel);
+ list_add_tail(&nstmt->list, &stmt->list);
+
+ list_del(&prev->list);
+ stmt_free(prev);
+
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ ctx->stmt = nstmt;
+}
+
+static void stmt_expr_postprocess(struct rule_pp_ctx *ctx, struct stmt *prev)
+{
+ enum ops op;
+
+ if (prev && ctx->stmt->ops->type == prev->ops->type &&
+ expr_may_merge_range(ctx->stmt->expr, prev->expr, &op))
+ expr_postprocess_range(ctx, prev, op);
+
+ expr_postprocess(ctx, &ctx->stmt->expr);
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct rule_pp_ctx rctx;
- struct stmt *stmt, *next;
+ struct stmt *stmt, *next, *prev = NULL;
memset(&rctx, 0, sizeof(rctx));
proto_ctx_init(&rctx.pctx, rule->handle.family);
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+ rctx.stmt = stmt;
+
switch (stmt->ops->type) {
case STMT_EXPRESSION:
- expr_postprocess(&rctx, stmt, &stmt->expr);
+ stmt_expr_postprocess(&rctx, prev);
break;
case STMT_META:
if (stmt->meta.expr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->meta.expr);
+ expr_postprocess(&rctx, &stmt->meta.expr);
break;
case STMT_CT:
if (stmt->ct.expr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->ct.expr);
+ expr_postprocess(&rctx, &stmt->ct.expr);
break;
case STMT_NAT:
if (stmt->nat.addr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->nat.addr);
+ expr_postprocess(&rctx, &stmt->nat.addr);
if (stmt->nat.proto != NULL)
- expr_postprocess(&rctx, stmt, &stmt->nat.proto);
+ expr_postprocess(&rctx, &stmt->nat.proto);
break;
case STMT_REDIR:
if (stmt->redir.proto != NULL)
- expr_postprocess(&rctx, stmt,
- &stmt->redir.proto);
+ expr_postprocess(&rctx, &stmt->redir.proto);
break;
case STMT_REJECT:
- stmt_reject_postprocess(rctx, stmt);
+ stmt_reject_postprocess(&rctx);
break;
case STMT_SET:
- expr_postprocess(&rctx, stmt, &stmt->set.key);
+ expr_postprocess(&rctx, &stmt->set.key);
break;
default:
break;
}
+ prev = rctx.stmt;
}
}