Skip to content

Commit c9626a2

Browse files
ummakynesdavem330
authored andcommitted
netfilter: nf_tables: add hardware offload support
This patch adds hardware offload support for nftables through the existing netdev_ops->ndo_setup_tc() interface, the TC_SETUP_CLSFLOWER classifier and the flow rule API. This hardware offload support is available for the NFPROTO_NETDEV family and the ingress hook. Each nftables expression has a new ->offload interface, that is used to populate the flow rule object that is attached to the transaction object. There is a new per-table NFT_TABLE_F_HW flag, that is set on to offload an entire table, including all of its chains. This patch supports for basic metadata (layer 3 and 4 protocol numbers), 5-tuple payload matching and the accept/drop actions; this also includes basechain hardware offload only. Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f9e3008 commit c9626a2

File tree

10 files changed

+691
-7
lines changed

10 files changed

+691
-7
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ struct nft_ctx {
161161
const struct nlattr * const *nla;
162162
u32 portid;
163163
u32 seq;
164+
u16 flags;
164165
u8 family;
165166
u8 level;
166167
bool report;
@@ -735,6 +736,9 @@ enum nft_trans_phase {
735736
NFT_TRANS_RELEASE
736737
};
737738

739+
struct nft_flow_rule;
740+
struct nft_offload_ctx;
741+
738742
/**
739743
* struct nft_expr_ops - nf_tables expression operations
740744
*
@@ -777,6 +781,10 @@ struct nft_expr_ops {
777781
const struct nft_data **data);
778782
bool (*gc)(struct net *net,
779783
const struct nft_expr *expr);
784+
int (*offload)(struct nft_offload_ctx *ctx,
785+
struct nft_flow_rule *flow,
786+
const struct nft_expr *expr);
787+
u32 offload_flags;
780788
const struct nft_expr_type *type;
781789
void *data;
782790
};
@@ -859,6 +867,7 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
859867

860868
enum nft_chain_flags {
861869
NFT_BASE_CHAIN = 0x1,
870+
NFT_CHAIN_HW_OFFLOAD = 0x2,
862871
};
863872

864873
/**
@@ -942,6 +951,7 @@ struct nft_stats {
942951
* @stats: per-cpu chain stats
943952
* @chain: the chain
944953
* @dev_name: device name that this base chain is attached to (if any)
954+
* @cb_list: list of flow block callbacks (for hardware offload)
945955
*/
946956
struct nft_base_chain {
947957
struct nf_hook_ops ops;
@@ -951,6 +961,7 @@ struct nft_base_chain {
951961
struct nft_stats __percpu *stats;
952962
struct nft_chain chain;
953963
char dev_name[IFNAMSIZ];
964+
struct list_head cb_list;
954965
};
955966

956967
static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain)
@@ -1322,11 +1333,14 @@ struct nft_trans {
13221333

13231334
struct nft_trans_rule {
13241335
struct nft_rule *rule;
1336+
struct nft_flow_rule *flow;
13251337
u32 rule_id;
13261338
};
13271339

13281340
#define nft_trans_rule(trans) \
13291341
(((struct nft_trans_rule *)trans->data)->rule)
1342+
#define nft_trans_flow_rule(trans) \
1343+
(((struct nft_trans_rule *)trans->data)->flow)
13301344
#define nft_trans_rule_id(trans) \
13311345
(((struct nft_trans_rule *)trans->data)->rule_id)
13321346

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef _NET_NF_TABLES_OFFLOAD_H
2+
#define _NET_NF_TABLES_OFFLOAD_H
3+
4+
#include <net/flow_offload.h>
5+
#include <net/netfilter/nf_tables.h>
6+
7+
struct nft_offload_reg {
8+
u32 key;
9+
u32 len;
10+
u32 base_offset;
11+
u32 offset;
12+
struct nft_data mask;
13+
};
14+
15+
enum nft_offload_dep_type {
16+
NFT_OFFLOAD_DEP_UNSPEC = 0,
17+
NFT_OFFLOAD_DEP_NETWORK,
18+
NFT_OFFLOAD_DEP_TRANSPORT,
19+
};
20+
21+
struct nft_offload_ctx {
22+
struct {
23+
enum nft_offload_dep_type type;
24+
__be16 l3num;
25+
u8 protonum;
26+
} dep;
27+
unsigned int num_actions;
28+
struct nft_offload_reg regs[NFT_REG32_15 + 1];
29+
};
30+
31+
void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
32+
enum nft_offload_dep_type type);
33+
void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
34+
const void *data, u32 len);
35+
36+
struct nft_flow_key {
37+
struct flow_dissector_key_basic basic;
38+
union {
39+
struct flow_dissector_key_ipv4_addrs ipv4;
40+
struct flow_dissector_key_ipv6_addrs ipv6;
41+
};
42+
struct flow_dissector_key_ports tp;
43+
struct flow_dissector_key_ip ip;
44+
struct flow_dissector_key_vlan vlan;
45+
struct flow_dissector_key_eth_addrs eth_addrs;
46+
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
47+
48+
struct nft_flow_match {
49+
struct flow_dissector dissector;
50+
struct nft_flow_key key;
51+
struct nft_flow_key mask;
52+
};
53+
54+
struct nft_flow_rule {
55+
__be16 proto;
56+
struct nft_flow_match match;
57+
struct flow_rule *rule;
58+
};
59+
60+
#define NFT_OFFLOAD_F_ACTION (1 << 0)
61+
62+
struct nft_rule;
63+
struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule);
64+
void nft_flow_rule_destroy(struct nft_flow_rule *flow);
65+
int nft_flow_rule_offload_commit(struct net *net);
66+
67+
#define NFT_OFFLOAD_MATCH(__key, __base, __field, __len, __reg) \
68+
(__reg)->base_offset = \
69+
offsetof(struct nft_flow_key, __base); \
70+
(__reg)->offset = \
71+
offsetof(struct nft_flow_key, __base.__field); \
72+
(__reg)->len = __len; \
73+
(__reg)->key = __key; \
74+
memset(&(__reg)->mask, 0xff, (__reg)->len);
75+
76+
#endif

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ enum nft_table_attributes {
192192
* @NFTA_CHAIN_USE: number of references to this chain (NLA_U32)
193193
* @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
194194
* @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
195+
* @NFTA_CHAIN_FLAGS: chain flags
195196
*/
196197
enum nft_chain_attributes {
197198
NFTA_CHAIN_UNSPEC,
@@ -204,6 +205,7 @@ enum nft_chain_attributes {
204205
NFTA_CHAIN_TYPE,
205206
NFTA_CHAIN_COUNTERS,
206207
NFTA_CHAIN_PAD,
208+
NFTA_CHAIN_FLAGS,
207209
__NFTA_CHAIN_MAX
208210
};
209211
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)

net/netfilter/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \
7878
nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \
7979
nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \
8080
nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o \
81-
nft_chain_route.o
81+
nft_chain_route.o nf_tables_offload.o
8282

8383
nf_tables_set-objs := nf_tables_set_core.o \
8484
nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o

net/netfilter/nf_tables_api.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <net/netfilter/nf_flow_table.h>
1919
#include <net/netfilter/nf_tables_core.h>
2020
#include <net/netfilter/nf_tables.h>
21+
#include <net/netfilter/nf_tables_offload.h>
2122
#include <net/net_namespace.h>
2223
#include <net/sock.h>
2324

@@ -97,6 +98,7 @@ static void nft_ctx_init(struct nft_ctx *ctx,
9798
ctx->nla = nla;
9899
ctx->portid = NETLINK_CB(skb).portid;
99100
ctx->report = nlmsg_report(nlh);
101+
ctx->flags = nlh->nlmsg_flags;
100102
ctx->seq = nlh->nlmsg_seq;
101103
}
102104

@@ -1169,6 +1171,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
11691171
[NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
11701172
[NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
11711173
[NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
1174+
[NFTA_CHAIN_FLAGS] = { .type = NLA_U32 },
11721175
};
11731176

11741177
static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
@@ -1603,7 +1606,7 @@ static struct nft_rule **nf_tables_chain_alloc_rules(const struct nft_chain *cha
16031606
}
16041607

16051608
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
1606-
u8 policy)
1609+
u8 policy, u32 flags)
16071610
{
16081611
const struct nlattr * const *nla = ctx->nla;
16091612
struct nft_table *table = ctx->table;
@@ -1657,8 +1660,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
16571660
ops->hook = hook.type->hooks[ops->hooknum];
16581661
ops->dev = hook.dev;
16591662

1660-
chain->flags |= NFT_BASE_CHAIN;
1663+
chain->flags |= NFT_BASE_CHAIN | flags;
16611664
basechain->policy = NF_ACCEPT;
1665+
INIT_LIST_HEAD(&basechain->cb_list);
16621666
} else {
16631667
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
16641668
if (chain == NULL)
@@ -1718,7 +1722,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
17181722
return err;
17191723
}
17201724

1721-
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
1725+
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
1726+
u32 flags)
17221727
{
17231728
const struct nlattr * const *nla = ctx->nla;
17241729
struct nft_table *table = ctx->table;
@@ -1730,6 +1735,9 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
17301735
struct nft_trans *trans;
17311736
int err;
17321737

1738+
if (chain->flags ^ flags)
1739+
return -EOPNOTSUPP;
1740+
17331741
if (nla[NFTA_CHAIN_HOOK]) {
17341742
if (!nft_is_base_chain(chain))
17351743
return -EBUSY;
@@ -1835,6 +1843,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
18351843
u8 policy = NF_ACCEPT;
18361844
struct nft_ctx ctx;
18371845
u64 handle = 0;
1846+
u32 flags = 0;
18381847

18391848
lockdep_assert_held(&net->nft.commit_mutex);
18401849

@@ -1889,6 +1898,9 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
18891898
}
18901899
}
18911900

1901+
if (nla[NFTA_CHAIN_FLAGS])
1902+
flags = ntohl(nla_get_be32(nla[NFTA_CHAIN_FLAGS]));
1903+
18921904
nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
18931905

18941906
if (chain != NULL) {
@@ -1899,10 +1911,10 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
18991911
if (nlh->nlmsg_flags & NLM_F_REPLACE)
19001912
return -EOPNOTSUPP;
19011913

1902-
return nf_tables_updchain(&ctx, genmask, policy);
1914+
return nf_tables_updchain(&ctx, genmask, policy, flags);
19031915
}
19041916

1905-
return nf_tables_addchain(&ctx, family, genmask, policy);
1917+
return nf_tables_addchain(&ctx, family, genmask, policy, flags);
19061918
}
19071919

19081920
static int nf_tables_delchain(struct net *net, struct sock *nlsk,
@@ -2658,6 +2670,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
26582670
u8 genmask = nft_genmask_next(net);
26592671
struct nft_expr_info *info = NULL;
26602672
int family = nfmsg->nfgen_family;
2673+
struct nft_flow_rule *flow;
26612674
struct nft_table *table;
26622675
struct nft_chain *chain;
26632676
struct nft_rule *rule, *old_rule = NULL;
@@ -2804,7 +2817,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
28042817

28052818
list_add_tail_rcu(&rule->list, &old_rule->list);
28062819
} else {
2807-
if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
2820+
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
2821+
if (!trans) {
28082822
err = -ENOMEM;
28092823
goto err2;
28102824
}
@@ -2827,6 +2841,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
28272841
if (net->nft.validate_state == NFT_VALIDATE_DO)
28282842
return nft_table_validate(net, table);
28292843

2844+
if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
2845+
flow = nft_flow_rule_create(rule);
2846+
if (IS_ERR(flow))
2847+
return PTR_ERR(flow);
2848+
2849+
nft_trans_flow_rule(trans) = flow;
2850+
}
2851+
28302852
return 0;
28312853
err2:
28322854
nf_tables_rule_release(&ctx, rule);
@@ -6624,6 +6646,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
66246646
struct nft_trans_elem *te;
66256647
struct nft_chain *chain;
66266648
struct nft_table *table;
6649+
int err;
66276650

66286651
if (list_empty(&net->nft.commit_list)) {
66296652
mutex_unlock(&net->nft.commit_mutex);
@@ -6634,6 +6657,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
66346657
if (nf_tables_validate(net) < 0)
66356658
return -EAGAIN;
66366659

6660+
err = nft_flow_rule_offload_commit(net);
6661+
if (err < 0)
6662+
return err;
6663+
66376664
/* 1. Allocate space for next generation rules_gen_X[] */
66386665
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
66396666
int ret;

0 commit comments

Comments
 (0)