bool payload_expr_trim(struct expr *expr, struct expr *mask,
const struct proto_ctx *ctx, unsigned int *shift);
+bool payload_expr_trim_force(struct expr *expr, struct expr *mask,
+ unsigned int *shift);
extern void payload_expr_expand(struct list_head *list, struct expr *expr,
const struct proto_ctx *ctx);
extern void payload_expr_complete(struct expr *expr,
}
}
-static void __binop_postprocess(struct rule_pp_ctx *ctx,
+static bool __binop_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *left,
struct expr *mask,
expr_set_type(right, left->dtype, left->byteorder);
expr_free(binop);
+ return true;
+ } else if (left->etype == EXPR_PAYLOAD &&
+ expr->right->etype == EXPR_VALUE &&
+ payload_expr_trim_force(left, mask, &shift)) {
+ mpz_rshift_ui(expr->right->value, shift);
+ *expr_binop = expr_get(left);
+ expr_free(binop);
+ return true;
}
+
+ return false;
}
-static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
+static bool binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
struct expr **expr_binop)
{
struct expr *binop = *expr_binop;
struct expr *left = binop->left;
struct expr *mask = binop->right;
- __binop_postprocess(ctx, expr, left, mask, expr_binop);
+ return __binop_postprocess(ctx, expr, left, mask, expr_binop);
}
static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
switch (expr->left->etype) {
case EXPR_BINOP: {/* I? */
+ unsigned int shift = 0;
mpz_t tmp;
if (expr->op != OP_OR)
mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- binop_postprocess(ctx, expr, &expr->left);
- if (!payload_is_known(payload)) {
+ if (!binop_postprocess(ctx, expr, &expr->left) &&
+ !payload_is_known(payload) &&
+ !payload_expr_trim_force(payload,
+ mask, &shift)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
return;
}
+ if (shift)
+ mpz_rshift_ui(value->value, shift);
+
mpz_clear(tmp);
expr_free(stmt->payload.expr);
stmt->payload.expr = expr_get(payload);
switch (expr->op) {
case OP_AND: { /* IIa */
+ unsigned int shift_unused;
mpz_t tmp;
mpz_init(tmp);
mpz_clear(bitmask);
stmt_payload_binop_pp(ctx, expr);
- if (!payload_is_known(expr->left)) {
+ if (!payload_is_known(expr->left) &&
+ !payload_expr_trim_force(expr->left, mask, &shift_unused)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
return;
* clear the payload expression.
* The "mask" value becomes new stmt->payload.value
* so set this to 0.
+ * Also the reason why &shift_unused is ignored.
*/
mpz_set_ui(mask->value, 0);
break;
* @expr: the payload expression
* @mask: mask to use when searching templates
* @ctx: protocol context
+ * @shift: shift adjustment to fix up RHS value
*
* Walk the template list and determine if a match can be found without
* using the provided mask.
return false;
}
+/**
+ * payload_expr_trim_force - adjust payload len/offset according to mask
+ *
+ * @expr: the payload expression
+ * @mask: mask to use when searching templates
+ * @shift: shift adjustment to fix up RHS value
+ *
+ * Force-trim an unknown payload expression according to mask.
+ *
+ * This is only useful for unkown payload expressions that need
+ * to be printed in raw syntax (@base,offset,length). The kernel
+ * can only deal with byte-divisible offsets/length, e.g. @th,16,8.
+ * In such case we might be able to get rid of the mask.
+ * @base,offset,length & MASK OPERATOR VALUE then becomes
+ * @base,offset,length VALUE, where at least one of offset increases
+ * and length decreases.
+ *
+ * This function also returns the shift for the right hand
+ * constant side of the expression.
+ *
+ * @return: true if @expr was adjusted and mask can be discarded.
+ */
+bool payload_expr_trim_force(struct expr *expr, struct expr *mask, unsigned int *shift)
+{
+ unsigned int payload_offset = expr->payload.offset;
+ unsigned int mask_len = mask_length(mask);
+ unsigned int off, real_len;
+
+ if (payload_is_known(expr) || expr->len <= mask_len)
+ return false;
+
+ /* This provides the payload offset to use.
+ * mask->len is the total length of the mask, e.g. 16.
+ * mask_len holds the last bit number that will be zeroed,
+ */
+ off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
+ payload_offset += off;
+
+ /* kernel only allows offsets <= 255 */
+ if (round_up(payload_offset, BITS_PER_BYTE) > 255)
+ return false;
+
+ real_len = mpz_popcount(mask->value);
+ if (real_len > expr->len)
+ return false;
+
+ expr->payload.offset = payload_offset;
+ expr->len = real_len;
+
+ *shift = mask_to_offset(mask);
+ return true;
+}
+
/**
* payload_expr_expand - expand raw merged adjacent payload expressions into its
* original components