@@ -3112,7 +3112,7 @@ static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop)
}
}
-static bool stmt_payload_binop_postprocess_i(struct rule_pp_ctx *ctx)
+static bool stmt_payload_binop_postprocess_i_a(struct rule_pp_ctx *ctx)
{
struct expr *expr, *binop, *payload, *value, *mask;
struct stmt *stmt = ctx->stmt;
@@ -3166,6 +3166,68 @@ static bool stmt_payload_binop_postprocess_i(struct rule_pp_ctx *ctx)
return true;
}
+static bool stmt_payload_binop_postprocess_i_b(struct rule_pp_ctx *ctx)
+{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
+ struct expr *expr, *payload, *mask, *xor;
+ struct stmt *stmt = ctx->stmt;
+ unsigned int shift;
+ mpz_t tmp, bitmask;
+
+ expr = stmt->payload.val;
+
+ if (expr->op != OP_XOR)
+ return false;
+
+ if (expr->left->etype != EXPR_BINOP)
+ return false;
+
+ if (expr->left->op != OP_AND)
+ return false;
+
+ if (expr->right->etype == EXPR_UNARY) {
+ /*
+ * If the payload value was originally in a different byte-order
+ * from the payload expression, there will be a byte-order
+ * conversion to remove.
+ */
+ xor = expr_get(expr->right->arg);
+ expr_free(expr->right);
+ expr->right = xor;
+ } else
+ xor = expr->right;
+
+ mask = expr->left->right;
+ payload = expr->left->left;
+
+ mpz_init(tmp);
+ mpz_set(tmp, mask->value);
+
+ mpz_init_bitmask(bitmask, payload->len);
+ mpz_xor(bitmask, bitmask, mask->value);
+ mpz_set(mask->value, bitmask);
+ mpz_clear(bitmask);
+
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift))
+ payload_match_postprocess(ctx, expr->left, payload);
+
+ if (!payload_is_known(payload)) {
+ mpz_set(mask->value, tmp);
+ } else {
+ if (shift) {
+ expr->right = expr_get(xor->left);
+ expr_free(xor);
+ }
+ expr_free(stmt->payload.expr);
+ stmt->payload.expr = expr_get(payload);
+ stmt->payload.val = expr_get(expr->right);
+ expr_free(expr);
+ }
+
+ mpz_clear(tmp);
+ return true;
+}
+
static bool stmt_payload_binop_postprocess_ii(struct rule_pp_ctx *ctx)
{
struct expr *expr, *payload, *value;
@@ -3233,21 +3295,30 @@ static bool stmt_payload_binop_postprocess_ii(struct rule_pp_ctx *ctx)
* and a mask to clear the real payload offset/length.
*
* So check if we have one of the following binops:
- * I)
+ *
+ * Ia)
* binop (|)
* binop(&) value/set
* payload value(mask)
*
- * This is the normal case, the | RHS is the value the user wants
- * to set, the & RHS is the mask value that discards bits we need
+ * This is the normal constant case, the | RHS is the value the user
+ * wants to set, the & RHS is the mask value that discards bits we need
* to clear but retains everything unrelated to the set operation.
*
+ * Ib)
+ * binop (^)
+ * binop(&) value/set
+ * payload value(mask)
+ *
+ * The user wants to set a variable payload argument. The ^ RHS is the
+ * variable expression. The mask is as above.
+ *
* IIa)
* binop (&)
* payload mask
*
* User specified a zero set value -- netlink bitwise decoding
- * discarded the redundant "| 0" part. This is identical to I),
+ * discarded the redundant "| 0" part. This is identical to Ia),
* we can just set value to 0 after we inferred the real payload size.
*
* IIb)
@@ -3270,7 +3341,10 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
switch (expr->left->etype) {
case EXPR_BINOP: /* I? */
- if (stmt_payload_binop_postprocess_i(ctx))
+ if (stmt_payload_binop_postprocess_i_a(ctx))
+ return;
+
+ if (stmt_payload_binop_postprocess_i_b(ctx))
return;
break;
If a user uses a variable payload expression in a payload statement, the structure of the statement value is not handled by the existing statement postprocessing function, so we need to extend it. Signed-off-by: Jeremy Sowden <jeremy@azazel.net> --- src/netlink_delinearize.c | 86 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 6 deletions(-)