Skip to content

Commit

Permalink
netfilter: nf_tables: add hardware offload support
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ummakynes authored and davem330 committed Jul 9, 2019
1 parent f9e3008 commit c9626a2
Show file tree
Hide file tree
Showing 10 changed files with 691 additions and 7 deletions.
14 changes: 14 additions & 0 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ struct nft_ctx {
const struct nlattr * const *nla;
u32 portid;
u32 seq;
u16 flags;
u8 family;
u8 level;
bool report;
Expand Down Expand Up @@ -735,6 +736,9 @@ enum nft_trans_phase {
NFT_TRANS_RELEASE
};

struct nft_flow_rule;
struct nft_offload_ctx;

/**
* struct nft_expr_ops - nf_tables expression operations
*
Expand Down Expand Up @@ -777,6 +781,10 @@ struct nft_expr_ops {
const struct nft_data **data);
bool (*gc)(struct net *net,
const struct nft_expr *expr);
int (*offload)(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr);
u32 offload_flags;
const struct nft_expr_type *type;
void *data;
};
Expand Down Expand Up @@ -859,6 +867,7 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)

enum nft_chain_flags {
NFT_BASE_CHAIN = 0x1,
NFT_CHAIN_HW_OFFLOAD = 0x2,
};

/**
Expand Down Expand Up @@ -942,6 +951,7 @@ struct nft_stats {
* @stats: per-cpu chain stats
* @chain: the chain
* @dev_name: device name that this base chain is attached to (if any)
* @cb_list: list of flow block callbacks (for hardware offload)
*/
struct nft_base_chain {
struct nf_hook_ops ops;
Expand All @@ -951,6 +961,7 @@ struct nft_base_chain {
struct nft_stats __percpu *stats;
struct nft_chain chain;
char dev_name[IFNAMSIZ];
struct list_head cb_list;
};

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

struct nft_trans_rule {
struct nft_rule *rule;
struct nft_flow_rule *flow;
u32 rule_id;
};

#define nft_trans_rule(trans) \
(((struct nft_trans_rule *)trans->data)->rule)
#define nft_trans_flow_rule(trans) \
(((struct nft_trans_rule *)trans->data)->flow)
#define nft_trans_rule_id(trans) \
(((struct nft_trans_rule *)trans->data)->rule_id)

Expand Down
76 changes: 76 additions & 0 deletions include/net/netfilter/nf_tables_offload.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef _NET_NF_TABLES_OFFLOAD_H
#define _NET_NF_TABLES_OFFLOAD_H

#include <net/flow_offload.h>
#include <net/netfilter/nf_tables.h>

struct nft_offload_reg {
u32 key;
u32 len;
u32 base_offset;
u32 offset;
struct nft_data mask;
};

enum nft_offload_dep_type {
NFT_OFFLOAD_DEP_UNSPEC = 0,
NFT_OFFLOAD_DEP_NETWORK,
NFT_OFFLOAD_DEP_TRANSPORT,
};

struct nft_offload_ctx {
struct {
enum nft_offload_dep_type type;
__be16 l3num;
u8 protonum;
} dep;
unsigned int num_actions;
struct nft_offload_reg regs[NFT_REG32_15 + 1];
};

void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
enum nft_offload_dep_type type);
void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
const void *data, u32 len);

struct nft_flow_key {
struct flow_dissector_key_basic basic;
union {
struct flow_dissector_key_ipv4_addrs ipv4;
struct flow_dissector_key_ipv6_addrs ipv6;
};
struct flow_dissector_key_ports tp;
struct flow_dissector_key_ip ip;
struct flow_dissector_key_vlan vlan;
struct flow_dissector_key_eth_addrs eth_addrs;
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */

struct nft_flow_match {
struct flow_dissector dissector;
struct nft_flow_key key;
struct nft_flow_key mask;
};

struct nft_flow_rule {
__be16 proto;
struct nft_flow_match match;
struct flow_rule *rule;
};

#define NFT_OFFLOAD_F_ACTION (1 << 0)

struct nft_rule;
struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule);
void nft_flow_rule_destroy(struct nft_flow_rule *flow);
int nft_flow_rule_offload_commit(struct net *net);

#define NFT_OFFLOAD_MATCH(__key, __base, __field, __len, __reg) \
(__reg)->base_offset = \
offsetof(struct nft_flow_key, __base); \
(__reg)->offset = \
offsetof(struct nft_flow_key, __base.__field); \
(__reg)->len = __len; \
(__reg)->key = __key; \
memset(&(__reg)->mask, 0xff, (__reg)->len);

#endif
2 changes: 2 additions & 0 deletions include/uapi/linux/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ enum nft_table_attributes {
* @NFTA_CHAIN_USE: number of references to this chain (NLA_U32)
* @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
* @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
* @NFTA_CHAIN_FLAGS: chain flags
*/
enum nft_chain_attributes {
NFTA_CHAIN_UNSPEC,
Expand All @@ -204,6 +205,7 @@ enum nft_chain_attributes {
NFTA_CHAIN_TYPE,
NFTA_CHAIN_COUNTERS,
NFTA_CHAIN_PAD,
NFTA_CHAIN_FLAGS,
__NFTA_CHAIN_MAX
};
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \
nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \
nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \
nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o \
nft_chain_route.o
nft_chain_route.o nf_tables_offload.o

nf_tables_set-objs := nf_tables_set_core.o \
nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o
Expand Down
39 changes: 33 additions & 6 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <net/netfilter/nf_flow_table.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_offload.h>
#include <net/net_namespace.h>
#include <net/sock.h>

Expand Down Expand Up @@ -97,6 +98,7 @@ static void nft_ctx_init(struct nft_ctx *ctx,
ctx->nla = nla;
ctx->portid = NETLINK_CB(skb).portid;
ctx->report = nlmsg_report(nlh);
ctx->flags = nlh->nlmsg_flags;
ctx->seq = nlh->nlmsg_seq;
}

Expand Down Expand Up @@ -1169,6 +1171,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
[NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
[NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
[NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
[NFTA_CHAIN_FLAGS] = { .type = NLA_U32 },
};

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

static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
u8 policy)
u8 policy, u32 flags)
{
const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table;
Expand Down Expand Up @@ -1657,8 +1660,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
ops->hook = hook.type->hooks[ops->hooknum];
ops->dev = hook.dev;

chain->flags |= NFT_BASE_CHAIN;
chain->flags |= NFT_BASE_CHAIN | flags;
basechain->policy = NF_ACCEPT;
INIT_LIST_HEAD(&basechain->cb_list);
} else {
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
Expand Down Expand Up @@ -1718,7 +1722,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
return err;
}

static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
u32 flags)
{
const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table;
Expand All @@ -1730,6 +1735,9 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
struct nft_trans *trans;
int err;

if (chain->flags ^ flags)
return -EOPNOTSUPP;

if (nla[NFTA_CHAIN_HOOK]) {
if (!nft_is_base_chain(chain))
return -EBUSY;
Expand Down Expand Up @@ -1835,6 +1843,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
u8 policy = NF_ACCEPT;
struct nft_ctx ctx;
u64 handle = 0;
u32 flags = 0;

lockdep_assert_held(&net->nft.commit_mutex);

Expand Down Expand Up @@ -1889,6 +1898,9 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
}
}

if (nla[NFTA_CHAIN_FLAGS])
flags = ntohl(nla_get_be32(nla[NFTA_CHAIN_FLAGS]));

nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);

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

return nf_tables_updchain(&ctx, genmask, policy);
return nf_tables_updchain(&ctx, genmask, policy, flags);
}

return nf_tables_addchain(&ctx, family, genmask, policy);
return nf_tables_addchain(&ctx, family, genmask, policy, flags);
}

static int nf_tables_delchain(struct net *net, struct sock *nlsk,
Expand Down Expand Up @@ -2658,6 +2670,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
u8 genmask = nft_genmask_next(net);
struct nft_expr_info *info = NULL;
int family = nfmsg->nfgen_family;
struct nft_flow_rule *flow;
struct nft_table *table;
struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL;
Expand Down Expand Up @@ -2804,7 +2817,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,

list_add_tail_rcu(&rule->list, &old_rule->list);
} else {
if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
if (!trans) {
err = -ENOMEM;
goto err2;
}
Expand All @@ -2827,6 +2841,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
if (net->nft.validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, table);

if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
flow = nft_flow_rule_create(rule);
if (IS_ERR(flow))
return PTR_ERR(flow);

nft_trans_flow_rule(trans) = flow;
}

return 0;
err2:
nf_tables_rule_release(&ctx, rule);
Expand Down Expand Up @@ -6624,6 +6646,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
struct nft_trans_elem *te;
struct nft_chain *chain;
struct nft_table *table;
int err;

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

err = nft_flow_rule_offload_commit(net);
if (err < 0)
return err;

/* 1. Allocate space for next generation rules_gen_X[] */
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
int ret;
Expand Down
Loading

0 comments on commit c9626a2

Please sign in to comment.