Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
Pablo Neira Ayuso says:
====================
Netfilter updates for net-next
The following patchset contains Netfilter updates for your net-next tree.
Basically, nf_tables updates to add the set extension infrastructure and finish
the transaction for sets from Patrick McHardy. More specifically, they are:
1) Move netns to basechain and use recently added possible_net_t, from
   Patrick McHardy.
2) Use LOGLEVEL_<FOO> from nf_log infrastructure, from Joe Perches.
3) Restore nf_log_trace that was accidentally removed during conflict
   resolution.
4) nft_queue does not depend on NETFILTER_XTABLES, starting from here
   all patches from Patrick McHardy.
5) Use raw_smp_processor_id() in nft_meta.
Then, several patches to prepare ground for the new set extension
infrastructure:
6) Pass object length to the hash callback in rhashtable as needed by
   the new set extension infrastructure.
7) Cleanup patch to restore struct nft_hash as wrapper for struct
   rhashtable
8) Another small source code readability cleanup for nft_hash.
9) Convert nft_hash to rhashtable callbacks.
And finally...
10) Add the new set extension infrastructure.
11) Convert the nft_hash and nft_rbtree sets to use it.
12) Batch set element release to avoid several RCU grace period in a row
    and add new function nft_set_elem_destroy() to consolidate set element
    release.
13) Return the set extension data area from nft_lookup.
14) Refactor existing transaction code to add some helper functions
    and document it.
15) Complete the set transaction support, using similar approach to what we
    already use, to activate/deactivate elements in an atomic fashion.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
						commit
						4ef295e047
					
				| @ -88,7 +88,7 @@ struct rhashtable_compare_arg { | ||||
| }; | ||||
| 
 | ||||
| typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed); | ||||
| typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed); | ||||
| typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 len, u32 seed); | ||||
| typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg, | ||||
| 			       const void *obj); | ||||
| 
 | ||||
| @ -242,7 +242,9 @@ static inline unsigned int rht_head_hashfn( | ||||
| 	const char *ptr = rht_obj(ht, he); | ||||
| 
 | ||||
| 	return likely(params.obj_hashfn) ? | ||||
| 	       rht_bucket_index(tbl, params.obj_hashfn(ptr, tbl->hash_rnd)) : | ||||
| 	       rht_bucket_index(tbl, params.obj_hashfn(ptr, params.key_len ?: | ||||
| 							    ht->p.key_len, | ||||
| 						       tbl->hash_rnd)) : | ||||
| 	       rht_key_hashfn(ht, tbl, ptr + params.key_offset, params); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -138,19 +138,12 @@ struct nft_userdata { | ||||
| /**
 | ||||
|  *	struct nft_set_elem - generic representation of set elements | ||||
|  * | ||||
|  *	@cookie: implementation specific element cookie | ||||
|  *	@key: element key | ||||
|  *	@data: element data (maps only) | ||||
|  *	@flags: element flags (end of interval) | ||||
|  * | ||||
|  *	The cookie can be used to store a handle to the element for subsequent | ||||
|  *	removal. | ||||
|  *	@priv: element private data and extensions | ||||
|  */ | ||||
| struct nft_set_elem { | ||||
| 	void			*cookie; | ||||
| 	struct nft_data		key; | ||||
| 	struct nft_data		data; | ||||
| 	u32			flags; | ||||
| 	void			*priv; | ||||
| }; | ||||
| 
 | ||||
| struct nft_set; | ||||
| @ -202,11 +195,15 @@ struct nft_set_estimate { | ||||
| 	enum nft_set_class	class; | ||||
| }; | ||||
| 
 | ||||
| struct nft_set_ext; | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_set_ops - nf_tables set operations | ||||
|  * | ||||
|  *	@lookup: look up an element within the set | ||||
|  *	@insert: insert new element into set | ||||
|  *	@activate: activate new element in the next generation | ||||
|  *	@deactivate: deactivate element in the next generation | ||||
|  *	@remove: remove element from set | ||||
|  *	@walk: iterate over all set elemeennts | ||||
|  *	@privsize: function to return size of set private data | ||||
| @ -214,16 +211,19 @@ struct nft_set_estimate { | ||||
|  *	@destroy: destroy private data of set instance | ||||
|  *	@list: nf_tables_set_ops list node | ||||
|  *	@owner: module reference | ||||
|  *	@elemsize: element private size | ||||
|  *	@features: features supported by the implementation | ||||
|  */ | ||||
| struct nft_set_ops { | ||||
| 	bool				(*lookup)(const struct nft_set *set, | ||||
| 						  const struct nft_data *key, | ||||
| 						  struct nft_data *data); | ||||
| 	int				(*get)(const struct nft_set *set, | ||||
| 					       struct nft_set_elem *elem); | ||||
| 						  const struct nft_set_ext **ext); | ||||
| 	int				(*insert)(const struct nft_set *set, | ||||
| 						  const struct nft_set_elem *elem); | ||||
| 	void				(*activate)(const struct nft_set *set, | ||||
| 						    const struct nft_set_elem *elem); | ||||
| 	void *				(*deactivate)(const struct nft_set *set, | ||||
| 						      const struct nft_set_elem *elem); | ||||
| 	void				(*remove)(const struct nft_set *set, | ||||
| 						  const struct nft_set_elem *elem); | ||||
| 	void				(*walk)(const struct nft_ctx *ctx, | ||||
| @ -241,6 +241,7 @@ struct nft_set_ops { | ||||
| 
 | ||||
| 	struct list_head		list; | ||||
| 	struct module			*owner; | ||||
| 	unsigned int			elemsize; | ||||
| 	u32				features; | ||||
| }; | ||||
| 
 | ||||
| @ -259,6 +260,7 @@ void nft_unregister_set(struct nft_set_ops *ops); | ||||
|  * 	@nelems: number of elements | ||||
|  *	@policy: set parameterization (see enum nft_set_policies) | ||||
|  * 	@ops: set ops | ||||
|  * 	@pnet: network namespace | ||||
|  * 	@flags: set flags | ||||
|  * 	@klen: key length | ||||
|  * 	@dlen: data length | ||||
| @ -275,6 +277,7 @@ struct nft_set { | ||||
| 	u16				policy; | ||||
| 	/* runtime data below here */ | ||||
| 	const struct nft_set_ops	*ops ____cacheline_aligned; | ||||
| 	possible_net_t			pnet; | ||||
| 	u16				flags; | ||||
| 	u8				klen; | ||||
| 	u8				dlen; | ||||
| @ -311,6 +314,121 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, | ||||
| void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, | ||||
| 			  struct nft_set_binding *binding); | ||||
| 
 | ||||
| /**
 | ||||
|  *	enum nft_set_extensions - set extension type IDs | ||||
|  * | ||||
|  *	@NFT_SET_EXT_KEY: element key | ||||
|  *	@NFT_SET_EXT_DATA: mapping data | ||||
|  *	@NFT_SET_EXT_FLAGS: element flags | ||||
|  *	@NFT_SET_EXT_NUM: number of extension types | ||||
|  */ | ||||
| enum nft_set_extensions { | ||||
| 	NFT_SET_EXT_KEY, | ||||
| 	NFT_SET_EXT_DATA, | ||||
| 	NFT_SET_EXT_FLAGS, | ||||
| 	NFT_SET_EXT_NUM | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_set_ext_type - set extension type | ||||
|  * | ||||
|  * 	@len: fixed part length of the extension | ||||
|  * 	@align: alignment requirements of the extension | ||||
|  */ | ||||
| struct nft_set_ext_type { | ||||
| 	u8	len; | ||||
| 	u8	align; | ||||
| }; | ||||
| 
 | ||||
| extern const struct nft_set_ext_type nft_set_ext_types[]; | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_set_ext_tmpl - set extension template | ||||
|  * | ||||
|  *	@len: length of extension area | ||||
|  *	@offset: offsets of individual extension types | ||||
|  */ | ||||
| struct nft_set_ext_tmpl { | ||||
| 	u16	len; | ||||
| 	u8	offset[NFT_SET_EXT_NUM]; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_set_ext - set extensions | ||||
|  * | ||||
|  *	@genmask: generation mask | ||||
|  *	@offset: offsets of individual extension types | ||||
|  *	@data: beginning of extension data | ||||
|  */ | ||||
| struct nft_set_ext { | ||||
| 	u8	genmask; | ||||
| 	u8	offset[NFT_SET_EXT_NUM]; | ||||
| 	char	data[0]; | ||||
| }; | ||||
| 
 | ||||
| static inline void nft_set_ext_prepare(struct nft_set_ext_tmpl *tmpl) | ||||
| { | ||||
| 	memset(tmpl, 0, sizeof(*tmpl)); | ||||
| 	tmpl->len = sizeof(struct nft_set_ext); | ||||
| } | ||||
| 
 | ||||
| static inline void nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, | ||||
| 					  unsigned int len) | ||||
| { | ||||
| 	tmpl->len	 = ALIGN(tmpl->len, nft_set_ext_types[id].align); | ||||
| 	BUG_ON(tmpl->len > U8_MAX); | ||||
| 	tmpl->offset[id] = tmpl->len; | ||||
| 	tmpl->len	+= nft_set_ext_types[id].len + len; | ||||
| } | ||||
| 
 | ||||
| static inline void nft_set_ext_add(struct nft_set_ext_tmpl *tmpl, u8 id) | ||||
| { | ||||
| 	nft_set_ext_add_length(tmpl, id, 0); | ||||
| } | ||||
| 
 | ||||
| static inline void nft_set_ext_init(struct nft_set_ext *ext, | ||||
| 				    const struct nft_set_ext_tmpl *tmpl) | ||||
| { | ||||
| 	memcpy(ext->offset, tmpl->offset, sizeof(ext->offset)); | ||||
| } | ||||
| 
 | ||||
| static inline bool __nft_set_ext_exists(const struct nft_set_ext *ext, u8 id) | ||||
| { | ||||
| 	return !!ext->offset[id]; | ||||
| } | ||||
| 
 | ||||
| static inline bool nft_set_ext_exists(const struct nft_set_ext *ext, u8 id) | ||||
| { | ||||
| 	return ext && __nft_set_ext_exists(ext, id); | ||||
| } | ||||
| 
 | ||||
| static inline void *nft_set_ext(const struct nft_set_ext *ext, u8 id) | ||||
| { | ||||
| 	return (void *)ext + ext->offset[id]; | ||||
| } | ||||
| 
 | ||||
| static inline struct nft_data *nft_set_ext_key(const struct nft_set_ext *ext) | ||||
| { | ||||
| 	return nft_set_ext(ext, NFT_SET_EXT_KEY); | ||||
| } | ||||
| 
 | ||||
| static inline struct nft_data *nft_set_ext_data(const struct nft_set_ext *ext) | ||||
| { | ||||
| 	return nft_set_ext(ext, NFT_SET_EXT_DATA); | ||||
| } | ||||
| 
 | ||||
| static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext) | ||||
| { | ||||
| 	return nft_set_ext(ext, NFT_SET_EXT_FLAGS); | ||||
| } | ||||
| 
 | ||||
| static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, | ||||
| 						   void *elem) | ||||
| { | ||||
| 	return elem + set->ops->elemsize; | ||||
| } | ||||
| 
 | ||||
| void nft_set_elem_destroy(const struct nft_set *set, void *elem); | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_expr_type - nf_tables expression type | ||||
| @ -449,7 +567,6 @@ enum nft_chain_flags { | ||||
|  * | ||||
|  *	@rules: list of rules in the chain | ||||
|  *	@list: used internally | ||||
|  *	@net: net namespace that this chain belongs to | ||||
|  *	@table: table that this chain belongs to | ||||
|  *	@handle: chain handle | ||||
|  *	@use: number of jump references to this chain | ||||
| @ -460,7 +577,6 @@ enum nft_chain_flags { | ||||
| struct nft_chain { | ||||
| 	struct list_head		rules; | ||||
| 	struct list_head		list; | ||||
| 	struct net			*net; | ||||
| 	struct nft_table		*table; | ||||
| 	u64				handle; | ||||
| 	u32				use; | ||||
| @ -512,6 +628,7 @@ struct nft_stats { | ||||
|  *	struct nft_base_chain - nf_tables base chain | ||||
|  * | ||||
|  *	@ops: netfilter hook ops | ||||
|  *	@pnet: net namespace that this chain belongs to | ||||
|  *	@type: chain type | ||||
|  *	@policy: default policy | ||||
|  *	@stats: per-cpu chain stats | ||||
| @ -519,6 +636,7 @@ struct nft_stats { | ||||
|  */ | ||||
| struct nft_base_chain { | ||||
| 	struct nf_hook_ops		ops[NFT_HOOK_OPS_MAX]; | ||||
| 	possible_net_t			pnet; | ||||
| 	const struct nf_chain_type	*type; | ||||
| 	u8				policy; | ||||
| 	struct nft_stats __percpu	*stats; | ||||
| @ -605,6 +723,50 @@ void nft_unregister_expr(struct nft_expr_type *); | ||||
| #define MODULE_ALIAS_NFT_SET() \ | ||||
| 	MODULE_ALIAS("nft-set") | ||||
| 
 | ||||
| /*
 | ||||
|  * The gencursor defines two generations, the currently active and the | ||||
|  * next one. Objects contain a bitmask of 2 bits specifying the generations | ||||
|  * they're active in. A set bit means they're inactive in the generation | ||||
|  * represented by that bit. | ||||
|  * | ||||
|  * New objects start out as inactive in the current and active in the | ||||
|  * next generation. When committing the ruleset the bitmask is cleared, | ||||
|  * meaning they're active in all generations. When removing an object, | ||||
|  * it is set inactive in the next generation. After committing the ruleset, | ||||
|  * the objects are removed. | ||||
|  */ | ||||
| static inline unsigned int nft_gencursor_next(const struct net *net) | ||||
| { | ||||
| 	return net->nft.gencursor + 1 == 1 ? 1 : 0; | ||||
| } | ||||
| 
 | ||||
| static inline u8 nft_genmask_next(const struct net *net) | ||||
| { | ||||
| 	return 1 << nft_gencursor_next(net); | ||||
| } | ||||
| 
 | ||||
| static inline u8 nft_genmask_cur(const struct net *net) | ||||
| { | ||||
| 	/* Use ACCESS_ONCE() to prevent refetching the value for atomicity */ | ||||
| 	return 1 << ACCESS_ONCE(net->nft.gencursor); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Set element transaction helpers | ||||
|  */ | ||||
| 
 | ||||
| static inline bool nft_set_elem_active(const struct nft_set_ext *ext, | ||||
| 				       u8 genmask) | ||||
| { | ||||
| 	return !(ext->genmask & genmask); | ||||
| } | ||||
| 
 | ||||
| static inline void nft_set_elem_change_active(const struct nft_set *set, | ||||
| 					      struct nft_set_ext *ext) | ||||
| { | ||||
| 	ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	struct nft_trans - nf_tables object update in transaction | ||||
|  * | ||||
|  | ||||
| @ -691,7 +691,7 @@ static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed) | ||||
|  *	struct rhash_head	node; | ||||
|  * }; | ||||
|  * | ||||
|  * u32 my_hash_fn(const void *data, u32 seed) | ||||
|  * u32 my_hash_fn(const void *data, u32 len, u32 seed) | ||||
|  * { | ||||
|  *	struct test_obj *obj = data; | ||||
|  * | ||||
|  | ||||
| @ -10,8 +10,10 @@ | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/skbuff.h> | ||||
| @ -27,7 +29,7 @@ static struct nf_loginfo default_loginfo = { | ||||
| 	.type	= NF_LOG_TYPE_LOG, | ||||
| 	.u = { | ||||
| 		.log = { | ||||
| 			.level	  = 5, | ||||
| 			.level	  = LOGLEVEL_NOTICE, | ||||
| 			.logflags = NF_LOG_MASK, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| @ -5,8 +5,10 @@ | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/skbuff.h> | ||||
| @ -26,7 +28,7 @@ static struct nf_loginfo default_loginfo = { | ||||
| 	.type	= NF_LOG_TYPE_LOG, | ||||
| 	.u = { | ||||
| 		.log = { | ||||
| 			.level	  = 5, | ||||
| 			.level	  = LOGLEVEL_NOTICE, | ||||
| 			.logflags = NF_LOG_MASK, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| @ -9,7 +9,10 @@ | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/capability.h> | ||||
| #include <linux/in.h> | ||||
| #include <linux/skbuff.h> | ||||
| @ -234,7 +237,7 @@ static struct nf_loginfo trace_loginfo = { | ||||
| 	.type = NF_LOG_TYPE_LOG, | ||||
| 	.u = { | ||||
| 		.log = { | ||||
| 			.level = 4, | ||||
| 			.level = LOGLEVEL_WARNING, | ||||
| 			.logflags = NF_LOG_MASK, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| @ -5,8 +5,10 @@ | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/skbuff.h> | ||||
| @ -27,7 +29,7 @@ static struct nf_loginfo default_loginfo = { | ||||
| 	.type	= NF_LOG_TYPE_LOG, | ||||
| 	.u = { | ||||
| 		.log = { | ||||
| 			.level	  = 5, | ||||
| 			.level	  = LOGLEVEL_NOTICE, | ||||
| 			.logflags = NF_LOG_MASK, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| @ -522,7 +522,6 @@ config NFT_NAT | ||||
| 	  typical Network Address Translation (NAT) packet transformations. | ||||
| 
 | ||||
| config NFT_QUEUE | ||||
| 	depends on NETFILTER_XTABLES | ||||
| 	depends on NETFILTER_NETLINK_QUEUE | ||||
| 	tristate "Netfilter nf_tables queue module" | ||||
| 	help | ||||
|  | ||||
| @ -198,36 +198,31 @@ static int nft_delchain(struct nft_ctx *ctx) | ||||
| static inline bool | ||||
| nft_rule_is_active(struct net *net, const struct nft_rule *rule) | ||||
| { | ||||
| 	return (rule->genmask & (1 << net->nft.gencursor)) == 0; | ||||
| } | ||||
| 
 | ||||
| static inline int gencursor_next(struct net *net) | ||||
| { | ||||
| 	return net->nft.gencursor+1 == 1 ? 1 : 0; | ||||
| 	return (rule->genmask & nft_genmask_cur(net)) == 0; | ||||
| } | ||||
| 
 | ||||
| static inline int | ||||
| nft_rule_is_active_next(struct net *net, const struct nft_rule *rule) | ||||
| { | ||||
| 	return (rule->genmask & (1 << gencursor_next(net))) == 0; | ||||
| 	return (rule->genmask & nft_genmask_next(net)) == 0; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| nft_rule_activate_next(struct net *net, struct nft_rule *rule) | ||||
| { | ||||
| 	/* Now inactive, will be active in the future */ | ||||
| 	rule->genmask = (1 << net->nft.gencursor); | ||||
| 	rule->genmask = nft_genmask_cur(net); | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| nft_rule_deactivate_next(struct net *net, struct nft_rule *rule) | ||||
| { | ||||
| 	rule->genmask = (1 << gencursor_next(net)); | ||||
| 	rule->genmask = nft_genmask_next(net); | ||||
| } | ||||
| 
 | ||||
| static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) | ||||
| { | ||||
| 	rule->genmask &= ~(1 << gencursor_next(net)); | ||||
| 	rule->genmask &= ~nft_genmask_next(net); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| @ -1354,6 +1349,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | ||||
| 			rcu_assign_pointer(basechain->stats, stats); | ||||
| 		} | ||||
| 
 | ||||
| 		write_pnet(&basechain->pnet, net); | ||||
| 		basechain->type = type; | ||||
| 		chain = &basechain->chain; | ||||
| 
 | ||||
| @ -1381,7 +1377,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&chain->rules); | ||||
| 	chain->handle = nf_tables_alloc_handle(table); | ||||
| 	chain->net = net; | ||||
| 	chain->table = table; | ||||
| 	nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); | ||||
| 
 | ||||
| @ -2695,6 +2690,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&set->bindings); | ||||
| 	write_pnet(&set->pnet, net); | ||||
| 	set->ops   = ops; | ||||
| 	set->ktype = ktype; | ||||
| 	set->klen  = desc.klen; | ||||
| @ -2771,10 +2767,11 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx, | ||||
| 					const struct nft_set_iter *iter, | ||||
| 					const struct nft_set_elem *elem) | ||||
| { | ||||
| 	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); | ||||
| 	enum nft_registers dreg; | ||||
| 
 | ||||
| 	dreg = nft_type_to_reg(set->dtype); | ||||
| 	return nft_validate_data_load(ctx, dreg, &elem->data, | ||||
| 	return nft_validate_data_load(ctx, dreg, nft_set_ext_data(ext), | ||||
| 				      set->dtype == NFT_DATA_VERDICT ? | ||||
| 				      NFT_DATA_VERDICT : NFT_DATA_VALUE); | ||||
| } | ||||
| @ -2827,6 +2824,22 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, | ||||
| 		nf_tables_set_destroy(ctx, set); | ||||
| } | ||||
| 
 | ||||
| const struct nft_set_ext_type nft_set_ext_types[] = { | ||||
| 	[NFT_SET_EXT_KEY]		= { | ||||
| 		.len	= sizeof(struct nft_data), | ||||
| 		.align	= __alignof__(struct nft_data), | ||||
| 	}, | ||||
| 	[NFT_SET_EXT_DATA]		= { | ||||
| 		.len	= sizeof(struct nft_data), | ||||
| 		.align	= __alignof__(struct nft_data), | ||||
| 	}, | ||||
| 	[NFT_SET_EXT_FLAGS]		= { | ||||
| 		.len	= sizeof(u8), | ||||
| 		.align	= __alignof__(u8), | ||||
| 	}, | ||||
| }; | ||||
| EXPORT_SYMBOL_GPL(nft_set_ext_types); | ||||
| 
 | ||||
| /*
 | ||||
|  * Set elements | ||||
|  */ | ||||
| @ -2873,6 +2886,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, | ||||
| 				  const struct nft_set *set, | ||||
| 				  const struct nft_set_elem *elem) | ||||
| { | ||||
| 	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); | ||||
| 	unsigned char *b = skb_tail_pointer(skb); | ||||
| 	struct nlattr *nest; | ||||
| 
 | ||||
| @ -2880,20 +2894,20 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, | ||||
| 	if (nest == NULL) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE, | ||||
| 			  set->klen) < 0) | ||||
| 	if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext), | ||||
| 			  NFT_DATA_VALUE, set->klen) < 0) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	if (set->flags & NFT_SET_MAP && | ||||
| 	    !(elem->flags & NFT_SET_ELEM_INTERVAL_END) && | ||||
| 	    nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data, | ||||
| 	if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && | ||||
| 	    nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext), | ||||
| 			  set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE, | ||||
| 			  set->dlen) < 0) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	if (elem->flags != 0) | ||||
| 		if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags))) | ||||
| 			goto nla_put_failure; | ||||
| 	if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && | ||||
| 	    nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, | ||||
| 		         htonl(*nft_set_ext_flags(ext)))) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	nla_nest_end(skb, nest); | ||||
| 	return 0; | ||||
| @ -3114,15 +3128,54 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx, | ||||
| 	return trans; | ||||
| } | ||||
| 
 | ||||
| static void *nft_set_elem_init(const struct nft_set *set, | ||||
| 			       const struct nft_set_ext_tmpl *tmpl, | ||||
| 			       const struct nft_data *key, | ||||
| 			       const struct nft_data *data, | ||||
| 			       gfp_t gfp) | ||||
| { | ||||
| 	struct nft_set_ext *ext; | ||||
| 	void *elem; | ||||
| 
 | ||||
| 	elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); | ||||
| 	if (elem == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	ext = nft_set_elem_ext(set, elem); | ||||
| 	nft_set_ext_init(ext, tmpl); | ||||
| 
 | ||||
| 	memcpy(nft_set_ext_key(ext), key, set->klen); | ||||
| 	if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) | ||||
| 		memcpy(nft_set_ext_data(ext), data, set->dlen); | ||||
| 
 | ||||
| 	return elem; | ||||
| } | ||||
| 
 | ||||
| void nft_set_elem_destroy(const struct nft_set *set, void *elem) | ||||
| { | ||||
| 	struct nft_set_ext *ext = nft_set_elem_ext(set, elem); | ||||
| 
 | ||||
| 	nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE); | ||||
| 	if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) | ||||
| 		nft_data_uninit(nft_set_ext_data(ext), set->dtype); | ||||
| 
 | ||||
| 	kfree(elem); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(nft_set_elem_destroy); | ||||
| 
 | ||||
| static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, | ||||
| 			    const struct nlattr *attr) | ||||
| { | ||||
| 	struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; | ||||
| 	struct nft_data_desc d1, d2; | ||||
| 	struct nft_set_ext_tmpl tmpl; | ||||
| 	struct nft_set_ext *ext; | ||||
| 	struct nft_set_elem elem; | ||||
| 	struct nft_set_binding *binding; | ||||
| 	struct nft_data data; | ||||
| 	enum nft_registers dreg; | ||||
| 	struct nft_trans *trans; | ||||
| 	u32 flags; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (set->size && set->nelems == set->size) | ||||
| @ -3136,22 +3189,26 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, | ||||
| 	if (nla[NFTA_SET_ELEM_KEY] == NULL) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	elem.flags = 0; | ||||
| 	nft_set_ext_prepare(&tmpl); | ||||
| 
 | ||||
| 	flags = 0; | ||||
| 	if (nla[NFTA_SET_ELEM_FLAGS] != NULL) { | ||||
| 		elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); | ||||
| 		if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END) | ||||
| 		flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); | ||||
| 		if (flags & ~NFT_SET_ELEM_INTERVAL_END) | ||||
| 			return -EINVAL; | ||||
| 		if (!(set->flags & NFT_SET_INTERVAL) && | ||||
| 		    elem.flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 		    flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 			return -EINVAL; | ||||
| 		if (flags != 0) | ||||
| 			nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS); | ||||
| 	} | ||||
| 
 | ||||
| 	if (set->flags & NFT_SET_MAP) { | ||||
| 		if (nla[NFTA_SET_ELEM_DATA] == NULL && | ||||
| 		    !(elem.flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 		    !(flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 			return -EINVAL; | ||||
| 		if (nla[NFTA_SET_ELEM_DATA] != NULL && | ||||
| 		    elem.flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 		    flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 			return -EINVAL; | ||||
| 	} else { | ||||
| 		if (nla[NFTA_SET_ELEM_DATA] != NULL) | ||||
| @ -3165,12 +3222,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, | ||||
| 	if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	err = -EEXIST; | ||||
| 	if (set->ops->get(set, &elem) == 0) | ||||
| 		goto err2; | ||||
| 	nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY); | ||||
| 
 | ||||
| 	if (nla[NFTA_SET_ELEM_DATA] != NULL) { | ||||
| 		err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]); | ||||
| 		err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]); | ||||
| 		if (err < 0) | ||||
| 			goto err2; | ||||
| 
 | ||||
| @ -3187,29 +3242,43 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, | ||||
| 			}; | ||||
| 
 | ||||
| 			err = nft_validate_data_load(&bind_ctx, dreg, | ||||
| 						     &elem.data, d2.type); | ||||
| 						     &data, d2.type); | ||||
| 			if (err < 0) | ||||
| 				goto err3; | ||||
| 		} | ||||
| 
 | ||||
| 		nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA); | ||||
| 	} | ||||
| 
 | ||||
| 	err = -ENOMEM; | ||||
| 	elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL); | ||||
| 	if (elem.priv == NULL) | ||||
| 		goto err3; | ||||
| 
 | ||||
| 	ext = nft_set_elem_ext(set, elem.priv); | ||||
| 	if (flags) | ||||
| 		*nft_set_ext_flags(ext) = flags; | ||||
| 
 | ||||
| 	trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); | ||||
| 	if (trans == NULL) | ||||
| 		goto err3; | ||||
| 		goto err4; | ||||
| 
 | ||||
| 	ext->genmask = nft_genmask_cur(ctx->net); | ||||
| 	err = set->ops->insert(set, &elem); | ||||
| 	if (err < 0) | ||||
| 		goto err4; | ||||
| 		goto err5; | ||||
| 
 | ||||
| 	nft_trans_elem(trans) = elem; | ||||
| 	list_add_tail(&trans->list, &ctx->net->nft.commit_list); | ||||
| 	return 0; | ||||
| 
 | ||||
| err4: | ||||
| err5: | ||||
| 	kfree(trans); | ||||
| err4: | ||||
| 	kfree(elem.priv); | ||||
| err3: | ||||
| 	if (nla[NFTA_SET_ELEM_DATA] != NULL) | ||||
| 		nft_data_uninit(&elem.data, d2.type); | ||||
| 		nft_data_uninit(&data, d2.type); | ||||
| err2: | ||||
| 	nft_data_uninit(&elem.key, d1.type); | ||||
| err1: | ||||
| @ -3282,19 +3351,24 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, | ||||
| 	if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	err = set->ops->get(set, &elem); | ||||
| 	if (err < 0) | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set); | ||||
| 	if (trans == NULL) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto err2; | ||||
| 	} | ||||
| 
 | ||||
| 	elem.priv = set->ops->deactivate(set, &elem); | ||||
| 	if (elem.priv == NULL) { | ||||
| 		err = -ENOENT; | ||||
| 		goto err3; | ||||
| 	} | ||||
| 
 | ||||
| 	nft_trans_elem(trans) = elem; | ||||
| 	list_add_tail(&trans->list, &ctx->net->nft.commit_list); | ||||
| 	return 0; | ||||
| 
 | ||||
| err3: | ||||
| 	kfree(trans); | ||||
| err2: | ||||
| 	nft_data_uninit(&elem.key, desc.type); | ||||
| err1: | ||||
| @ -3532,6 +3606,10 @@ static void nf_tables_commit_release(struct nft_trans *trans) | ||||
| 	case NFT_MSG_DELSET: | ||||
| 		nft_set_destroy(nft_trans_set(trans)); | ||||
| 		break; | ||||
| 	case NFT_MSG_DELSETELEM: | ||||
| 		nft_set_elem_destroy(nft_trans_elem_set(trans), | ||||
| 				     nft_trans_elem(trans).priv); | ||||
| 		break; | ||||
| 	} | ||||
| 	kfree(trans); | ||||
| } | ||||
| @ -3546,7 +3624,7 @@ static int nf_tables_commit(struct sk_buff *skb) | ||||
| 	while (++net->nft.base_seq == 0); | ||||
| 
 | ||||
| 	/* A new generation has just started */ | ||||
| 	net->nft.gencursor = gencursor_next(net); | ||||
| 	net->nft.gencursor = nft_gencursor_next(net); | ||||
| 
 | ||||
| 	/* Make sure all packets have left the previous generation before
 | ||||
| 	 * purging old rules. | ||||
| @ -3617,24 +3695,21 @@ static int nf_tables_commit(struct sk_buff *skb) | ||||
| 					     NFT_MSG_DELSET, GFP_KERNEL); | ||||
| 			break; | ||||
| 		case NFT_MSG_NEWSETELEM: | ||||
| 			nf_tables_setelem_notify(&trans->ctx, | ||||
| 						 nft_trans_elem_set(trans), | ||||
| 						 &nft_trans_elem(trans), | ||||
| 			te = (struct nft_trans_elem *)trans->data; | ||||
| 
 | ||||
| 			te->set->ops->activate(te->set, &te->elem); | ||||
| 			nf_tables_setelem_notify(&trans->ctx, te->set, | ||||
| 						 &te->elem, | ||||
| 						 NFT_MSG_NEWSETELEM, 0); | ||||
| 			nft_trans_destroy(trans); | ||||
| 			break; | ||||
| 		case NFT_MSG_DELSETELEM: | ||||
| 			te = (struct nft_trans_elem *)trans->data; | ||||
| 
 | ||||
| 			nf_tables_setelem_notify(&trans->ctx, te->set, | ||||
| 						 &te->elem, | ||||
| 						 NFT_MSG_DELSETELEM, 0); | ||||
| 			te->set->ops->get(te->set, &te->elem); | ||||
| 			nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); | ||||
| 			if (te->set->flags & NFT_SET_MAP && | ||||
| 			    !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 				nft_data_uninit(&te->elem.data, te->set->dtype); | ||||
| 			te->set->ops->remove(te->set, &te->elem); | ||||
| 			nft_trans_destroy(trans); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| @ -3666,6 +3741,10 @@ static void nf_tables_abort_release(struct nft_trans *trans) | ||||
| 	case NFT_MSG_NEWSET: | ||||
| 		nft_set_destroy(nft_trans_set(trans)); | ||||
| 		break; | ||||
| 	case NFT_MSG_NEWSETELEM: | ||||
| 		nft_set_elem_destroy(nft_trans_elem_set(trans), | ||||
| 				     nft_trans_elem(trans).priv); | ||||
| 		break; | ||||
| 	} | ||||
| 	kfree(trans); | ||||
| } | ||||
| @ -3736,16 +3815,15 @@ static int nf_tables_abort(struct sk_buff *skb) | ||||
| 		case NFT_MSG_NEWSETELEM: | ||||
| 			nft_trans_elem_set(trans)->nelems--; | ||||
| 			te = (struct nft_trans_elem *)trans->data; | ||||
| 			te->set->ops->get(te->set, &te->elem); | ||||
| 			nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); | ||||
| 			if (te->set->flags & NFT_SET_MAP && | ||||
| 			    !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 				nft_data_uninit(&te->elem.data, te->set->dtype); | ||||
| 
 | ||||
| 			te->set->ops->remove(te->set, &te->elem); | ||||
| 			nft_trans_destroy(trans); | ||||
| 			break; | ||||
| 		case NFT_MSG_DELSETELEM: | ||||
| 			te = (struct nft_trans_elem *)trans->data; | ||||
| 
 | ||||
| 			nft_trans_elem_set(trans)->nelems++; | ||||
| 			te->set->ops->activate(te->set, &te->elem); | ||||
| 
 | ||||
| 			nft_trans_destroy(trans); | ||||
| 			break; | ||||
| 		} | ||||
| @ -3820,13 +3898,18 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx, | ||||
| 					const struct nft_set_iter *iter, | ||||
| 					const struct nft_set_elem *elem) | ||||
| { | ||||
| 	if (elem->flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); | ||||
| 	const struct nft_data *data; | ||||
| 
 | ||||
| 	if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && | ||||
| 	    *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	switch (elem->data.verdict) { | ||||
| 	data = nft_set_ext_data(ext); | ||||
| 	switch (data->verdict) { | ||||
| 	case NFT_JUMP: | ||||
| 	case NFT_GOTO: | ||||
| 		return nf_tables_check_loops(ctx, elem->data.chain); | ||||
| 		return nf_tables_check_loops(ctx, data->chain); | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
|  * Development of this code funded by Astaro AG (http://www.astaro.com/)
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/list.h> | ||||
| @ -37,7 +38,7 @@ static struct nf_loginfo trace_loginfo = { | ||||
| 	.type = NF_LOG_TYPE_LOG, | ||||
| 	.u = { | ||||
| 		.log = { | ||||
| 			.level = 4, | ||||
| 			.level = LOGLEVEL_WARNING, | ||||
| 			.logflags = NF_LOG_MASK, | ||||
| 	        }, | ||||
| 	}, | ||||
| @ -49,10 +50,10 @@ static void __nft_trace_packet(const struct nft_pktinfo *pkt, | ||||
| { | ||||
| 	struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); | ||||
| 
 | ||||
| 	nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, | ||||
| 		      pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", | ||||
| 		      chain->table->name, chain->name, comments[type], | ||||
| 		      rulenum); | ||||
| 	nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, | ||||
| 		     pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", | ||||
| 		     chain->table->name, chain->name, comments[type], | ||||
| 		     rulenum); | ||||
| } | ||||
| 
 | ||||
| static inline void nft_trace_packet(const struct nft_pktinfo *pkt, | ||||
| @ -112,6 +113,7 @@ unsigned int | ||||
| nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) | ||||
| { | ||||
| 	const struct nft_chain *chain = ops->priv, *basechain = chain; | ||||
| 	const struct net *net = read_pnet(&nft_base_chain(basechain)->pnet); | ||||
| 	const struct nft_rule *rule; | ||||
| 	const struct nft_expr *expr, *last; | ||||
| 	struct nft_data data[NFT_REG_MAX + 1]; | ||||
| @ -119,11 +121,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) | ||||
| 	struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; | ||||
| 	struct nft_stats *stats; | ||||
| 	int rulenum; | ||||
| 	/*
 | ||||
| 	 * Cache cursor to avoid problems in case that the cursor is updated | ||||
| 	 * while traversing the ruleset. | ||||
| 	 */ | ||||
| 	unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); | ||||
| 	unsigned int gencursor = nft_genmask_cur(net); | ||||
| 
 | ||||
| do_chain: | ||||
| 	rulenum = 0; | ||||
|  | ||||
| @ -23,24 +23,65 @@ | ||||
| /* We target a hash table size of 4, element hint is 75% of final size */ | ||||
| #define NFT_HASH_ELEMENT_HINT 3 | ||||
| 
 | ||||
| struct nft_hash { | ||||
| 	struct rhashtable		ht; | ||||
| }; | ||||
| 
 | ||||
| struct nft_hash_elem { | ||||
| 	struct rhash_head		node; | ||||
| 	struct nft_data			key; | ||||
| 	struct nft_data			data[]; | ||||
| 	struct nft_set_ext		ext; | ||||
| }; | ||||
| 
 | ||||
| struct nft_hash_cmp_arg { | ||||
| 	const struct nft_set		*set; | ||||
| 	const struct nft_data		*key; | ||||
| 	u8				genmask; | ||||
| }; | ||||
| 
 | ||||
| static const struct rhashtable_params nft_hash_params; | ||||
| 
 | ||||
| static inline u32 nft_hash_key(const void *data, u32 len, u32 seed) | ||||
| { | ||||
| 	const struct nft_hash_cmp_arg *arg = data; | ||||
| 
 | ||||
| 	return jhash(arg->key, len, seed); | ||||
| } | ||||
| 
 | ||||
| static inline u32 nft_hash_obj(const void *data, u32 len, u32 seed) | ||||
| { | ||||
| 	const struct nft_hash_elem *he = data; | ||||
| 
 | ||||
| 	return jhash(nft_set_ext_key(&he->ext), len, seed); | ||||
| } | ||||
| 
 | ||||
| static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg, | ||||
| 			       const void *ptr) | ||||
| { | ||||
| 	const struct nft_hash_cmp_arg *x = arg->key; | ||||
| 	const struct nft_hash_elem *he = ptr; | ||||
| 
 | ||||
| 	if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) | ||||
| 		return 1; | ||||
| 	if (!nft_set_elem_active(&he->ext, x->genmask)) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static bool nft_hash_lookup(const struct nft_set *set, | ||||
| 			    const struct nft_data *key, | ||||
| 			    struct nft_data *data) | ||||
| 			    const struct nft_set_ext **ext) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	const struct nft_hash_elem *he; | ||||
| 	struct nft_hash_cmp_arg arg = { | ||||
| 		.genmask = nft_genmask_cur(read_pnet(&set->pnet)), | ||||
| 		.set	 = set, | ||||
| 		.key	 = key, | ||||
| 	}; | ||||
| 
 | ||||
| 	he = rhashtable_lookup_fast(priv, key, nft_hash_params); | ||||
| 	if (he && set->flags & NFT_SET_MAP) | ||||
| 		nft_data_copy(data, he->data); | ||||
| 	he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); | ||||
| 	if (he != NULL) | ||||
| 		*ext = &he->ext; | ||||
| 
 | ||||
| 	return !!he; | ||||
| } | ||||
| @ -48,79 +89,64 @@ static bool nft_hash_lookup(const struct nft_set *set, | ||||
| static int nft_hash_insert(const struct nft_set *set, | ||||
| 			   const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he; | ||||
| 	unsigned int size; | ||||
| 	int err; | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he = elem->priv; | ||||
| 	struct nft_hash_cmp_arg arg = { | ||||
| 		.genmask = nft_genmask_next(read_pnet(&set->pnet)), | ||||
| 		.set	 = set, | ||||
| 		.key	 = &elem->key, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (elem->flags != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	size = sizeof(*he); | ||||
| 	if (set->flags & NFT_SET_MAP) | ||||
| 		size += sizeof(he->data[0]); | ||||
| 
 | ||||
| 	he = kzalloc(size, GFP_KERNEL); | ||||
| 	if (he == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	nft_data_copy(&he->key, &elem->key); | ||||
| 	if (set->flags & NFT_SET_MAP) | ||||
| 		nft_data_copy(he->data, &elem->data); | ||||
| 
 | ||||
| 	err = rhashtable_insert_fast(priv, &he->node, nft_hash_params); | ||||
| 	if (err) | ||||
| 		kfree(he); | ||||
| 
 | ||||
| 	return err; | ||||
| 	return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, | ||||
| 					    nft_hash_params); | ||||
| } | ||||
| 
 | ||||
| static void nft_hash_elem_destroy(const struct nft_set *set, | ||||
| 				  struct nft_hash_elem *he) | ||||
| static void nft_hash_activate(const struct nft_set *set, | ||||
| 			      const struct nft_set_elem *elem) | ||||
| { | ||||
| 	nft_data_uninit(&he->key, NFT_DATA_VALUE); | ||||
| 	if (set->flags & NFT_SET_MAP) | ||||
| 		nft_data_uninit(he->data, set->dtype); | ||||
| 	kfree(he); | ||||
| 	struct nft_hash_elem *he = elem->priv; | ||||
| 
 | ||||
| 	nft_set_elem_change_active(set, &he->ext); | ||||
| } | ||||
| 
 | ||||
| static void *nft_hash_deactivate(const struct nft_set *set, | ||||
| 				 const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he; | ||||
| 	struct nft_hash_cmp_arg arg = { | ||||
| 		.genmask = nft_genmask_next(read_pnet(&set->pnet)), | ||||
| 		.set	 = set, | ||||
| 		.key	 = &elem->key, | ||||
| 	}; | ||||
| 
 | ||||
| 	he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); | ||||
| 	if (he != NULL) | ||||
| 		nft_set_elem_change_active(set, &he->ext); | ||||
| 
 | ||||
| 	return he; | ||||
| } | ||||
| 
 | ||||
| static void nft_hash_remove(const struct nft_set *set, | ||||
| 			    const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he = elem->priv; | ||||
| 
 | ||||
| 	rhashtable_remove_fast(priv, elem->cookie, nft_hash_params); | ||||
| 	synchronize_rcu(); | ||||
| 	kfree(elem->cookie); | ||||
| } | ||||
| 
 | ||||
| static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he; | ||||
| 
 | ||||
| 	he = rhashtable_lookup_fast(priv, &elem->key, nft_hash_params); | ||||
| 	if (!he) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	elem->cookie = he; | ||||
| 	elem->flags = 0; | ||||
| 	if (set->flags & NFT_SET_MAP) | ||||
| 		nft_data_copy(&elem->data, he->data); | ||||
| 
 | ||||
| 	return 0; | ||||
| 	rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params); | ||||
| } | ||||
| 
 | ||||
| static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, | ||||
| 			  struct nft_set_iter *iter) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	const struct nft_hash_elem *he; | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	struct nft_hash_elem *he; | ||||
| 	struct rhashtable_iter hti; | ||||
| 	struct nft_set_elem elem; | ||||
| 	u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = rhashtable_walk_init(priv, &hti); | ||||
| 	err = rhashtable_walk_init(&priv->ht, &hti); | ||||
| 	iter->err = err; | ||||
| 	if (err) | ||||
| 		return; | ||||
| @ -144,11 +170,10 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, | ||||
| 
 | ||||
| 		if (iter->count < iter->skip) | ||||
| 			goto cont; | ||||
| 		if (!nft_set_elem_active(&he->ext, genmask)) | ||||
| 			goto cont; | ||||
| 
 | ||||
| 		memcpy(&elem.key, &he->key, sizeof(elem.key)); | ||||
| 		if (set->flags & NFT_SET_MAP) | ||||
| 			memcpy(&elem.data, he->data, sizeof(elem.data)); | ||||
| 		elem.flags = 0; | ||||
| 		elem.priv = he; | ||||
| 
 | ||||
| 		iter->err = iter->fn(ctx, set, iter, &elem); | ||||
| 		if (iter->err < 0) | ||||
| @ -165,37 +190,40 @@ out: | ||||
| 
 | ||||
| static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) | ||||
| { | ||||
| 	return sizeof(struct rhashtable); | ||||
| 	return sizeof(struct nft_hash); | ||||
| } | ||||
| 
 | ||||
| static const struct rhashtable_params nft_hash_params = { | ||||
| 	.head_offset = offsetof(struct nft_hash_elem, node), | ||||
| 	.key_offset = offsetof(struct nft_hash_elem, key), | ||||
| 	.hashfn = jhash, | ||||
| 	.automatic_shrinking = true, | ||||
| 	.head_offset		= offsetof(struct nft_hash_elem, node), | ||||
| 	.hashfn			= nft_hash_key, | ||||
| 	.obj_hashfn		= nft_hash_obj, | ||||
| 	.obj_cmpfn		= nft_hash_cmp, | ||||
| 	.automatic_shrinking	= true, | ||||
| }; | ||||
| 
 | ||||
| static int nft_hash_init(const struct nft_set *set, | ||||
| 			 const struct nft_set_desc *desc, | ||||
| 			 const struct nlattr * const tb[]) | ||||
| { | ||||
| 	struct rhashtable *priv = nft_set_priv(set); | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 	struct rhashtable_params params = nft_hash_params; | ||||
| 
 | ||||
| 	params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT; | ||||
| 	params.key_len = set->klen; | ||||
| 	params.key_len	  = set->klen; | ||||
| 
 | ||||
| 	return rhashtable_init(priv, ¶ms); | ||||
| 	return rhashtable_init(&priv->ht, ¶ms); | ||||
| } | ||||
| 
 | ||||
| static void nft_free_element(void *ptr, void *arg) | ||||
| static void nft_hash_elem_destroy(void *ptr, void *arg) | ||||
| { | ||||
| 	nft_hash_elem_destroy((const struct nft_set *)arg, ptr); | ||||
| 	nft_set_elem_destroy((const struct nft_set *)arg, ptr); | ||||
| } | ||||
| 
 | ||||
| static void nft_hash_destroy(const struct nft_set *set) | ||||
| { | ||||
| 	rhashtable_free_and_destroy(nft_set_priv(set), nft_free_element, | ||||
| 	struct nft_hash *priv = nft_set_priv(set); | ||||
| 
 | ||||
| 	rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy, | ||||
| 				    (void *)set); | ||||
| } | ||||
| 
 | ||||
| @ -205,11 +233,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, | ||||
| 	unsigned int esize; | ||||
| 
 | ||||
| 	esize = sizeof(struct nft_hash_elem); | ||||
| 	if (features & NFT_SET_MAP) | ||||
| 		esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]); | ||||
| 
 | ||||
| 	if (desc->size) { | ||||
| 		est->size = sizeof(struct rhashtable) + | ||||
| 		est->size = sizeof(struct nft_hash) + | ||||
| 			    roundup_pow_of_two(desc->size * 4 / 3) * | ||||
| 			    sizeof(struct nft_hash_elem *) + | ||||
| 			    desc->size * esize; | ||||
| @ -229,11 +254,13 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, | ||||
| 
 | ||||
| static struct nft_set_ops nft_hash_ops __read_mostly = { | ||||
| 	.privsize       = nft_hash_privsize, | ||||
| 	.elemsize	= offsetof(struct nft_hash_elem, ext), | ||||
| 	.estimate	= nft_hash_estimate, | ||||
| 	.init		= nft_hash_init, | ||||
| 	.destroy	= nft_hash_destroy, | ||||
| 	.get		= nft_hash_get, | ||||
| 	.insert		= nft_hash_insert, | ||||
| 	.activate	= nft_hash_activate, | ||||
| 	.deactivate	= nft_hash_deactivate, | ||||
| 	.remove		= nft_hash_remove, | ||||
| 	.lookup		= nft_hash_lookup, | ||||
| 	.walk		= nft_hash_walk, | ||||
|  | ||||
| @ -78,7 +78,7 @@ static int nft_log_init(const struct nft_ctx *ctx, | ||||
| 			li->u.log.level = | ||||
| 				ntohl(nla_get_be32(tb[NFTA_LOG_LEVEL])); | ||||
| 		} else { | ||||
| 			li->u.log.level = 4; | ||||
| 			li->u.log.level = LOGLEVEL_WARNING; | ||||
| 		} | ||||
| 		if (tb[NFTA_LOG_FLAGS] != NULL) { | ||||
| 			li->u.log.logflags = | ||||
|  | ||||
| @ -31,9 +31,13 @@ static void nft_lookup_eval(const struct nft_expr *expr, | ||||
| { | ||||
| 	const struct nft_lookup *priv = nft_expr_priv(expr); | ||||
| 	const struct nft_set *set = priv->set; | ||||
| 	const struct nft_set_ext *ext; | ||||
| 
 | ||||
| 	if (set->ops->lookup(set, &data[priv->sreg], &data[priv->dreg])) | ||||
| 	if (set->ops->lookup(set, &data[priv->sreg], &ext)) { | ||||
| 		if (set->flags & NFT_SET_MAP) | ||||
| 			nft_data_copy(&data[priv->dreg], nft_set_ext_data(ext)); | ||||
| 		return; | ||||
| 	} | ||||
| 	data[NFT_REG_VERDICT].verdict = NFT_BREAK; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -153,7 +153,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, | ||||
| 		} | ||||
| 		break; | ||||
| 	case NFT_META_CPU: | ||||
| 		dest->data[0] = smp_processor_id(); | ||||
| 		dest->data[0] = raw_smp_processor_id(); | ||||
| 		break; | ||||
| 	case NFT_META_IIFGROUP: | ||||
| 		if (in == NULL) | ||||
|  | ||||
| @ -26,18 +26,18 @@ struct nft_rbtree { | ||||
| 
 | ||||
| struct nft_rbtree_elem { | ||||
| 	struct rb_node		node; | ||||
| 	u16			flags; | ||||
| 	struct nft_data		key; | ||||
| 	struct nft_data		data[]; | ||||
| 	struct nft_set_ext	ext; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static bool nft_rbtree_lookup(const struct nft_set *set, | ||||
| 			      const struct nft_data *key, | ||||
| 			      struct nft_data *data) | ||||
| 			      const struct nft_set_ext **ext) | ||||
| { | ||||
| 	const struct nft_rbtree *priv = nft_set_priv(set); | ||||
| 	const struct nft_rbtree_elem *rbe, *interval = NULL; | ||||
| 	const struct rb_node *parent; | ||||
| 	u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); | ||||
| 	int d; | ||||
| 
 | ||||
| 	spin_lock_bh(&nft_rbtree_lock); | ||||
| @ -45,7 +45,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set, | ||||
| 	while (parent != NULL) { | ||||
| 		rbe = rb_entry(parent, struct nft_rbtree_elem, node); | ||||
| 
 | ||||
| 		d = nft_data_cmp(&rbe->key, key, set->klen); | ||||
| 		d = nft_data_cmp(nft_set_ext_key(&rbe->ext), key, set->klen); | ||||
| 		if (d < 0) { | ||||
| 			parent = parent->rb_left; | ||||
| 			interval = rbe; | ||||
| @ -53,12 +53,17 @@ static bool nft_rbtree_lookup(const struct nft_set *set, | ||||
| 			parent = parent->rb_right; | ||||
| 		else { | ||||
| found: | ||||
| 			if (rbe->flags & NFT_SET_ELEM_INTERVAL_END) | ||||
| 			if (!nft_set_elem_active(&rbe->ext, genmask)) { | ||||
| 				parent = parent->rb_left; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) && | ||||
| 			    *nft_set_ext_flags(&rbe->ext) & | ||||
| 			    NFT_SET_ELEM_INTERVAL_END) | ||||
| 				goto out; | ||||
| 			if (set->flags & NFT_SET_MAP) | ||||
| 				nft_data_copy(data, rbe->data); | ||||
| 
 | ||||
| 			spin_unlock_bh(&nft_rbtree_lock); | ||||
| 
 | ||||
| 			*ext = &rbe->ext; | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| @ -72,23 +77,13 @@ out: | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void nft_rbtree_elem_destroy(const struct nft_set *set, | ||||
| 				    struct nft_rbtree_elem *rbe) | ||||
| { | ||||
| 	nft_data_uninit(&rbe->key, NFT_DATA_VALUE); | ||||
| 	if (set->flags & NFT_SET_MAP && | ||||
| 	    !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 		nft_data_uninit(rbe->data, set->dtype); | ||||
| 
 | ||||
| 	kfree(rbe); | ||||
| } | ||||
| 
 | ||||
| static int __nft_rbtree_insert(const struct nft_set *set, | ||||
| 			       struct nft_rbtree_elem *new) | ||||
| { | ||||
| 	struct nft_rbtree *priv = nft_set_priv(set); | ||||
| 	struct nft_rbtree_elem *rbe; | ||||
| 	struct rb_node *parent, **p; | ||||
| 	u8 genmask = nft_genmask_next(read_pnet(&set->pnet)); | ||||
| 	int d; | ||||
| 
 | ||||
| 	parent = NULL; | ||||
| @ -96,13 +91,18 @@ static int __nft_rbtree_insert(const struct nft_set *set, | ||||
| 	while (*p != NULL) { | ||||
| 		parent = *p; | ||||
| 		rbe = rb_entry(parent, struct nft_rbtree_elem, node); | ||||
| 		d = nft_data_cmp(&rbe->key, &new->key, set->klen); | ||||
| 		d = nft_data_cmp(nft_set_ext_key(&rbe->ext), | ||||
| 				 nft_set_ext_key(&new->ext), | ||||
| 				 set->klen); | ||||
| 		if (d < 0) | ||||
| 			p = &parent->rb_left; | ||||
| 		else if (d > 0) | ||||
| 			p = &parent->rb_right; | ||||
| 		else | ||||
| 			return -EEXIST; | ||||
| 		else { | ||||
| 			if (nft_set_elem_active(&rbe->ext, genmask)) | ||||
| 				return -EEXIST; | ||||
| 			p = &parent->rb_left; | ||||
| 		} | ||||
| 	} | ||||
| 	rb_link_node(&new->node, parent, p); | ||||
| 	rb_insert_color(&new->node, &priv->root); | ||||
| @ -112,31 +112,13 @@ static int __nft_rbtree_insert(const struct nft_set *set, | ||||
| static int nft_rbtree_insert(const struct nft_set *set, | ||||
| 			     const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct nft_rbtree_elem *rbe; | ||||
| 	unsigned int size; | ||||
| 	struct nft_rbtree_elem *rbe = elem->priv; | ||||
| 	int err; | ||||
| 
 | ||||
| 	size = sizeof(*rbe); | ||||
| 	if (set->flags & NFT_SET_MAP && | ||||
| 	    !(elem->flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 		size += sizeof(rbe->data[0]); | ||||
| 
 | ||||
| 	rbe = kzalloc(size, GFP_KERNEL); | ||||
| 	if (rbe == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	rbe->flags = elem->flags; | ||||
| 	nft_data_copy(&rbe->key, &elem->key); | ||||
| 	if (set->flags & NFT_SET_MAP && | ||||
| 	    !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 		nft_data_copy(rbe->data, &elem->data); | ||||
| 
 | ||||
| 	spin_lock_bh(&nft_rbtree_lock); | ||||
| 	err = __nft_rbtree_insert(set, rbe); | ||||
| 	if (err < 0) | ||||
| 		kfree(rbe); | ||||
| 
 | ||||
| 	spin_unlock_bh(&nft_rbtree_lock); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| @ -144,39 +126,49 @@ static void nft_rbtree_remove(const struct nft_set *set, | ||||
| 			      const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct nft_rbtree *priv = nft_set_priv(set); | ||||
| 	struct nft_rbtree_elem *rbe = elem->cookie; | ||||
| 	struct nft_rbtree_elem *rbe = elem->priv; | ||||
| 
 | ||||
| 	spin_lock_bh(&nft_rbtree_lock); | ||||
| 	rb_erase(&rbe->node, &priv->root); | ||||
| 	spin_unlock_bh(&nft_rbtree_lock); | ||||
| 	kfree(rbe); | ||||
| } | ||||
| 
 | ||||
| static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) | ||||
| static void nft_rbtree_activate(const struct nft_set *set, | ||||
| 				const struct nft_set_elem *elem) | ||||
| { | ||||
| 	struct nft_rbtree_elem *rbe = elem->priv; | ||||
| 
 | ||||
| 	nft_set_elem_change_active(set, &rbe->ext); | ||||
| } | ||||
| 
 | ||||
| static void *nft_rbtree_deactivate(const struct nft_set *set, | ||||
| 				   const struct nft_set_elem *elem) | ||||
| { | ||||
| 	const struct nft_rbtree *priv = nft_set_priv(set); | ||||
| 	const struct rb_node *parent = priv->root.rb_node; | ||||
| 	struct nft_rbtree_elem *rbe; | ||||
| 	u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); | ||||
| 	int d; | ||||
| 
 | ||||
| 	while (parent != NULL) { | ||||
| 		rbe = rb_entry(parent, struct nft_rbtree_elem, node); | ||||
| 
 | ||||
| 		d = nft_data_cmp(&rbe->key, &elem->key, set->klen); | ||||
| 		d = nft_data_cmp(nft_set_ext_key(&rbe->ext), &elem->key, | ||||
| 				 set->klen); | ||||
| 		if (d < 0) | ||||
| 			parent = parent->rb_left; | ||||
| 		else if (d > 0) | ||||
| 			parent = parent->rb_right; | ||||
| 		else { | ||||
| 			elem->cookie = rbe; | ||||
| 			if (set->flags & NFT_SET_MAP && | ||||
| 			    !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 				nft_data_copy(&elem->data, rbe->data); | ||||
| 			elem->flags = rbe->flags; | ||||
| 			return 0; | ||||
| 			if (!nft_set_elem_active(&rbe->ext, genmask)) { | ||||
| 				parent = parent->rb_left; | ||||
| 				continue; | ||||
| 			} | ||||
| 			nft_set_elem_change_active(set, &rbe->ext); | ||||
| 			return rbe; | ||||
| 		} | ||||
| 	} | ||||
| 	return -ENOENT; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void nft_rbtree_walk(const struct nft_ctx *ctx, | ||||
| @ -184,21 +176,21 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, | ||||
| 			    struct nft_set_iter *iter) | ||||
| { | ||||
| 	const struct nft_rbtree *priv = nft_set_priv(set); | ||||
| 	const struct nft_rbtree_elem *rbe; | ||||
| 	struct nft_rbtree_elem *rbe; | ||||
| 	struct nft_set_elem elem; | ||||
| 	struct rb_node *node; | ||||
| 	u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); | ||||
| 
 | ||||
| 	spin_lock_bh(&nft_rbtree_lock); | ||||
| 	for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { | ||||
| 		rbe = rb_entry(node, struct nft_rbtree_elem, node); | ||||
| 
 | ||||
| 		if (iter->count < iter->skip) | ||||
| 			goto cont; | ||||
| 		if (!nft_set_elem_active(&rbe->ext, genmask)) | ||||
| 			goto cont; | ||||
| 
 | ||||
| 		rbe = rb_entry(node, struct nft_rbtree_elem, node); | ||||
| 		nft_data_copy(&elem.key, &rbe->key); | ||||
| 		if (set->flags & NFT_SET_MAP && | ||||
| 		    !(rbe->flags & NFT_SET_ELEM_INTERVAL_END)) | ||||
| 			nft_data_copy(&elem.data, rbe->data); | ||||
| 		elem.flags = rbe->flags; | ||||
| 		elem.priv = rbe; | ||||
| 
 | ||||
| 		iter->err = iter->fn(ctx, set, iter, &elem); | ||||
| 		if (iter->err < 0) { | ||||
| @ -235,7 +227,7 @@ static void nft_rbtree_destroy(const struct nft_set *set) | ||||
| 	while ((node = priv->root.rb_node) != NULL) { | ||||
| 		rb_erase(node, &priv->root); | ||||
| 		rbe = rb_entry(node, struct nft_rbtree_elem, node); | ||||
| 		nft_rbtree_elem_destroy(set, rbe); | ||||
| 		nft_set_elem_destroy(set, rbe); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -245,9 +237,6 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, | ||||
| 	unsigned int nsize; | ||||
| 
 | ||||
| 	nsize = sizeof(struct nft_rbtree_elem); | ||||
| 	if (features & NFT_SET_MAP) | ||||
| 		nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]); | ||||
| 
 | ||||
| 	if (desc->size) | ||||
| 		est->size = sizeof(struct nft_rbtree) + desc->size * nsize; | ||||
| 	else | ||||
| @ -260,12 +249,14 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, | ||||
| 
 | ||||
| static struct nft_set_ops nft_rbtree_ops __read_mostly = { | ||||
| 	.privsize	= nft_rbtree_privsize, | ||||
| 	.elemsize	= offsetof(struct nft_rbtree_elem, ext), | ||||
| 	.estimate	= nft_rbtree_estimate, | ||||
| 	.init		= nft_rbtree_init, | ||||
| 	.destroy	= nft_rbtree_destroy, | ||||
| 	.insert		= nft_rbtree_insert, | ||||
| 	.remove		= nft_rbtree_remove, | ||||
| 	.get		= nft_rbtree_get, | ||||
| 	.deactivate	= nft_rbtree_deactivate, | ||||
| 	.activate	= nft_rbtree_activate, | ||||
| 	.lookup		= nft_rbtree_lookup, | ||||
| 	.walk		= nft_rbtree_walk, | ||||
| 	.features	= NFT_SET_INTERVAL | NFT_SET_MAP, | ||||
|  | ||||
| @ -3127,7 +3127,7 @@ static struct pernet_operations __net_initdata netlink_net_ops = { | ||||
| 	.exit = netlink_net_exit, | ||||
| }; | ||||
| 
 | ||||
| static inline u32 netlink_hash(const void *data, u32 seed) | ||||
| static inline u32 netlink_hash(const void *data, u32 len, u32 seed) | ||||
| { | ||||
| 	const struct netlink_sock *nlk = data; | ||||
| 	struct netlink_compare_arg arg; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user