This allows to coalesce payload + bitwise + cmp expression into one combo expression. This does not require updates on the datapath, the existing mask field is adjusted based on the bitwise mask. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- v2: call nft_expr_track_reset_dreg() to reset register that has been combo'ed in the register bitmap. include/net/netfilter/nf_tables.h | 11 +++++++++-- include/net/netfilter/nf_tables_core.h | 3 ++- net/netfilter/nf_tables_api.c | 26 +++++++++++++++++++++++++- net/netfilter/nft_bitwise.c | 16 +++++++++++++++- net/netfilter/nft_payload.c | 11 ++++++++--- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 51bb7e295222..28c12fb870d9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -156,6 +156,7 @@ enum nft_expr_track_type { NFT_EXPR_UNSET = 0, NFT_EXPR_PAYLOAD, NFT_EXPR_META, + NFT_EXPR_BITWISE, NFT_EXPR_CMP }; @@ -182,6 +183,12 @@ struct nft_expr_track { bool inv; struct nft_data data; } cmp; + struct { + u8 sreg; + u8 dreg; + u8 len; + struct nft_data mask; + } bitwise; }; }; @@ -214,8 +221,8 @@ struct nft_combo_expr { struct nft_exprs_track { int num_exprs; - struct nft_expr_track expr[2]; - const struct nft_expr *saved_expr[2]; + struct nft_expr_track expr[3]; + const struct nft_expr *saved_expr[3]; struct nft_combo_expr _expr; }; diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 1e101f66b26b..419ae7a2d413 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -165,7 +165,8 @@ void nft_objref_eval(const struct nft_expr *expr, struct nft_regs *regs, void nft_objref_map_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt); -void nft_payload_combo_init(struct nft_expr *expr, struct nft_exprs_track *track); +void nft_payload_combo_init(struct nft_expr *expr, struct nft_exprs_track *track, + bool bitmask); void nft_meta_combo_init(struct nft_expr *expr, struct nft_exprs_track *track); #endif /* _NET_NF_TABLES_CORE_H */ diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 007d184bd684..dc0b5e1ada16 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -8942,7 +8942,7 @@ static int nft_expr_track(struct nft_expr_track_ctx *ctx, expr_track->expr[0].payload.dreg == expr_track->expr[1].cmp.sreg && expr_track->expr[0].payload.len == expr_track->expr[1].cmp.len) { nft_expr_track_reset_dreg(ctx, expr_track->expr[0].payload.dreg, expr_track->expr[0].payload.len); - nft_payload_combo_init((struct nft_expr *)&expr_track->_expr, expr_track); + nft_payload_combo_init((struct nft_expr *)&expr_track->_expr, expr_track, false); expr_track->saved_expr[0] = (struct nft_expr *)&expr_track->_expr; expr_track->num_exprs = 1; return 1; @@ -8962,6 +8962,30 @@ static int nft_expr_track(struct nft_expr_track_ctx *ctx, } } break; + case NFT_EXPR_BITWISE: + if (expr_track->expr[0].type == NFT_EXPR_PAYLOAD && + expr_track->expr[0].payload.dreg == expr_track->expr[1].bitwise.sreg && + expr_track->expr[0].payload.len >= expr_track->expr[1].bitwise.len) + return 0; + break; + default: + return 1; + } + break; + case 2: + switch (expr_track->expr[0].type) { + case NFT_EXPR_PAYLOAD: + if (expr_track->expr[1].type == NFT_EXPR_BITWISE && + expr_track->expr[2].type == NFT_EXPR_CMP && + expr_track->expr[1].bitwise.dreg == expr_track->expr[2].cmp.sreg && + expr_track->expr[1].bitwise.len >= expr_track->expr[2].cmp.len) { + nft_expr_track_reset_dreg(ctx, expr_track->expr[0].payload.dreg, expr_track->expr[0].payload.len); + nft_payload_combo_init((struct nft_expr *)&expr_track->_expr, expr_track, true); + expr_track->saved_expr[0] = (struct nft_expr *)&expr_track->_expr; + expr_track->num_exprs = 1; + return 1; + } + break; default: break; } diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 0ab2d281f245..a263f8ca59c5 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -271,7 +271,21 @@ static int nft_bitwise_track(struct nft_expr_track_ctx *ctx, nft_expr_track_sreg(ctx, priv->sreg, priv->len); nft_expr_track_dreg(ctx, priv->dreg, priv->len); - return 1; + if (priv->op != NFT_BITWISE_BOOL) + return 1; + + if (memcmp(&priv->xor, &zero, sizeof(priv->xor))) + return 1; + + if (priv->sreg != priv->dreg) + return 1; + + track->type = NFT_EXPR_BITWISE; + track->bitwise.sreg = priv->sreg; + track->bitwise.dreg = priv->dreg; + memcpy(&track->bitwise.mask, &priv->mask, sizeof(priv->mask)); + + return 0; } static int nft_bitwise_offload(struct nft_offload_ctx *ctx, diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 0169ced11704..8d1d4df2f90a 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -1051,17 +1051,22 @@ void nft_cmp16_mask(struct nft_data *data, unsigned int len, bool be) data->data[i] = 0; } -void nft_payload_combo_init(struct nft_expr *expr, struct nft_exprs_track *track) +void nft_payload_combo_init(struct nft_expr *expr, + struct nft_exprs_track *track, bool bitmask) { struct nft_payload_combo *priv = nft_expr_priv(expr); + int index = bitmask ? 2 : 1; expr->ops = &nft_payload_combo_ops; priv->base = track->expr[0].payload.base; priv->offset = track->expr[0].payload.offset; priv->len = track->expr[0].payload.len; - memcpy(&priv->data, &track->expr[1].cmp.data, sizeof(priv->data)); - priv->inv = track->expr[1].cmp.inv; + memcpy(&priv->data, &track->expr[index].cmp.data, sizeof(priv->data)); + priv->inv = track->expr[index].cmp.inv; nft_cmp16_mask(&priv->mask, track->expr[1].cmp.len, true); + + if (bitmask) + priv->mask.data[0] &= track->expr[1].bitwise.mask.data[0]; } const struct nft_expr_ops nft_payload_combo_ops = { -- 2.30.2