Skip to content

Commit c29b72e

Browse files
kaberummakynes
authored andcommitted
netfilter: nft_payload: add optimized payload implementation for small loads
Add an optimized payload expression implementation for small (up to 4 bytes) aligned data loads from the linear packet area. This patch also includes original Patrick McHardy's entitled (nf_tables: inline nft_payload_fast_eval() into main evaluation loop). Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent cb7dbfd commit c29b72e

File tree

3 files changed

+81
-28
lines changed

3 files changed

+81
-28
lines changed

include/net/netfilter/nf_tables_core.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ extern void nft_bitwise_module_exit(void);
2727
extern int nft_byteorder_module_init(void);
2828
extern void nft_byteorder_module_exit(void);
2929

30+
struct nft_payload {
31+
enum nft_payload_bases base:8;
32+
u8 offset;
33+
u8 len;
34+
enum nft_registers dreg:8;
35+
};
36+
37+
extern const struct nft_expr_ops nft_payload_fast_ops;
38+
3039
extern int nft_payload_module_init(void);
3140
extern void nft_payload_module_exit(void);
3241

net/netfilter/nf_tables_core.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,34 @@ static void nft_cmp_fast_eval(const struct nft_expr *expr,
3232
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
3333
}
3434

35+
static bool nft_payload_fast_eval(const struct nft_expr *expr,
36+
struct nft_data data[NFT_REG_MAX + 1],
37+
const struct nft_pktinfo *pkt)
38+
{
39+
const struct nft_payload *priv = nft_expr_priv(expr);
40+
const struct sk_buff *skb = pkt->skb;
41+
struct nft_data *dest = &data[priv->dreg];
42+
unsigned char *ptr;
43+
44+
if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
45+
ptr = skb_network_header(skb);
46+
else
47+
ptr = skb_transport_header(skb);
48+
49+
ptr += priv->offset;
50+
51+
if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
52+
return false;
53+
54+
if (priv->len == 2)
55+
*(u16 *)dest->data = *(u16 *)ptr;
56+
else if (priv->len == 4)
57+
*(u32 *)dest->data = *(u32 *)ptr;
58+
else
59+
*(u8 *)dest->data = *(u8 *)ptr;
60+
return true;
61+
}
62+
3563
unsigned int nft_do_chain(const struct nf_hook_ops *ops,
3664
struct sk_buff *skb,
3765
const struct net_device *in,
@@ -62,7 +90,8 @@ unsigned int nft_do_chain(const struct nf_hook_ops *ops,
6290
nft_rule_for_each_expr(expr, last, rule) {
6391
if (expr->ops == &nft_cmp_fast_ops)
6492
nft_cmp_fast_eval(expr, data);
65-
else
93+
else if (expr->ops != &nft_payload_fast_ops ||
94+
!nft_payload_fast_eval(expr, data, &pkt))
6695
expr->ops->eval(expr, data, &pkt);
6796

6897
if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)

net/netfilter/nft_payload.c

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@
1717
#include <net/netfilter/nf_tables_core.h>
1818
#include <net/netfilter/nf_tables.h>
1919

20-
struct nft_payload {
21-
enum nft_payload_bases base:8;
22-
u8 offset;
23-
u8 len;
24-
enum nft_registers dreg:8;
25-
};
26-
2720
static void nft_payload_eval(const struct nft_expr *expr,
2821
struct nft_data data[NFT_REG_MAX + 1],
2922
const struct nft_pktinfo *pkt)
@@ -71,27 +64,9 @@ static int nft_payload_init(const struct nft_ctx *ctx,
7164
struct nft_payload *priv = nft_expr_priv(expr);
7265
int err;
7366

74-
if (tb[NFTA_PAYLOAD_DREG] == NULL ||
75-
tb[NFTA_PAYLOAD_BASE] == NULL ||
76-
tb[NFTA_PAYLOAD_OFFSET] == NULL ||
77-
tb[NFTA_PAYLOAD_LEN] == NULL)
78-
return -EINVAL;
79-
80-
priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
81-
switch (priv->base) {
82-
case NFT_PAYLOAD_LL_HEADER:
83-
case NFT_PAYLOAD_NETWORK_HEADER:
84-
case NFT_PAYLOAD_TRANSPORT_HEADER:
85-
break;
86-
default:
87-
return -EOPNOTSUPP;
88-
}
89-
67+
priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
9068
priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
9169
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
92-
if (priv->len == 0 ||
93-
priv->len > FIELD_SIZEOF(struct nft_data, data))
94-
return -EINVAL;
9570

9671
priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG]));
9772
err = nft_validate_output_register(priv->dreg);
@@ -124,9 +99,49 @@ static const struct nft_expr_ops nft_payload_ops = {
12499
.dump = nft_payload_dump,
125100
};
126101

102+
const struct nft_expr_ops nft_payload_fast_ops = {
103+
.type = &nft_payload_type,
104+
.size = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
105+
.eval = nft_payload_eval,
106+
.init = nft_payload_init,
107+
.dump = nft_payload_dump,
108+
};
109+
110+
static const struct nft_expr_ops *nft_payload_select_ops(const struct nlattr * const tb[])
111+
{
112+
enum nft_payload_bases base;
113+
unsigned int offset, len;
114+
115+
if (tb[NFTA_PAYLOAD_DREG] == NULL ||
116+
tb[NFTA_PAYLOAD_BASE] == NULL ||
117+
tb[NFTA_PAYLOAD_OFFSET] == NULL ||
118+
tb[NFTA_PAYLOAD_LEN] == NULL)
119+
return ERR_PTR(-EINVAL);
120+
121+
base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
122+
switch (base) {
123+
case NFT_PAYLOAD_LL_HEADER:
124+
case NFT_PAYLOAD_NETWORK_HEADER:
125+
case NFT_PAYLOAD_TRANSPORT_HEADER:
126+
break;
127+
default:
128+
return ERR_PTR(-EOPNOTSUPP);
129+
}
130+
131+
offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
132+
len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
133+
if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data))
134+
return ERR_PTR(-EINVAL);
135+
136+
if (len <= 4 && IS_ALIGNED(offset, len) && base != NFT_PAYLOAD_LL_HEADER)
137+
return &nft_payload_fast_ops;
138+
else
139+
return &nft_payload_ops;
140+
}
141+
127142
static struct nft_expr_type nft_payload_type __read_mostly = {
128143
.name = "payload",
129-
.ops = &nft_payload_ops,
144+
.select_ops = nft_payload_select_ops,
130145
.policy = nft_payload_policy,
131146
.maxattr = NFTA_PAYLOAD_MAX,
132147
.owner = THIS_MODULE,

0 commit comments

Comments
 (0)