Commit c9626a2c authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso Committed by David S. Miller

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: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f9e30088
...@@ -161,6 +161,7 @@ struct nft_ctx { ...@@ -161,6 +161,7 @@ struct nft_ctx {
const struct nlattr * const *nla; const struct nlattr * const *nla;
u32 portid; u32 portid;
u32 seq; u32 seq;
u16 flags;
u8 family; u8 family;
u8 level; u8 level;
bool report; bool report;
...@@ -735,6 +736,9 @@ enum nft_trans_phase { ...@@ -735,6 +736,9 @@ enum nft_trans_phase {
NFT_TRANS_RELEASE NFT_TRANS_RELEASE
}; };
struct nft_flow_rule;
struct nft_offload_ctx;
/** /**
* struct nft_expr_ops - nf_tables expression operations * struct nft_expr_ops - nf_tables expression operations
* *
...@@ -777,6 +781,10 @@ struct nft_expr_ops { ...@@ -777,6 +781,10 @@ struct nft_expr_ops {
const struct nft_data **data); const struct nft_data **data);
bool (*gc)(struct net *net, bool (*gc)(struct net *net,
const struct nft_expr *expr); 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; const struct nft_expr_type *type;
void *data; void *data;
}; };
...@@ -859,6 +867,7 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule) ...@@ -859,6 +867,7 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
enum nft_chain_flags { enum nft_chain_flags {
NFT_BASE_CHAIN = 0x1, NFT_BASE_CHAIN = 0x1,
NFT_CHAIN_HW_OFFLOAD = 0x2,
}; };
/** /**
...@@ -942,6 +951,7 @@ struct nft_stats { ...@@ -942,6 +951,7 @@ struct nft_stats {
* @stats: per-cpu chain stats * @stats: per-cpu chain stats
* @chain: the chain * @chain: the chain
* @dev_name: device name that this base chain is attached to (if any) * @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 nft_base_chain {
struct nf_hook_ops ops; struct nf_hook_ops ops;
...@@ -951,6 +961,7 @@ struct nft_base_chain { ...@@ -951,6 +961,7 @@ struct nft_base_chain {
struct nft_stats __percpu *stats; struct nft_stats __percpu *stats;
struct nft_chain chain; struct nft_chain chain;
char dev_name[IFNAMSIZ]; char dev_name[IFNAMSIZ];
struct list_head cb_list;
}; };
static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain) static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain)
...@@ -1322,11 +1333,14 @@ struct nft_trans { ...@@ -1322,11 +1333,14 @@ struct nft_trans {
struct nft_trans_rule { struct nft_trans_rule {
struct nft_rule *rule; struct nft_rule *rule;
struct nft_flow_rule *flow;
u32 rule_id; u32 rule_id;
}; };
#define nft_trans_rule(trans) \ #define nft_trans_rule(trans) \
(((struct nft_trans_rule *)trans->data)->rule) (((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) \ #define nft_trans_rule_id(trans) \
(((struct nft_trans_rule *)trans->data)->rule_id) (((struct nft_trans_rule *)trans->data)->rule_id)
......
#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
...@@ -192,6 +192,7 @@ enum nft_table_attributes { ...@@ -192,6 +192,7 @@ enum nft_table_attributes {
* @NFTA_CHAIN_USE: number of references to this chain (NLA_U32) * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32)
* @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING) * @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_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
* @NFTA_CHAIN_FLAGS: chain flags
*/ */
enum nft_chain_attributes { enum nft_chain_attributes {
NFTA_CHAIN_UNSPEC, NFTA_CHAIN_UNSPEC,
...@@ -204,6 +205,7 @@ enum nft_chain_attributes { ...@@ -204,6 +205,7 @@ enum nft_chain_attributes {
NFTA_CHAIN_TYPE, NFTA_CHAIN_TYPE,
NFTA_CHAIN_COUNTERS, NFTA_CHAIN_COUNTERS,
NFTA_CHAIN_PAD, NFTA_CHAIN_PAD,
NFTA_CHAIN_FLAGS,
__NFTA_CHAIN_MAX __NFTA_CHAIN_MAX
}; };
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
......
...@@ -78,7 +78,7 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \ ...@@ -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 \ 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_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \
nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.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 \ nf_tables_set-objs := nf_tables_set_core.o \
nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <net/netfilter/nf_flow_table.h> #include <net/netfilter/nf_flow_table.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_offload.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -97,6 +98,7 @@ static void nft_ctx_init(struct nft_ctx *ctx, ...@@ -97,6 +98,7 @@ static void nft_ctx_init(struct nft_ctx *ctx,
ctx->nla = nla; ctx->nla = nla;
ctx->portid = NETLINK_CB(skb).portid; ctx->portid = NETLINK_CB(skb).portid;
ctx->report = nlmsg_report(nlh); ctx->report = nlmsg_report(nlh);
ctx->flags = nlh->nlmsg_flags;
ctx->seq = nlh->nlmsg_seq; ctx->seq = nlh->nlmsg_seq;
} }
...@@ -1169,6 +1171,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { ...@@ -1169,6 +1171,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
[NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, [NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
[NFTA_CHAIN_TYPE] = { .type = NLA_STRING }, [NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
[NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
[NFTA_CHAIN_FLAGS] = { .type = NLA_U32 },
}; };
static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { 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 ...@@ -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, 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; const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table; struct nft_table *table = ctx->table;
...@@ -1657,8 +1660,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, ...@@ -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->hook = hook.type->hooks[ops->hooknum];
ops->dev = hook.dev; ops->dev = hook.dev;
chain->flags |= NFT_BASE_CHAIN; chain->flags |= NFT_BASE_CHAIN | flags;
basechain->policy = NF_ACCEPT; basechain->policy = NF_ACCEPT;
INIT_LIST_HEAD(&basechain->cb_list);
} else { } else {
chain = kzalloc(sizeof(*chain), GFP_KERNEL); chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL) if (chain == NULL)
...@@ -1718,7 +1722,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, ...@@ -1718,7 +1722,8 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
return err; 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; const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table; struct nft_table *table = ctx->table;
...@@ -1730,6 +1735,9 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy) ...@@ -1730,6 +1735,9 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
struct nft_trans *trans; struct nft_trans *trans;
int err; int err;
if (chain->flags ^ flags)
return -EOPNOTSUPP;
if (nla[NFTA_CHAIN_HOOK]) { if (nla[NFTA_CHAIN_HOOK]) {
if (!nft_is_base_chain(chain)) if (!nft_is_base_chain(chain))
return -EBUSY; return -EBUSY;
...@@ -1835,6 +1843,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1835,6 +1843,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
u8 policy = NF_ACCEPT; u8 policy = NF_ACCEPT;
struct nft_ctx ctx; struct nft_ctx ctx;
u64 handle = 0; u64 handle = 0;
u32 flags = 0;
lockdep_assert_held(&net->nft.commit_mutex); lockdep_assert_held(&net->nft.commit_mutex);
...@@ -1889,6 +1898,9 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -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); nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
if (chain != NULL) { if (chain != NULL) {
...@@ -1899,10 +1911,10 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1899,10 +1911,10 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_REPLACE) if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP; 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, 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, ...@@ -2658,6 +2670,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
struct nft_expr_info *info = NULL; struct nft_expr_info *info = NULL;
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nft_flow_rule *flow;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL; struct nft_rule *rule, *old_rule = NULL;
...@@ -2804,7 +2817,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, ...@@ -2804,7 +2817,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
list_add_tail_rcu(&rule->list, &old_rule->list); list_add_tail_rcu(&rule->list, &old_rule->list);
} else { } 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; err = -ENOMEM;
goto err2; goto err2;
} }
...@@ -2827,6 +2841,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, ...@@ -2827,6 +2841,14 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
if (net->nft.validate_state == NFT_VALIDATE_DO) if (net->nft.validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, table); 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; return 0;
err2: err2:
nf_tables_rule_release(&ctx, rule); nf_tables_rule_release(&ctx, rule);
...@@ -6624,6 +6646,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -6624,6 +6646,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
struct nft_trans_elem *te; struct nft_trans_elem *te;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_table *table; struct nft_table *table;
int err;
if (list_empty(&net->nft.commit_list)) { if (list_empty(&net->nft.commit_list)) {
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&net->nft.commit_mutex);
...@@ -6634,6 +6657,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -6634,6 +6657,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
if (nf_tables_validate(net) < 0) if (nf_tables_validate(net) < 0)
return -EAGAIN; return -EAGAIN;
err = nft_flow_rule_offload_commit(net);
if (err < 0)
return err;
/* 1. Allocate space for next generation rules_gen_X[] */ /* 1. Allocate space for next generation rules_gen_X[] */
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
int ret; int ret;
......
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <net/flow_offload.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_offload.h>
#include <net/pkt_cls.h>
static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
{
struct nft_flow_rule *flow;
flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
if (!flow)
return NULL;
flow->rule = flow_rule_alloc(num_actions);
if (!flow->rule) {
kfree(flow);
return NULL;
}
flow->rule->match.dissector = &flow->match.dissector;
flow->rule->match.mask = &flow->match.mask;
flow->rule->match.key = &flow->match.key;
return flow;
}
struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule)
{
struct nft_offload_ctx ctx = {
.dep = {
.type = NFT_OFFLOAD_DEP_UNSPEC,
},
};
struct nft_flow_rule *flow;
int num_actions = 0, err;
struct nft_expr *expr;
expr = nft_expr_first(rule);
while (expr->ops && expr != nft_expr_last(rule)) {
if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
num_actions++;
expr = nft_expr_next(expr);
}
flow = nft_flow_rule_alloc(num_actions);
if (!flow)
return ERR_PTR(-ENOMEM);
expr = nft_expr_first(rule);
while (expr->ops && expr != nft_expr_last(rule)) {
if (!expr->ops->offload) {
err = -EOPNOTSUPP;
goto err_out;
}
err = expr->ops->offload(&ctx, flow, expr);
if (err < 0)
goto err_out;
expr = nft_expr_next(expr);
}
flow->proto = ctx.dep.l3num;
return flow;
err_out:
nft_flow_rule_destroy(flow);
return ERR_PTR(err);
}
void nft_flow_rule_destroy(struct nft_flow_rule *flow)
{
kfree(flow->rule);
kfree(flow);
}
void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
enum nft_offload_dep_type type)
{
ctx->dep.type = type;
}
void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
const void *data, u32 len)
{
switch (ctx->dep.type) {
case NFT_OFFLOAD_DEP_NETWORK:
WARN_ON(len != sizeof(__u16));
memcpy(&ctx->dep.l3num, data, sizeof(__u16));
break;
case NFT_OFFLOAD_DEP_TRANSPORT:
WARN_ON(len != sizeof(__u8));
memcpy(&ctx->dep.protonum, data, sizeof(__u8));
break;
default:
break;
}
ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
}
static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
__be16 proto,
struct netlink_ext_ack *extack)
{
common->protocol = proto;
common->extack = extack;
}
static int nft_setup_cb_call(struct nft_base_chain *basechain,
enum tc_setup_type type, void *type_data)
{
struct flow_block_cb *block_cb;
int err;
list_for_each_entry(block_cb, &basechain->cb_list, list) {
err = block_cb->cb(type, type_data, block_cb->cb_priv);
if (err < 0)
return err;
}
return 0;
}
static int nft_flow_offload_rule(struct nft_trans *trans,
enum flow_cls_command command)
{
struct nft_flow_rule *flow = nft_trans_flow_rule(trans);
struct nft_rule *rule = nft_trans_rule(trans);
struct flow_cls_offload cls_flow = {};
struct nft_base_chain *basechain;
struct netlink_ext_ack extack;
__be16 proto = ETH_P_ALL;
if (!nft_is_base_chain(trans->ctx.chain))
return -EOPNOTSUPP;
basechain = nft_base_chain(trans->ctx.chain);
if (flow)
proto = flow->proto;
nft_flow_offload_common_init(&cls_flow.common, proto, &extack);
cls_flow.command = command;
cls_flow.cookie = (unsigned long) rule;
if (flow)
cls_flow.rule = flow->rule;
return nft_setup_cb_call(basechain, TC_SETUP_CLSFLOWER, &cls_flow);
}
static int nft_flow_offload_bind(struct flow_block_offload *bo,
struct nft_base_chain *basechain)
{
list_splice(&bo->cb_list, &basechain->cb_list);
return 0;
}
static int nft_flow_offload_unbind(struct flow_block_offload *bo,
struct nft_base_chain *basechain)
{
struct flow_block_cb *block_cb, *next;
list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
list_del(&block_cb->list);
flow_block_cb_free(block_cb);
}
return 0;
}
#define FLOW_SETUP_BLOCK TC_SETUP_BLOCK
static int nft_flow_offload_chain(struct nft_trans *trans,
enum flow_block_command cmd)
{
struct nft_chain *chain = trans->ctx.chain;
struct netlink_ext_ack extack = {};
struct flow_block_offload bo = {};
struct nft_base_chain *basechain;
struct net_device *dev;
int err;
if (!nft_is_base_chain(chain))
return -EOPNOTSUPP;
basechain = nft_base_chain(chain);
dev = basechain->ops.dev;
if (!dev || !dev->netdev_ops->ndo_setup_tc)
return -EOPNOTSUPP;
/* Only default policy to accept is supported for now. */
if (cmd == FLOW_BLOCK_BIND &&
nft_trans_chain_policy(trans) != -1 &&
nft_trans_chain_policy(trans) != NF_ACCEPT)
return -EOPNOTSUPP;
bo.command = cmd;
bo.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
bo.extack = &extack;
INIT_LIST_HEAD(&bo.cb_list);
err = dev->netdev_ops->ndo_setup_tc(dev, FLOW_SETUP_BLOCK, &bo);
if (err < 0)
return err;
switch (cmd) {
case FLOW_BLOCK_BIND:
err = nft_flow_offload_bind(&bo, basechain);
break;
case FLOW_BLOCK_UNBIND:
err = nft_flow_offload_unbind(&bo, basechain);
break;
}
return err;
}
int nft_flow_rule_offload_commit(struct net *net)
{
struct nft_trans *trans;
int err = 0;
list_for_each_entry(trans, &net->nft.commit_list, list) {
if (trans->ctx.family != NFPROTO_NETDEV)
continue;
switch (trans->msg_type) {
case NFT_MSG_NEWCHAIN:
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue;
err = nft_flow_offload_chain(trans, FLOW_BLOCK_BIND);
break;
case NFT_MSG_DELCHAIN:
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue;
err = nft_flow_offload_chain(trans, FLOW_BLOCK_UNBIND);
break;
case NFT_MSG_NEWRULE:
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue;
if (trans->ctx.flags & NLM_F_REPLACE ||
!(trans->ctx.flags & NLM_F_APPEND))
return -EOPNOTSUPP;
err = nft_flow_offload_rule(trans, FLOW_CLS_REPLACE);
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break;
case NFT_MSG_DELRULE:
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue;
err = nft_flow_offload_rule(trans, FLOW_CLS_DESTROY);
break;
}
if (err)
return err;
}
return err;
}
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables_offload.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
struct nft_cmp_expr { struct nft_cmp_expr {
...@@ -107,12 +108,44 @@ static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -107,12 +108,44 @@ static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1; return -1;
} }
static int __nft_cmp_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_cmp_expr *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->sreg];
u8 *mask = (u8 *)&flow->match.mask;
u8 *key = (u8 *)&flow->match.key;
if (priv->op != NFT_CMP_EQ)
return -EOPNOTSUPP;
memcpy(key + reg->offset, &priv->data, priv->len);
memcpy(mask + reg->offset, &reg->mask, priv->len);
flow->match.dissector.used_keys |= BIT(reg->key);
flow->match.dissector.offset[reg->key] = reg->base_offset;
nft_offload_update_dependency(ctx, &priv->data, priv->len);
return 0;
}
static int nft_cmp_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr)
{
const struct nft_cmp_expr *priv = nft_expr_priv(expr);
return __nft_cmp_offload(ctx, flow, priv);
}
static const struct nft_expr_ops nft_cmp_ops = { static const struct nft_expr_ops nft_cmp_ops = {
.type = &nft_cmp_type, .type = &nft_cmp_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
.eval = nft_cmp_eval, .eval = nft_cmp_eval,
.init = nft_cmp_init, .init = nft_cmp_init,
.dump = nft_cmp_dump, .dump = nft_cmp_dump,
.offload = nft_cmp_offload,
}; };
static int nft_cmp_fast_init(const struct nft_ctx *ctx, static int nft_cmp_fast_init(const struct nft_ctx *ctx,
...@@ -143,6 +176,25 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx, ...@@ -143,6 +176,25 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
return 0; return 0;
} }
static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr)
{
const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
struct nft_cmp_expr cmp = {
.data = {
.data = {
[0] = priv->data,
},
},
.sreg = priv->sreg,
.len = priv->len / BITS_PER_BYTE,
.op = NFT_CMP_EQ,
};
return __nft_cmp_offload(ctx, flow, &cmp);
}
static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
{ {
const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
...@@ -169,6 +221,7 @@ const struct nft_expr_ops nft_cmp_fast_ops = { ...@@ -169,6 +221,7 @@ const struct nft_expr_ops nft_cmp_fast_ops = {
.eval = NULL, /* inlined */ .eval = NULL, /* inlined */
.init = nft_cmp_fast_init, .init = nft_cmp_fast_init,
.dump = nft_cmp_fast_dump, .dump = nft_cmp_fast_dump,
.offload = nft_cmp_fast_offload,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_offload.h>
void nft_immediate_eval(const struct nft_expr *expr, void nft_immediate_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
...@@ -124,6 +125,34 @@ static int nft_immediate_validate(const struct nft_ctx *ctx, ...@@ -124,6 +125,34 @@ static int nft_immediate_validate(const struct nft_ctx *ctx,
return 0; return 0;
} }
static int nft_immediate_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
struct flow_action_entry *entry;
const struct nft_data *data;
if (priv->dreg != NFT_REG_VERDICT)
return -EOPNOTSUPP;
entry = &flow->rule->action.entries[ctx->num_actions++];
data = &priv->data;
switch (data->verdict.code) {
case NF_ACCEPT:
entry->id = FLOW_ACTION_ACCEPT;
break;
case NF_DROP:
entry->id = FLOW_ACTION_DROP;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct nft_expr_ops nft_imm_ops = { static const struct nft_expr_ops nft_imm_ops = {
.type = &nft_imm_type, .type = &nft_imm_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
...@@ -133,6 +162,8 @@ static const struct nft_expr_ops nft_imm_ops = { ...@@ -133,6 +162,8 @@ static const struct nft_expr_ops nft_imm_ops = {
.deactivate = nft_immediate_deactivate, .deactivate = nft_immediate_deactivate,
.dump = nft_immediate_dump, .dump = nft_immediate_dump,
.validate = nft_immediate_validate, .validate = nft_immediate_validate,
.offload = nft_immediate_offload,
.offload_flags = NFT_OFFLOAD_F_ACTION,
}; };
struct nft_expr_type nft_imm_type __read_mostly = { struct nft_expr_type nft_imm_type __read_mostly = {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nft_meta.h> #include <net/netfilter/nft_meta.h>
#include <net/netfilter/nf_tables_offload.h>
#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
...@@ -490,6 +491,31 @@ void nft_meta_set_destroy(const struct nft_ctx *ctx, ...@@ -490,6 +491,31 @@ void nft_meta_set_destroy(const struct nft_ctx *ctx,
} }
EXPORT_SYMBOL_GPL(nft_meta_set_destroy); EXPORT_SYMBOL_GPL(nft_meta_set_destroy);
static int nft_meta_get_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr)
{
const struct nft_meta *priv = nft_expr_priv(expr);
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->key) {
case NFT_META_PROTOCOL:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto,
sizeof(__u16), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK);
break;
case NFT_META_L4PROTO:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct nft_expr_ops nft_meta_get_ops = { static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type, .type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
...@@ -497,6 +523,7 @@ static const struct nft_expr_ops nft_meta_get_ops = { ...@@ -497,6 +523,7 @@ static const struct nft_expr_ops nft_meta_get_ops = {
.init = nft_meta_get_init, .init = nft_meta_get_init,
.dump = nft_meta_get_dump, .dump = nft_meta_get_dump,
.validate = nft_meta_get_validate, .validate = nft_meta_get_validate,
.offload = nft_meta_get_offload,
}; };
static const struct nft_expr_ops nft_meta_set_ops = { static const struct nft_expr_ops nft_meta_set_ops = {
......
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_offload.h>
/* For layer 4 checksum field offset. */ /* For layer 4 checksum field offset. */
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
/* add vlan header into the user buffer for if tag was removed by offloads */ /* add vlan header into the user buffer for if tag was removed by offloads */
static bool static bool
...@@ -150,12 +153,195 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -150,12 +153,195 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1; return -1;
} }
static int nft_payload_offload_ll(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->offset) {
case offsetof(struct ethhdr, h_source):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
src, ETH_ALEN, reg);
break;
case offsetof(struct ethhdr, h_dest):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
dst, ETH_ALEN, reg);
break;
}
return 0;
}
static int nft_payload_offload_ip(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->offset) {
case offsetof(struct iphdr, saddr):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, src,
sizeof(struct in_addr), reg);
break;
case offsetof(struct iphdr, daddr):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, dst,
sizeof(struct in_addr), reg);
break;
case offsetof(struct iphdr, protocol):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int nft_payload_offload_ip6(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->offset) {
case offsetof(struct ipv6hdr, saddr):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, src,
sizeof(struct in6_addr), reg);
break;
case offsetof(struct ipv6hdr, daddr):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, dst,
sizeof(struct in6_addr), reg);
break;
case offsetof(struct ipv6hdr, nexthdr):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int nft_payload_offload_nh(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
int err;
switch (ctx->dep.l3num) {
case htons(ETH_P_IP):
err = nft_payload_offload_ip(ctx, flow, priv);
break;
case htons(ETH_P_IPV6):
err = nft_payload_offload_ip6(ctx, flow, priv);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static int nft_payload_offload_tcp(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->offset) {
case offsetof(struct tcphdr, source):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg);
break;
case offsetof(struct tcphdr, dest):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
sizeof(__be16), reg);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int nft_payload_offload_udp(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
switch (priv->offset) {
case offsetof(struct udphdr, source):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg);
break;
case offsetof(struct udphdr, dest):
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
sizeof(__be16), reg);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int nft_payload_offload_th(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
{
int err;
switch (ctx->dep.protonum) {
case IPPROTO_TCP:
err = nft_payload_offload_tcp(ctx, flow, priv);
break;
case IPPROTO_UDP:
err = nft_payload_offload_udp(ctx, flow, priv);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static int nft_payload_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_expr *expr)
{
const struct nft_payload *priv = nft_expr_priv(expr);
int err;
switch (priv->base) {
case NFT_PAYLOAD_LL_HEADER:
err = nft_payload_offload_ll(ctx, flow, priv);
break;
case NFT_PAYLOAD_NETWORK_HEADER:
err = nft_payload_offload_nh(ctx, flow, priv);
break;
case NFT_PAYLOAD_TRANSPORT_HEADER:
err = nft_payload_offload_th(ctx, flow, priv);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static const struct nft_expr_ops nft_payload_ops = { static const struct nft_expr_ops nft_payload_ops = {
.type = &nft_payload_type, .type = &nft_payload_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
.eval = nft_payload_eval, .eval = nft_payload_eval,
.init = nft_payload_init, .init = nft_payload_init,
.dump = nft_payload_dump, .dump = nft_payload_dump,
.offload = nft_payload_offload,
}; };
const struct nft_expr_ops nft_payload_fast_ops = { const struct nft_expr_ops nft_payload_fast_ops = {
...@@ -164,6 +350,7 @@ const struct nft_expr_ops nft_payload_fast_ops = { ...@@ -164,6 +350,7 @@ const struct nft_expr_ops nft_payload_fast_ops = {
.eval = nft_payload_eval, .eval = nft_payload_eval,
.init = nft_payload_init, .init = nft_payload_init,
.dump = nft_payload_dump, .dump = nft_payload_dump,
.offload = nft_payload_offload,
}; };
static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum) static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment