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@xxxxxxxxxx> --- src/netlink_delinearize.c | 85 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 4036646d57ac..e7042d6ae940 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -2862,7 +2862,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; @@ -2916,6 +2916,67 @@ 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 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, &ctx->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; @@ -2983,21 +3044,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) @@ -3020,7 +3090,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; -- 2.35.1