Skip to content

Commit 10fdd6d

Browse files
Phil Sutterummakynes
authored andcommitted
netfilter: nf_tables: Implement fast bitwise expression
A typical use of bitwise expression is to mask out parts of an IP address when matching on the network part only. Optimize for this common use with a fast variant for NFT_BITWISE_BOOL-type expressions operating on 32bit-sized values. Signed-off-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 5f48846 commit 10fdd6d

File tree

3 files changed

+156
-6
lines changed

3 files changed

+156
-6
lines changed

include/net/netfilter/nf_tables_core.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ extern struct nft_object_type nft_secmark_obj_type;
2323
int nf_tables_core_module_init(void);
2424
void nf_tables_core_module_exit(void);
2525

26+
struct nft_bitwise_fast_expr {
27+
u32 mask;
28+
u32 xor;
29+
enum nft_registers sreg:8;
30+
enum nft_registers dreg:8;
31+
};
32+
2633
struct nft_cmp_fast_expr {
2734
u32 data;
2835
u32 mask;
@@ -68,6 +75,8 @@ struct nft_payload_set {
6875

6976
extern const struct nft_expr_ops nft_payload_fast_ops;
7077

78+
extern const struct nft_expr_ops nft_bitwise_fast_ops;
79+
7180
extern struct static_key_false nft_counters_enabled;
7281
extern struct static_key_false nft_trace_enabled;
7382

net/netfilter/nf_tables_core.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ static inline void nft_trace_packet(struct nft_traceinfo *info,
4747
}
4848
}
4949

50+
static void nft_bitwise_fast_eval(const struct nft_expr *expr,
51+
struct nft_regs *regs)
52+
{
53+
const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr);
54+
u32 *src = &regs->data[priv->sreg];
55+
u32 *dst = &regs->data[priv->dreg];
56+
57+
*dst = (*src & priv->mask) ^ priv->xor;
58+
}
59+
5060
static void nft_cmp_fast_eval(const struct nft_expr *expr,
5161
struct nft_regs *regs)
5262
{
@@ -175,6 +185,8 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
175185
nft_rule_for_each_expr(expr, last, rule) {
176186
if (expr->ops == &nft_cmp_fast_ops)
177187
nft_cmp_fast_eval(expr, &regs);
188+
else if (expr->ops == &nft_bitwise_fast_ops)
189+
nft_bitwise_fast_eval(expr, &regs);
178190
else if (expr->ops != &nft_payload_fast_ops ||
179191
!nft_payload_fast_eval(expr, &regs, pkt))
180192
expr_call_ops_eval(expr, &regs, pkt);

net/netfilter/nft_bitwise.c

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,6 @@ static int nft_bitwise_init(const struct nft_ctx *ctx,
163163
u32 len;
164164
int err;
165165

166-
if (!tb[NFTA_BITWISE_SREG] ||
167-
!tb[NFTA_BITWISE_DREG] ||
168-
!tb[NFTA_BITWISE_LEN])
169-
return -EINVAL;
170-
171166
err = nft_parse_u32_check(tb[NFTA_BITWISE_LEN], U8_MAX, &len);
172167
if (err < 0)
173168
return err;
@@ -292,9 +287,143 @@ static const struct nft_expr_ops nft_bitwise_ops = {
292287
.offload = nft_bitwise_offload,
293288
};
294289

290+
static int
291+
nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out)
292+
{
293+
struct nft_data_desc desc;
294+
struct nft_data data;
295+
int err = 0;
296+
297+
err = nft_data_init(NULL, &data, sizeof(data), &desc, tb);
298+
if (err < 0)
299+
return err;
300+
301+
if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) {
302+
err = -EINVAL;
303+
goto err;
304+
}
305+
*out = data.data[0];
306+
err:
307+
nft_data_release(&data, desc.type);
308+
return err;
309+
}
310+
311+
static int nft_bitwise_fast_init(const struct nft_ctx *ctx,
312+
const struct nft_expr *expr,
313+
const struct nlattr * const tb[])
314+
{
315+
struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr);
316+
int err;
317+
318+
priv->sreg = nft_parse_register(tb[NFTA_BITWISE_SREG]);
319+
err = nft_validate_register_load(priv->sreg, sizeof(u32));
320+
if (err < 0)
321+
return err;
322+
323+
priv->dreg = nft_parse_register(tb[NFTA_BITWISE_DREG]);
324+
err = nft_validate_register_store(ctx, priv->dreg, NULL,
325+
NFT_DATA_VALUE, sizeof(u32));
326+
if (err < 0)
327+
return err;
328+
329+
if (tb[NFTA_BITWISE_DATA])
330+
return -EINVAL;
331+
332+
if (!tb[NFTA_BITWISE_MASK] ||
333+
!tb[NFTA_BITWISE_XOR])
334+
return -EINVAL;
335+
336+
err = nft_bitwise_extract_u32_data(tb[NFTA_BITWISE_MASK], &priv->mask);
337+
if (err < 0)
338+
return err;
339+
340+
err = nft_bitwise_extract_u32_data(tb[NFTA_BITWISE_XOR], &priv->xor);
341+
if (err < 0)
342+
return err;
343+
344+
return 0;
345+
}
346+
347+
static int
348+
nft_bitwise_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
349+
{
350+
const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr);
351+
struct nft_data data;
352+
353+
if (nft_dump_register(skb, NFTA_BITWISE_SREG, priv->sreg))
354+
return -1;
355+
if (nft_dump_register(skb, NFTA_BITWISE_DREG, priv->dreg))
356+
return -1;
357+
if (nla_put_be32(skb, NFTA_BITWISE_LEN, htonl(sizeof(u32))))
358+
return -1;
359+
if (nla_put_be32(skb, NFTA_BITWISE_OP, htonl(NFT_BITWISE_BOOL)))
360+
return -1;
361+
362+
data.data[0] = priv->mask;
363+
if (nft_data_dump(skb, NFTA_BITWISE_MASK, &data,
364+
NFT_DATA_VALUE, sizeof(u32)) < 0)
365+
return -1;
366+
367+
data.data[0] = priv->xor;
368+
if (nft_data_dump(skb, NFTA_BITWISE_XOR, &data,
369+
NFT_DATA_VALUE, sizeof(u32)) < 0)
370+
return -1;
371+
372+
return 0;
373+
}
374+
375+
static int nft_bitwise_fast_offload(struct nft_offload_ctx *ctx,
376+
struct nft_flow_rule *flow,
377+
const struct nft_expr *expr)
378+
{
379+
const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr);
380+
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
381+
382+
if (priv->xor || priv->sreg != priv->dreg || reg->len != sizeof(u32))
383+
return -EOPNOTSUPP;
384+
385+
reg->mask.data[0] = priv->mask;
386+
return 0;
387+
}
388+
389+
const struct nft_expr_ops nft_bitwise_fast_ops = {
390+
.type = &nft_bitwise_type,
391+
.size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise_fast_expr)),
392+
.eval = NULL, /* inlined */
393+
.init = nft_bitwise_fast_init,
394+
.dump = nft_bitwise_fast_dump,
395+
.offload = nft_bitwise_fast_offload,
396+
};
397+
398+
static const struct nft_expr_ops *
399+
nft_bitwise_select_ops(const struct nft_ctx *ctx,
400+
const struct nlattr * const tb[])
401+
{
402+
int err;
403+
u32 len;
404+
405+
if (!tb[NFTA_BITWISE_LEN] ||
406+
!tb[NFTA_BITWISE_SREG] ||
407+
!tb[NFTA_BITWISE_DREG])
408+
return ERR_PTR(-EINVAL);
409+
410+
err = nft_parse_u32_check(tb[NFTA_BITWISE_LEN], U8_MAX, &len);
411+
if (err < 0)
412+
return ERR_PTR(err);
413+
414+
if (len != sizeof(u32))
415+
return &nft_bitwise_ops;
416+
417+
if (tb[NFTA_BITWISE_OP] &&
418+
ntohl(nla_get_be32(tb[NFTA_BITWISE_OP])) != NFT_BITWISE_BOOL)
419+
return &nft_bitwise_ops;
420+
421+
return &nft_bitwise_fast_ops;
422+
}
423+
295424
struct nft_expr_type nft_bitwise_type __read_mostly = {
296425
.name = "bitwise",
297-
.ops = &nft_bitwise_ops,
426+
.select_ops = nft_bitwise_select_ops,
298427
.policy = nft_bitwise_policy,
299428
.maxattr = NFTA_BITWISE_MAX,
300429
.owner = THIS_MODULE,

0 commit comments

Comments
 (0)