summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/proto.h4
-rw-r--r--src/evaluate.c78
-rw-r--r--src/netlink_delinearize.c47
-rw-r--r--src/proto.c2
4 files changed, 121 insertions, 10 deletions
diff --git a/include/proto.h b/include/proto.h
index 0e531b24..a43bf98b 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -69,6 +69,7 @@ struct proto_hdr_template {
* @name: protocol name
* @base: header base
* @protocol_key: key of template containing upper layer protocol description
+ * @length: total size of the header, in bits
* @protocols: link to upper layer protocol descriptions indexed by protocol value
* @templates: header templates
*/
@@ -76,6 +77,7 @@ struct proto_desc {
const char *name;
enum proto_bases base;
unsigned int protocol_key;
+ unsigned int length;
struct {
unsigned int num;
const struct proto_desc *desc;
@@ -122,6 +124,7 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
* @family: hook family
* @location: location of the relational expression defining the context
* @desc: protocol description for this layer
+ * @offset: offset from the base, for stacked headers (eg 8*14 for vlan on top of ether)
*
* The location of the context is the location of the relational expression
* defining it, either directly through a protocol match or indirectly
@@ -132,6 +135,7 @@ struct proto_ctx {
struct {
struct location location;
const struct proto_desc *desc;
+ unsigned int offset;
} protocol[PROTO_BASE_MAX + 1];
};
diff --git a/src/evaluate.c b/src/evaluate.c
index 22dd6d56..95469180 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -249,6 +249,82 @@ static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static int
+conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
+ const struct expr *expr,
+ struct stmt **res)
+{
+ enum proto_bases base = expr->payload.base;
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc = NULL;
+ struct expr *dep, *left, *right;
+ struct stmt *stmt;
+
+ assert(expr->payload.base == PROTO_BASE_LL_HDR);
+
+ desc = ctx->pctx.protocol[base].desc;
+ tmpl = &desc->templates[desc->protocol_key];
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+ if (stmt_evaluate(ctx, stmt) < 0)
+ return expr_error(ctx->msgs, expr,
+ "dependency statement is invalid");
+
+ ctx->pctx.protocol[base].desc = expr->payload.desc;
+ assert(ctx->pctx.protocol[base].offset == 0);
+
+ assert(desc->length);
+ ctx->pctx.protocol[base].offset += desc->length;
+
+ *res = stmt;
+ return 0;
+}
+
+static bool resolve_protocol_conflict(struct eval_ctx *ctx,
+ struct expr *payload)
+{
+ const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
+ enum proto_bases base = payload->payload.base;
+ const struct proto_desc *desc;
+ struct stmt *nstmt = NULL;
+ int link;
+
+ desc = ctx->pctx.protocol[base].desc;
+
+ if (desc == payload->payload.desc) {
+ payload->payload.offset += ctx->pctx.protocol[base].offset;
+ return true;
+ }
+
+ if (payload->payload.base != h->base)
+ return false;
+
+ assert(desc->length);
+ if (base < PROTO_BASE_MAX) {
+ const struct proto_desc *next = ctx->pctx.protocol[base + 1].desc;
+
+ if (payload->payload.desc == next) {
+ payload->payload.offset += desc->length;
+ return true;
+ }
+ }
+
+ link = proto_find_num(desc, payload->payload.desc);
+ if (link < 0 || conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
+ return false;
+
+ payload->payload.offset += ctx->pctx.protocol[base].offset;
+ list_add_tail(&nstmt->list, &ctx->stmt->list);
+
+ return true;
+}
+
/*
* Payload expression: check whether dependencies are fulfilled, otherwise
* generate the necessary relational expression and prepend it to the current
@@ -264,7 +340,7 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **expr)
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
list_add_tail(&nstmt->list, &ctx->stmt->list);
- } else if (ctx->pctx.protocol[base].desc != payload->payload.desc)
+ } else if (!resolve_protocol_conflict(ctx, payload))
return expr_error(ctx->msgs, payload,
"conflicting protocols specified: %s vs. %s",
ctx->pctx.protocol[base].desc->name,
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 787eec76..af972032 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -923,7 +923,9 @@ 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;
+ struct expr *nexpr = NULL;
+ enum proto_bases base = left->payload.base;
+ const struct expr_ops *payload_ops = left->ops;
payload_expr_expand(&list, left, &ctx->pctx);
list_for_each_entry(left, &list, list) {
@@ -940,16 +942,37 @@ static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
list_add_tail(&nstmt->list, &ctx->stmt->list);
+ assert(left->ops == payload_ops);
+ assert(left->payload.base);
+ assert(base == left->payload.base);
+
/* 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
+ left->flags & EXPR_F_PROTOCOL) {
+ unsigned int proto = mpz_get_be16(tmp->value);
+ const struct proto_desc *desc, *next;
+ bool stacked_header = false;
+
+ desc = ctx->pctx.protocol[base].desc;
+ assert(desc);
+ if (desc) {
+ next = proto_find_upper(desc, proto);
+ stacked_header = next && next->base == base;
+ }
+
+ if (stacked_header) {
+ ctx->pctx.protocol[base].desc = next;
+ ctx->pctx.protocol[base].offset += desc->length;
+ payload_dependency_store(ctx, nstmt, base - 1);
+ } else {
+ payload_dependency_store(ctx, nstmt, base);
+ }
+ } else {
payload_dependency_kill(ctx, nexpr->left);
+ }
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
@@ -959,6 +982,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr)
{
+ enum proto_bases base = expr->left->payload.base;
+ struct expr *payload = expr->left;
+
+ assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
+ payload->payload.offset -= ctx->pctx.protocol[base].offset;
+
switch (expr->op) {
case OP_EQ:
case OP_NEQ:
@@ -968,10 +997,10 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Fall through */
default:
- payload_expr_complete(expr->left, &ctx->pctx);
- expr_set_type(expr->right, expr->left->dtype,
- expr->left->byteorder);
- payload_dependency_kill(ctx, expr->left);
+ payload_expr_complete(payload, &ctx->pctx);
+ expr_set_type(expr->right, payload->dtype,
+ payload->byteorder);
+ payload_dependency_kill(ctx, payload);
break;
}
}
diff --git a/src/proto.c b/src/proto.c
index d40caebc..b2aeac48 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -714,6 +714,7 @@ const struct proto_desc proto_vlan = {
.name = "vlan",
.base = PROTO_BASE_LL_HDR,
.protocol_key = VLANHDR_TYPE,
+ .length = sizeof(struct vlan_hdr) * BITS_PER_BYTE,
.protocols = {
PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
@@ -783,6 +784,7 @@ const struct proto_desc proto_eth = {
.name = "ether",
.base = PROTO_BASE_LL_HDR,
.protocol_key = ETHHDR_TYPE,
+ .length = sizeof(struct ether_header) * BITS_PER_BYTE,
.protocols = {
PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),