mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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 <pablo@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									f9e30088d2
								
							
						
					
					
						commit
						c9626a2cbd
					
				
					 10 changed files with 691 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -161,6 +161,7 @@ struct nft_ctx {
 | 
			
		|||
	const struct nlattr * const 	*nla;
 | 
			
		||||
	u32				portid;
 | 
			
		||||
	u32				seq;
 | 
			
		||||
	u16				flags;
 | 
			
		||||
	u8				family;
 | 
			
		||||
	u8				level;
 | 
			
		||||
	bool				report;
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										76
									
								
								include/net/netfilter/nf_tables_offload.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								include/net/netfilter/nf_tables_offload.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										267
									
								
								net/netfilter/nf_tables_offload.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								net/netfilter/nf_tables_offload.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,267 @@
 | 
			
		|||
/* 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 @@
 | 
			
		|||
#include <linux/netfilter.h>
 | 
			
		||||
#include <linux/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_core.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_offload.h>
 | 
			
		||||
#include <net/netfilter/nf_tables.h>
 | 
			
		||||
 | 
			
		||||
struct nft_cmp_expr {
 | 
			
		||||
| 
						 | 
				
			
			@ -107,12 +108,44 @@ static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
 | 
			
		|||
	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, ®->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 = {
 | 
			
		||||
	.type		= &nft_cmp_type,
 | 
			
		||||
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
 | 
			
		||||
	.eval		= nft_cmp_eval,
 | 
			
		||||
	.init		= nft_cmp_init,
 | 
			
		||||
	.dump		= nft_cmp_dump,
 | 
			
		||||
	.offload	= nft_cmp_offload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
 | 
			
		||||
| 
						 | 
				
			
			@ -169,6 +221,7 @@ const struct nft_expr_ops nft_cmp_fast_ops = {
 | 
			
		|||
	.eval		= NULL,	/* inlined */
 | 
			
		||||
	.init		= nft_cmp_fast_init,
 | 
			
		||||
	.dump		= nft_cmp_fast_dump,
 | 
			
		||||
	.offload	= nft_cmp_fast_offload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct nft_expr_ops *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
#include <linux/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_core.h>
 | 
			
		||||
#include <net/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_offload.h>
 | 
			
		||||
 | 
			
		||||
void nft_immediate_eval(const struct nft_expr *expr,
 | 
			
		||||
			struct nft_regs *regs,
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +125,34 @@ static int nft_immediate_validate(const struct nft_ctx *ctx,
 | 
			
		|||
	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 = {
 | 
			
		||||
	.type		= &nft_imm_type,
 | 
			
		||||
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +162,8 @@ static const struct nft_expr_ops nft_imm_ops = {
 | 
			
		|||
	.deactivate	= nft_immediate_deactivate,
 | 
			
		||||
	.dump		= nft_immediate_dump,
 | 
			
		||||
	.validate	= nft_immediate_validate,
 | 
			
		||||
	.offload	= nft_immediate_offload,
 | 
			
		||||
	.offload_flags	= NFT_OFFLOAD_F_ACTION,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nft_expr_type nft_imm_type __read_mostly = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@
 | 
			
		|||
#include <net/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_core.h>
 | 
			
		||||
#include <net/netfilter/nft_meta.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_offload.h>
 | 
			
		||||
 | 
			
		||||
#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -490,6 +491,31 @@ void nft_meta_set_destroy(const struct nft_ctx *ctx,
 | 
			
		|||
}
 | 
			
		||||
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 = {
 | 
			
		||||
	.type		= &nft_meta_type,
 | 
			
		||||
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
 | 
			
		||||
| 
						 | 
				
			
			@ -497,6 +523,7 @@ static const struct nft_expr_ops nft_meta_get_ops = {
 | 
			
		|||
	.init		= nft_meta_get_init,
 | 
			
		||||
	.dump		= nft_meta_get_dump,
 | 
			
		||||
	.validate	= nft_meta_get_validate,
 | 
			
		||||
	.offload	= nft_meta_get_offload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct nft_expr_ops nft_meta_set_ops = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,10 +15,13 @@
 | 
			
		|||
#include <linux/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_core.h>
 | 
			
		||||
#include <net/netfilter/nf_tables.h>
 | 
			
		||||
#include <net/netfilter/nf_tables_offload.h>
 | 
			
		||||
/* For layer 4 checksum field offset. */
 | 
			
		||||
#include <linux/tcp.h>
 | 
			
		||||
#include <linux/udp.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 */
 | 
			
		||||
static bool
 | 
			
		||||
| 
						 | 
				
			
			@ -150,12 +153,195 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
 | 
			
		|||
	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 = {
 | 
			
		||||
	.type		= &nft_payload_type,
 | 
			
		||||
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_payload)),
 | 
			
		||||
	.eval		= nft_payload_eval,
 | 
			
		||||
	.init		= nft_payload_init,
 | 
			
		||||
	.dump		= nft_payload_dump,
 | 
			
		||||
	.offload	= nft_payload_offload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
	.init		= nft_payload_init,
 | 
			
		||||
	.dump		= nft_payload_dump,
 | 
			
		||||
	.offload	= nft_payload_offload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue