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
16 changed files with 516 additions and 241 deletions
|
@ -88,7 +88,7 @@ struct rhashtable_compare_arg {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
|
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,
|
typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg,
|
||||||
const void *obj);
|
const void *obj);
|
||||||
|
|
||||||
|
@ -242,7 +242,9 @@ static inline unsigned int rht_head_hashfn(
|
||||||
const char *ptr = rht_obj(ht, he);
|
const char *ptr = rht_obj(ht, he);
|
||||||
|
|
||||||
return likely(params.obj_hashfn) ?
|
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);
|
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
|
* struct nft_set_elem - generic representation of set elements
|
||||||
*
|
*
|
||||||
* @cookie: implementation specific element cookie
|
|
||||||
* @key: element key
|
* @key: element key
|
||||||
* @data: element data (maps only)
|
* @priv: element private data and extensions
|
||||||
* @flags: element flags (end of interval)
|
|
||||||
*
|
|
||||||
* The cookie can be used to store a handle to the element for subsequent
|
|
||||||
* removal.
|
|
||||||
*/
|
*/
|
||||||
struct nft_set_elem {
|
struct nft_set_elem {
|
||||||
void *cookie;
|
|
||||||
struct nft_data key;
|
struct nft_data key;
|
||||||
struct nft_data data;
|
void *priv;
|
||||||
u32 flags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nft_set;
|
struct nft_set;
|
||||||
|
@ -202,11 +195,15 @@ struct nft_set_estimate {
|
||||||
enum nft_set_class class;
|
enum nft_set_class class;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nft_set_ext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nft_set_ops - nf_tables set operations
|
* struct nft_set_ops - nf_tables set operations
|
||||||
*
|
*
|
||||||
* @lookup: look up an element within the set
|
* @lookup: look up an element within the set
|
||||||
* @insert: insert new element into 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
|
* @remove: remove element from set
|
||||||
* @walk: iterate over all set elemeennts
|
* @walk: iterate over all set elemeennts
|
||||||
* @privsize: function to return size of set private data
|
* @privsize: function to return size of set private data
|
||||||
|
@ -214,16 +211,19 @@ struct nft_set_estimate {
|
||||||
* @destroy: destroy private data of set instance
|
* @destroy: destroy private data of set instance
|
||||||
* @list: nf_tables_set_ops list node
|
* @list: nf_tables_set_ops list node
|
||||||
* @owner: module reference
|
* @owner: module reference
|
||||||
|
* @elemsize: element private size
|
||||||
* @features: features supported by the implementation
|
* @features: features supported by the implementation
|
||||||
*/
|
*/
|
||||||
struct nft_set_ops {
|
struct nft_set_ops {
|
||||||
bool (*lookup)(const struct nft_set *set,
|
bool (*lookup)(const struct nft_set *set,
|
||||||
const struct nft_data *key,
|
const struct nft_data *key,
|
||||||
struct nft_data *data);
|
const struct nft_set_ext **ext);
|
||||||
int (*get)(const struct nft_set *set,
|
|
||||||
struct nft_set_elem *elem);
|
|
||||||
int (*insert)(const struct nft_set *set,
|
int (*insert)(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem);
|
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,
|
void (*remove)(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem);
|
const struct nft_set_elem *elem);
|
||||||
void (*walk)(const struct nft_ctx *ctx,
|
void (*walk)(const struct nft_ctx *ctx,
|
||||||
|
@ -241,6 +241,7 @@ struct nft_set_ops {
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
unsigned int elemsize;
|
||||||
u32 features;
|
u32 features;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -259,6 +260,7 @@ void nft_unregister_set(struct nft_set_ops *ops);
|
||||||
* @nelems: number of elements
|
* @nelems: number of elements
|
||||||
* @policy: set parameterization (see enum nft_set_policies)
|
* @policy: set parameterization (see enum nft_set_policies)
|
||||||
* @ops: set ops
|
* @ops: set ops
|
||||||
|
* @pnet: network namespace
|
||||||
* @flags: set flags
|
* @flags: set flags
|
||||||
* @klen: key length
|
* @klen: key length
|
||||||
* @dlen: data length
|
* @dlen: data length
|
||||||
|
@ -275,6 +277,7 @@ struct nft_set {
|
||||||
u16 policy;
|
u16 policy;
|
||||||
/* runtime data below here */
|
/* runtime data below here */
|
||||||
const struct nft_set_ops *ops ____cacheline_aligned;
|
const struct nft_set_ops *ops ____cacheline_aligned;
|
||||||
|
possible_net_t pnet;
|
||||||
u16 flags;
|
u16 flags;
|
||||||
u8 klen;
|
u8 klen;
|
||||||
u8 dlen;
|
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,
|
void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||||
struct nft_set_binding *binding);
|
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
|
* struct nft_expr_type - nf_tables expression type
|
||||||
|
@ -449,7 +567,6 @@ enum nft_chain_flags {
|
||||||
*
|
*
|
||||||
* @rules: list of rules in the chain
|
* @rules: list of rules in the chain
|
||||||
* @list: used internally
|
* @list: used internally
|
||||||
* @net: net namespace that this chain belongs to
|
|
||||||
* @table: table that this chain belongs to
|
* @table: table that this chain belongs to
|
||||||
* @handle: chain handle
|
* @handle: chain handle
|
||||||
* @use: number of jump references to this chain
|
* @use: number of jump references to this chain
|
||||||
|
@ -460,7 +577,6 @@ enum nft_chain_flags {
|
||||||
struct nft_chain {
|
struct nft_chain {
|
||||||
struct list_head rules;
|
struct list_head rules;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct net *net;
|
|
||||||
struct nft_table *table;
|
struct nft_table *table;
|
||||||
u64 handle;
|
u64 handle;
|
||||||
u32 use;
|
u32 use;
|
||||||
|
@ -512,6 +628,7 @@ struct nft_stats {
|
||||||
* struct nft_base_chain - nf_tables base chain
|
* struct nft_base_chain - nf_tables base chain
|
||||||
*
|
*
|
||||||
* @ops: netfilter hook ops
|
* @ops: netfilter hook ops
|
||||||
|
* @pnet: net namespace that this chain belongs to
|
||||||
* @type: chain type
|
* @type: chain type
|
||||||
* @policy: default policy
|
* @policy: default policy
|
||||||
* @stats: per-cpu chain stats
|
* @stats: per-cpu chain stats
|
||||||
|
@ -519,6 +636,7 @@ struct nft_stats {
|
||||||
*/
|
*/
|
||||||
struct nft_base_chain {
|
struct nft_base_chain {
|
||||||
struct nf_hook_ops ops[NFT_HOOK_OPS_MAX];
|
struct nf_hook_ops ops[NFT_HOOK_OPS_MAX];
|
||||||
|
possible_net_t pnet;
|
||||||
const struct nf_chain_type *type;
|
const struct nf_chain_type *type;
|
||||||
u8 policy;
|
u8 policy;
|
||||||
struct nft_stats __percpu *stats;
|
struct nft_stats __percpu *stats;
|
||||||
|
@ -605,6 +723,50 @@ void nft_unregister_expr(struct nft_expr_type *);
|
||||||
#define MODULE_ALIAS_NFT_SET() \
|
#define MODULE_ALIAS_NFT_SET() \
|
||||||
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
|
* 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;
|
* 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;
|
* struct test_obj *obj = data;
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,8 +10,10 @@
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
@ -27,7 +29,7 @@ static struct nf_loginfo default_loginfo = {
|
||||||
.type = NF_LOG_TYPE_LOG,
|
.type = NF_LOG_TYPE_LOG,
|
||||||
.u = {
|
.u = {
|
||||||
.log = {
|
.log = {
|
||||||
.level = 5,
|
.level = LOGLEVEL_NOTICE,
|
||||||
.logflags = NF_LOG_MASK,
|
.logflags = NF_LOG_MASK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
@ -26,7 +28,7 @@ static struct nf_loginfo default_loginfo = {
|
||||||
.type = NF_LOG_TYPE_LOG,
|
.type = NF_LOG_TYPE_LOG,
|
||||||
.u = {
|
.u = {
|
||||||
.log = {
|
.log = {
|
||||||
.level = 5,
|
.level = LOGLEVEL_NOTICE,
|
||||||
.logflags = NF_LOG_MASK,
|
.logflags = NF_LOG_MASK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,10 @@
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/in.h>
|
#include <linux/in.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
@ -234,7 +237,7 @@ static struct nf_loginfo trace_loginfo = {
|
||||||
.type = NF_LOG_TYPE_LOG,
|
.type = NF_LOG_TYPE_LOG,
|
||||||
.u = {
|
.u = {
|
||||||
.log = {
|
.log = {
|
||||||
.level = 4,
|
.level = LOGLEVEL_WARNING,
|
||||||
.logflags = NF_LOG_MASK,
|
.logflags = NF_LOG_MASK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
@ -27,7 +29,7 @@ static struct nf_loginfo default_loginfo = {
|
||||||
.type = NF_LOG_TYPE_LOG,
|
.type = NF_LOG_TYPE_LOG,
|
||||||
.u = {
|
.u = {
|
||||||
.log = {
|
.log = {
|
||||||
.level = 5,
|
.level = LOGLEVEL_NOTICE,
|
||||||
.logflags = NF_LOG_MASK,
|
.logflags = NF_LOG_MASK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -522,7 +522,6 @@ config NFT_NAT
|
||||||
typical Network Address Translation (NAT) packet transformations.
|
typical Network Address Translation (NAT) packet transformations.
|
||||||
|
|
||||||
config NFT_QUEUE
|
config NFT_QUEUE
|
||||||
depends on NETFILTER_XTABLES
|
|
||||||
depends on NETFILTER_NETLINK_QUEUE
|
depends on NETFILTER_NETLINK_QUEUE
|
||||||
tristate "Netfilter nf_tables queue module"
|
tristate "Netfilter nf_tables queue module"
|
||||||
help
|
help
|
||||||
|
|
|
@ -198,36 +198,31 @@ static int nft_delchain(struct nft_ctx *ctx)
|
||||||
static inline bool
|
static inline bool
|
||||||
nft_rule_is_active(struct net *net, const struct nft_rule *rule)
|
nft_rule_is_active(struct net *net, const struct nft_rule *rule)
|
||||||
{
|
{
|
||||||
return (rule->genmask & (1 << net->nft.gencursor)) == 0;
|
return (rule->genmask & nft_genmask_cur(net)) == 0;
|
||||||
}
|
|
||||||
|
|
||||||
static inline int gencursor_next(struct net *net)
|
|
||||||
{
|
|
||||||
return net->nft.gencursor+1 == 1 ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
|
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
|
static inline void
|
||||||
nft_rule_activate_next(struct net *net, struct nft_rule *rule)
|
nft_rule_activate_next(struct net *net, struct nft_rule *rule)
|
||||||
{
|
{
|
||||||
/* Now inactive, will be active in the future */
|
/* Now inactive, will be active in the future */
|
||||||
rule->genmask = (1 << net->nft.gencursor);
|
rule->genmask = nft_genmask_cur(net);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
nft_rule_deactivate_next(struct net *net, struct nft_rule *rule)
|
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)
|
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
|
static int
|
||||||
|
@ -1354,6 +1349,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
||||||
rcu_assign_pointer(basechain->stats, stats);
|
rcu_assign_pointer(basechain->stats, stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_pnet(&basechain->pnet, net);
|
||||||
basechain->type = type;
|
basechain->type = type;
|
||||||
chain = &basechain->chain;
|
chain = &basechain->chain;
|
||||||
|
|
||||||
|
@ -1381,7 +1377,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
||||||
|
|
||||||
INIT_LIST_HEAD(&chain->rules);
|
INIT_LIST_HEAD(&chain->rules);
|
||||||
chain->handle = nf_tables_alloc_handle(table);
|
chain->handle = nf_tables_alloc_handle(table);
|
||||||
chain->net = net;
|
|
||||||
chain->table = table;
|
chain->table = table;
|
||||||
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
|
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;
|
goto err2;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&set->bindings);
|
INIT_LIST_HEAD(&set->bindings);
|
||||||
|
write_pnet(&set->pnet, net);
|
||||||
set->ops = ops;
|
set->ops = ops;
|
||||||
set->ktype = ktype;
|
set->ktype = ktype;
|
||||||
set->klen = desc.klen;
|
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_iter *iter,
|
||||||
const struct nft_set_elem *elem)
|
const struct nft_set_elem *elem)
|
||||||
{
|
{
|
||||||
|
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
|
||||||
enum nft_registers dreg;
|
enum nft_registers dreg;
|
||||||
|
|
||||||
dreg = nft_type_to_reg(set->dtype);
|
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 ?
|
set->dtype == NFT_DATA_VERDICT ?
|
||||||
NFT_DATA_VERDICT : NFT_DATA_VALUE);
|
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);
|
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
|
* 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 *set,
|
||||||
const struct nft_set_elem *elem)
|
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);
|
unsigned char *b = skb_tail_pointer(skb);
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
|
|
||||||
|
@ -2880,19 +2894,19 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
|
||||||
if (nest == NULL)
|
if (nest == NULL)
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
|
if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
|
||||||
set->klen) < 0)
|
NFT_DATA_VALUE, set->klen) < 0)
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
if (set->flags & NFT_SET_MAP &&
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
|
||||||
!(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
|
nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext),
|
||||||
nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
|
|
||||||
set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
|
set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
|
||||||
set->dlen) < 0)
|
set->dlen) < 0)
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
if (elem->flags != 0)
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
|
||||||
if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
|
nla_put_be32(skb, NFTA_SET_ELEM_FLAGS,
|
||||||
|
htonl(*nft_set_ext_flags(ext))))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
nla_nest_end(skb, nest);
|
nla_nest_end(skb, nest);
|
||||||
|
@ -3114,15 +3128,54 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
|
||||||
return trans;
|
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,
|
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||||
const struct nlattr *attr)
|
const struct nlattr *attr)
|
||||||
{
|
{
|
||||||
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
|
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
|
||||||
struct nft_data_desc d1, d2;
|
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_elem elem;
|
||||||
struct nft_set_binding *binding;
|
struct nft_set_binding *binding;
|
||||||
|
struct nft_data data;
|
||||||
enum nft_registers dreg;
|
enum nft_registers dreg;
|
||||||
struct nft_trans *trans;
|
struct nft_trans *trans;
|
||||||
|
u32 flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (set->size && set->nelems == set->size)
|
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)
|
if (nla[NFTA_SET_ELEM_KEY] == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
elem.flags = 0;
|
nft_set_ext_prepare(&tmpl);
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
|
if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
|
||||||
elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
|
flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
|
||||||
if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
|
if (flags & ~NFT_SET_ELEM_INTERVAL_END)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!(set->flags & NFT_SET_INTERVAL) &&
|
if (!(set->flags & NFT_SET_INTERVAL) &&
|
||||||
elem.flags & NFT_SET_ELEM_INTERVAL_END)
|
flags & NFT_SET_ELEM_INTERVAL_END)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
if (flags != 0)
|
||||||
|
nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set->flags & NFT_SET_MAP) {
|
if (set->flags & NFT_SET_MAP) {
|
||||||
if (nla[NFTA_SET_ELEM_DATA] == NULL &&
|
if (nla[NFTA_SET_ELEM_DATA] == NULL &&
|
||||||
!(elem.flags & NFT_SET_ELEM_INTERVAL_END))
|
!(flags & NFT_SET_ELEM_INTERVAL_END))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (nla[NFTA_SET_ELEM_DATA] != NULL &&
|
if (nla[NFTA_SET_ELEM_DATA] != NULL &&
|
||||||
elem.flags & NFT_SET_ELEM_INTERVAL_END)
|
flags & NFT_SET_ELEM_INTERVAL_END)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else {
|
} else {
|
||||||
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
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)
|
if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
err = -EEXIST;
|
nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);
|
||||||
if (set->ops->get(set, &elem) == 0)
|
|
||||||
goto err2;
|
|
||||||
|
|
||||||
if (nla[NFTA_SET_ELEM_DATA] != NULL) {
|
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)
|
if (err < 0)
|
||||||
goto err2;
|
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,
|
err = nft_validate_data_load(&bind_ctx, dreg,
|
||||||
&elem.data, d2.type);
|
&data, d2.type);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err3;
|
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);
|
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
|
||||||
if (trans == NULL)
|
if (trans == NULL)
|
||||||
goto err3;
|
goto err4;
|
||||||
|
|
||||||
|
ext->genmask = nft_genmask_cur(ctx->net);
|
||||||
err = set->ops->insert(set, &elem);
|
err = set->ops->insert(set, &elem);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err4;
|
goto err5;
|
||||||
|
|
||||||
nft_trans_elem(trans) = elem;
|
nft_trans_elem(trans) = elem;
|
||||||
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err4:
|
err5:
|
||||||
kfree(trans);
|
kfree(trans);
|
||||||
|
err4:
|
||||||
|
kfree(elem.priv);
|
||||||
err3:
|
err3:
|
||||||
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
||||||
nft_data_uninit(&elem.data, d2.type);
|
nft_data_uninit(&data, d2.type);
|
||||||
err2:
|
err2:
|
||||||
nft_data_uninit(&elem.key, d1.type);
|
nft_data_uninit(&elem.key, d1.type);
|
||||||
err1:
|
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)
|
if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
err = set->ops->get(set, &elem);
|
|
||||||
if (err < 0)
|
|
||||||
goto err2;
|
|
||||||
|
|
||||||
trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
|
trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
|
||||||
if (trans == NULL) {
|
if (trans == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elem.priv = set->ops->deactivate(set, &elem);
|
||||||
|
if (elem.priv == NULL) {
|
||||||
|
err = -ENOENT;
|
||||||
|
goto err3;
|
||||||
|
}
|
||||||
|
|
||||||
nft_trans_elem(trans) = elem;
|
nft_trans_elem(trans) = elem;
|
||||||
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err3:
|
||||||
|
kfree(trans);
|
||||||
err2:
|
err2:
|
||||||
nft_data_uninit(&elem.key, desc.type);
|
nft_data_uninit(&elem.key, desc.type);
|
||||||
err1:
|
err1:
|
||||||
|
@ -3532,6 +3606,10 @@ static void nf_tables_commit_release(struct nft_trans *trans)
|
||||||
case NFT_MSG_DELSET:
|
case NFT_MSG_DELSET:
|
||||||
nft_set_destroy(nft_trans_set(trans));
|
nft_set_destroy(nft_trans_set(trans));
|
||||||
break;
|
break;
|
||||||
|
case NFT_MSG_DELSETELEM:
|
||||||
|
nft_set_elem_destroy(nft_trans_elem_set(trans),
|
||||||
|
nft_trans_elem(trans).priv);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
kfree(trans);
|
kfree(trans);
|
||||||
}
|
}
|
||||||
|
@ -3546,7 +3624,7 @@ static int nf_tables_commit(struct sk_buff *skb)
|
||||||
while (++net->nft.base_seq == 0);
|
while (++net->nft.base_seq == 0);
|
||||||
|
|
||||||
/* A new generation has just started */
|
/* 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
|
/* Make sure all packets have left the previous generation before
|
||||||
* purging old rules.
|
* purging old rules.
|
||||||
|
@ -3617,24 +3695,21 @@ static int nf_tables_commit(struct sk_buff *skb)
|
||||||
NFT_MSG_DELSET, GFP_KERNEL);
|
NFT_MSG_DELSET, GFP_KERNEL);
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_NEWSETELEM:
|
case NFT_MSG_NEWSETELEM:
|
||||||
nf_tables_setelem_notify(&trans->ctx,
|
te = (struct nft_trans_elem *)trans->data;
|
||||||
nft_trans_elem_set(trans),
|
|
||||||
&nft_trans_elem(trans),
|
te->set->ops->activate(te->set, &te->elem);
|
||||||
|
nf_tables_setelem_notify(&trans->ctx, te->set,
|
||||||
|
&te->elem,
|
||||||
NFT_MSG_NEWSETELEM, 0);
|
NFT_MSG_NEWSETELEM, 0);
|
||||||
nft_trans_destroy(trans);
|
nft_trans_destroy(trans);
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_DELSETELEM:
|
case NFT_MSG_DELSETELEM:
|
||||||
te = (struct nft_trans_elem *)trans->data;
|
te = (struct nft_trans_elem *)trans->data;
|
||||||
|
|
||||||
nf_tables_setelem_notify(&trans->ctx, te->set,
|
nf_tables_setelem_notify(&trans->ctx, te->set,
|
||||||
&te->elem,
|
&te->elem,
|
||||||
NFT_MSG_DELSETELEM, 0);
|
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);
|
te->set->ops->remove(te->set, &te->elem);
|
||||||
nft_trans_destroy(trans);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3666,6 +3741,10 @@ static void nf_tables_abort_release(struct nft_trans *trans)
|
||||||
case NFT_MSG_NEWSET:
|
case NFT_MSG_NEWSET:
|
||||||
nft_set_destroy(nft_trans_set(trans));
|
nft_set_destroy(nft_trans_set(trans));
|
||||||
break;
|
break;
|
||||||
|
case NFT_MSG_NEWSETELEM:
|
||||||
|
nft_set_elem_destroy(nft_trans_elem_set(trans),
|
||||||
|
nft_trans_elem(trans).priv);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
kfree(trans);
|
kfree(trans);
|
||||||
}
|
}
|
||||||
|
@ -3736,16 +3815,15 @@ static int nf_tables_abort(struct sk_buff *skb)
|
||||||
case NFT_MSG_NEWSETELEM:
|
case NFT_MSG_NEWSETELEM:
|
||||||
nft_trans_elem_set(trans)->nelems--;
|
nft_trans_elem_set(trans)->nelems--;
|
||||||
te = (struct nft_trans_elem *)trans->data;
|
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);
|
te->set->ops->remove(te->set, &te->elem);
|
||||||
nft_trans_destroy(trans);
|
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_DELSETELEM:
|
case NFT_MSG_DELSETELEM:
|
||||||
|
te = (struct nft_trans_elem *)trans->data;
|
||||||
|
|
||||||
nft_trans_elem_set(trans)->nelems++;
|
nft_trans_elem_set(trans)->nelems++;
|
||||||
|
te->set->ops->activate(te->set, &te->elem);
|
||||||
|
|
||||||
nft_trans_destroy(trans);
|
nft_trans_destroy(trans);
|
||||||
break;
|
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_iter *iter,
|
||||||
const struct nft_set_elem *elem)
|
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;
|
return 0;
|
||||||
|
|
||||||
switch (elem->data.verdict) {
|
data = nft_set_ext_data(ext);
|
||||||
|
switch (data->verdict) {
|
||||||
case NFT_JUMP:
|
case NFT_JUMP:
|
||||||
case NFT_GOTO:
|
case NFT_GOTO:
|
||||||
return nf_tables_check_loops(ctx, elem->data.chain);
|
return nf_tables_check_loops(ctx, data->chain);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* Development of this code funded by Astaro AG (http://www.astaro.com/)
|
* Development of this code funded by Astaro AG (http://www.astaro.com/)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
@ -37,7 +38,7 @@ static struct nf_loginfo trace_loginfo = {
|
||||||
.type = NF_LOG_TYPE_LOG,
|
.type = NF_LOG_TYPE_LOG,
|
||||||
.u = {
|
.u = {
|
||||||
.log = {
|
.log = {
|
||||||
.level = 4,
|
.level = LOGLEVEL_WARNING,
|
||||||
.logflags = NF_LOG_MASK,
|
.logflags = NF_LOG_MASK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -49,7 +50,7 @@ static void __nft_trace_packet(const struct nft_pktinfo *pkt,
|
||||||
{
|
{
|
||||||
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
|
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,
|
nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
|
||||||
pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
|
pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
|
||||||
chain->table->name, chain->name, comments[type],
|
chain->table->name, chain->name, comments[type],
|
||||||
rulenum);
|
rulenum);
|
||||||
|
@ -112,6 +113,7 @@ unsigned int
|
||||||
nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
|
nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
|
||||||
{
|
{
|
||||||
const struct nft_chain *chain = ops->priv, *basechain = chain;
|
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_rule *rule;
|
||||||
const struct nft_expr *expr, *last;
|
const struct nft_expr *expr, *last;
|
||||||
struct nft_data data[NFT_REG_MAX + 1];
|
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_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
|
||||||
struct nft_stats *stats;
|
struct nft_stats *stats;
|
||||||
int rulenum;
|
int rulenum;
|
||||||
/*
|
unsigned int gencursor = nft_genmask_cur(net);
|
||||||
* 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);
|
|
||||||
|
|
||||||
do_chain:
|
do_chain:
|
||||||
rulenum = 0;
|
rulenum = 0;
|
||||||
|
|
|
@ -23,24 +23,65 @@
|
||||||
/* We target a hash table size of 4, element hint is 75% of final size */
|
/* We target a hash table size of 4, element hint is 75% of final size */
|
||||||
#define NFT_HASH_ELEMENT_HINT 3
|
#define NFT_HASH_ELEMENT_HINT 3
|
||||||
|
|
||||||
|
struct nft_hash {
|
||||||
|
struct rhashtable ht;
|
||||||
|
};
|
||||||
|
|
||||||
struct nft_hash_elem {
|
struct nft_hash_elem {
|
||||||
struct rhash_head node;
|
struct rhash_head node;
|
||||||
struct nft_data key;
|
struct nft_set_ext ext;
|
||||||
struct nft_data data[];
|
};
|
||||||
|
|
||||||
|
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 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,
|
static bool nft_hash_lookup(const struct nft_set *set,
|
||||||
const struct nft_data *key,
|
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;
|
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);
|
he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
|
||||||
if (he && set->flags & NFT_SET_MAP)
|
if (he != NULL)
|
||||||
nft_data_copy(data, he->data);
|
*ext = &he->ext;
|
||||||
|
|
||||||
return !!he;
|
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,
|
static int nft_hash_insert(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem)
|
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;
|
struct nft_hash_elem *he = elem->priv;
|
||||||
unsigned int size;
|
struct nft_hash_cmp_arg arg = {
|
||||||
int err;
|
.genmask = nft_genmask_next(read_pnet(&set->pnet)),
|
||||||
|
.set = set,
|
||||||
|
.key = &elem->key,
|
||||||
|
};
|
||||||
|
|
||||||
if (elem->flags != 0)
|
return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
|
||||||
return -EINVAL;
|
nft_hash_params);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nft_hash_elem_destroy(const struct nft_set *set,
|
static void nft_hash_activate(const struct nft_set *set,
|
||||||
struct nft_hash_elem *he)
|
const struct nft_set_elem *elem)
|
||||||
{
|
{
|
||||||
nft_data_uninit(&he->key, NFT_DATA_VALUE);
|
struct nft_hash_elem *he = elem->priv;
|
||||||
if (set->flags & NFT_SET_MAP)
|
|
||||||
nft_data_uninit(he->data, set->dtype);
|
nft_set_elem_change_active(set, &he->ext);
|
||||||
kfree(he);
|
}
|
||||||
|
|
||||||
|
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,
|
static void nft_hash_remove(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem)
|
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);
|
rhashtable_remove_fast(&priv->ht, &he->node, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
|
static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
|
||||||
struct nft_set_iter *iter)
|
struct nft_set_iter *iter)
|
||||||
{
|
{
|
||||||
struct rhashtable *priv = nft_set_priv(set);
|
struct nft_hash *priv = nft_set_priv(set);
|
||||||
const struct nft_hash_elem *he;
|
struct nft_hash_elem *he;
|
||||||
struct rhashtable_iter hti;
|
struct rhashtable_iter hti;
|
||||||
struct nft_set_elem elem;
|
struct nft_set_elem elem;
|
||||||
|
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = rhashtable_walk_init(priv, &hti);
|
err = rhashtable_walk_init(&priv->ht, &hti);
|
||||||
iter->err = err;
|
iter->err = err;
|
||||||
if (err)
|
if (err)
|
||||||
return;
|
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)
|
if (iter->count < iter->skip)
|
||||||
goto cont;
|
goto cont;
|
||||||
|
if (!nft_set_elem_active(&he->ext, genmask))
|
||||||
|
goto cont;
|
||||||
|
|
||||||
memcpy(&elem.key, &he->key, sizeof(elem.key));
|
elem.priv = he;
|
||||||
if (set->flags & NFT_SET_MAP)
|
|
||||||
memcpy(&elem.data, he->data, sizeof(elem.data));
|
|
||||||
elem.flags = 0;
|
|
||||||
|
|
||||||
iter->err = iter->fn(ctx, set, iter, &elem);
|
iter->err = iter->fn(ctx, set, iter, &elem);
|
||||||
if (iter->err < 0)
|
if (iter->err < 0)
|
||||||
|
@ -165,13 +190,14 @@ out:
|
||||||
|
|
||||||
static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
|
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 = {
|
static const struct rhashtable_params nft_hash_params = {
|
||||||
.head_offset = offsetof(struct nft_hash_elem, node),
|
.head_offset = offsetof(struct nft_hash_elem, node),
|
||||||
.key_offset = offsetof(struct nft_hash_elem, key),
|
.hashfn = nft_hash_key,
|
||||||
.hashfn = jhash,
|
.obj_hashfn = nft_hash_obj,
|
||||||
|
.obj_cmpfn = nft_hash_cmp,
|
||||||
.automatic_shrinking = true,
|
.automatic_shrinking = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,23 +205,25 @@ static int nft_hash_init(const struct nft_set *set,
|
||||||
const struct nft_set_desc *desc,
|
const struct nft_set_desc *desc,
|
||||||
const struct nlattr * const tb[])
|
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;
|
struct rhashtable_params params = nft_hash_params;
|
||||||
|
|
||||||
params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT;
|
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)
|
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);
|
(void *)set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,11 +233,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
|
||||||
unsigned int esize;
|
unsigned int esize;
|
||||||
|
|
||||||
esize = sizeof(struct nft_hash_elem);
|
esize = sizeof(struct nft_hash_elem);
|
||||||
if (features & NFT_SET_MAP)
|
|
||||||
esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]);
|
|
||||||
|
|
||||||
if (desc->size) {
|
if (desc->size) {
|
||||||
est->size = sizeof(struct rhashtable) +
|
est->size = sizeof(struct nft_hash) +
|
||||||
roundup_pow_of_two(desc->size * 4 / 3) *
|
roundup_pow_of_two(desc->size * 4 / 3) *
|
||||||
sizeof(struct nft_hash_elem *) +
|
sizeof(struct nft_hash_elem *) +
|
||||||
desc->size * esize;
|
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 = {
|
static struct nft_set_ops nft_hash_ops __read_mostly = {
|
||||||
.privsize = nft_hash_privsize,
|
.privsize = nft_hash_privsize,
|
||||||
|
.elemsize = offsetof(struct nft_hash_elem, ext),
|
||||||
.estimate = nft_hash_estimate,
|
.estimate = nft_hash_estimate,
|
||||||
.init = nft_hash_init,
|
.init = nft_hash_init,
|
||||||
.destroy = nft_hash_destroy,
|
.destroy = nft_hash_destroy,
|
||||||
.get = nft_hash_get,
|
|
||||||
.insert = nft_hash_insert,
|
.insert = nft_hash_insert,
|
||||||
|
.activate = nft_hash_activate,
|
||||||
|
.deactivate = nft_hash_deactivate,
|
||||||
.remove = nft_hash_remove,
|
.remove = nft_hash_remove,
|
||||||
.lookup = nft_hash_lookup,
|
.lookup = nft_hash_lookup,
|
||||||
.walk = nft_hash_walk,
|
.walk = nft_hash_walk,
|
||||||
|
|
|
@ -78,7 +78,7 @@ static int nft_log_init(const struct nft_ctx *ctx,
|
||||||
li->u.log.level =
|
li->u.log.level =
|
||||||
ntohl(nla_get_be32(tb[NFTA_LOG_LEVEL]));
|
ntohl(nla_get_be32(tb[NFTA_LOG_LEVEL]));
|
||||||
} else {
|
} else {
|
||||||
li->u.log.level = 4;
|
li->u.log.level = LOGLEVEL_WARNING;
|
||||||
}
|
}
|
||||||
if (tb[NFTA_LOG_FLAGS] != NULL) {
|
if (tb[NFTA_LOG_FLAGS] != NULL) {
|
||||||
li->u.log.logflags =
|
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_lookup *priv = nft_expr_priv(expr);
|
||||||
const struct nft_set *set = priv->set;
|
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;
|
return;
|
||||||
|
}
|
||||||
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
|
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NFT_META_CPU:
|
case NFT_META_CPU:
|
||||||
dest->data[0] = smp_processor_id();
|
dest->data[0] = raw_smp_processor_id();
|
||||||
break;
|
break;
|
||||||
case NFT_META_IIFGROUP:
|
case NFT_META_IIFGROUP:
|
||||||
if (in == NULL)
|
if (in == NULL)
|
||||||
|
|
|
@ -26,18 +26,18 @@ struct nft_rbtree {
|
||||||
|
|
||||||
struct nft_rbtree_elem {
|
struct nft_rbtree_elem {
|
||||||
struct rb_node node;
|
struct rb_node node;
|
||||||
u16 flags;
|
struct nft_set_ext ext;
|
||||||
struct nft_data key;
|
|
||||||
struct nft_data data[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool nft_rbtree_lookup(const struct nft_set *set,
|
static bool nft_rbtree_lookup(const struct nft_set *set,
|
||||||
const struct nft_data *key,
|
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 *priv = nft_set_priv(set);
|
||||||
const struct nft_rbtree_elem *rbe, *interval = NULL;
|
const struct nft_rbtree_elem *rbe, *interval = NULL;
|
||||||
const struct rb_node *parent;
|
const struct rb_node *parent;
|
||||||
|
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
|
||||||
int d;
|
int d;
|
||||||
|
|
||||||
spin_lock_bh(&nft_rbtree_lock);
|
spin_lock_bh(&nft_rbtree_lock);
|
||||||
|
@ -45,7 +45,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
|
||||||
while (parent != NULL) {
|
while (parent != NULL) {
|
||||||
rbe = rb_entry(parent, struct nft_rbtree_elem, node);
|
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) {
|
if (d < 0) {
|
||||||
parent = parent->rb_left;
|
parent = parent->rb_left;
|
||||||
interval = rbe;
|
interval = rbe;
|
||||||
|
@ -53,12 +53,17 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
|
||||||
parent = parent->rb_right;
|
parent = parent->rb_right;
|
||||||
else {
|
else {
|
||||||
found:
|
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;
|
goto out;
|
||||||
if (set->flags & NFT_SET_MAP)
|
|
||||||
nft_data_copy(data, rbe->data);
|
|
||||||
|
|
||||||
spin_unlock_bh(&nft_rbtree_lock);
|
spin_unlock_bh(&nft_rbtree_lock);
|
||||||
|
|
||||||
|
*ext = &rbe->ext;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,23 +77,13 @@ out:
|
||||||
return false;
|
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,
|
static int __nft_rbtree_insert(const struct nft_set *set,
|
||||||
struct nft_rbtree_elem *new)
|
struct nft_rbtree_elem *new)
|
||||||
{
|
{
|
||||||
struct nft_rbtree *priv = nft_set_priv(set);
|
struct nft_rbtree *priv = nft_set_priv(set);
|
||||||
struct nft_rbtree_elem *rbe;
|
struct nft_rbtree_elem *rbe;
|
||||||
struct rb_node *parent, **p;
|
struct rb_node *parent, **p;
|
||||||
|
u8 genmask = nft_genmask_next(read_pnet(&set->pnet));
|
||||||
int d;
|
int d;
|
||||||
|
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
|
@ -96,13 +91,18 @@ static int __nft_rbtree_insert(const struct nft_set *set,
|
||||||
while (*p != NULL) {
|
while (*p != NULL) {
|
||||||
parent = *p;
|
parent = *p;
|
||||||
rbe = rb_entry(parent, struct nft_rbtree_elem, node);
|
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)
|
if (d < 0)
|
||||||
p = &parent->rb_left;
|
p = &parent->rb_left;
|
||||||
else if (d > 0)
|
else if (d > 0)
|
||||||
p = &parent->rb_right;
|
p = &parent->rb_right;
|
||||||
else
|
else {
|
||||||
|
if (nft_set_elem_active(&rbe->ext, genmask))
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
|
p = &parent->rb_left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rb_link_node(&new->node, parent, p);
|
rb_link_node(&new->node, parent, p);
|
||||||
rb_insert_color(&new->node, &priv->root);
|
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,
|
static int nft_rbtree_insert(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem)
|
const struct nft_set_elem *elem)
|
||||||
{
|
{
|
||||||
struct nft_rbtree_elem *rbe;
|
struct nft_rbtree_elem *rbe = elem->priv;
|
||||||
unsigned int size;
|
|
||||||
int err;
|
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);
|
spin_lock_bh(&nft_rbtree_lock);
|
||||||
err = __nft_rbtree_insert(set, rbe);
|
err = __nft_rbtree_insert(set, rbe);
|
||||||
if (err < 0)
|
|
||||||
kfree(rbe);
|
|
||||||
|
|
||||||
spin_unlock_bh(&nft_rbtree_lock);
|
spin_unlock_bh(&nft_rbtree_lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,39 +126,49 @@ static void nft_rbtree_remove(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem)
|
const struct nft_set_elem *elem)
|
||||||
{
|
{
|
||||||
struct nft_rbtree *priv = nft_set_priv(set);
|
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);
|
spin_lock_bh(&nft_rbtree_lock);
|
||||||
rb_erase(&rbe->node, &priv->root);
|
rb_erase(&rbe->node, &priv->root);
|
||||||
spin_unlock_bh(&nft_rbtree_lock);
|
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 nft_rbtree *priv = nft_set_priv(set);
|
||||||
const struct rb_node *parent = priv->root.rb_node;
|
const struct rb_node *parent = priv->root.rb_node;
|
||||||
struct nft_rbtree_elem *rbe;
|
struct nft_rbtree_elem *rbe;
|
||||||
|
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
|
||||||
int d;
|
int d;
|
||||||
|
|
||||||
while (parent != NULL) {
|
while (parent != NULL) {
|
||||||
rbe = rb_entry(parent, struct nft_rbtree_elem, node);
|
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)
|
if (d < 0)
|
||||||
parent = parent->rb_left;
|
parent = parent->rb_left;
|
||||||
else if (d > 0)
|
else if (d > 0)
|
||||||
parent = parent->rb_right;
|
parent = parent->rb_right;
|
||||||
else {
|
else {
|
||||||
elem->cookie = rbe;
|
if (!nft_set_elem_active(&rbe->ext, genmask)) {
|
||||||
if (set->flags & NFT_SET_MAP &&
|
parent = parent->rb_left;
|
||||||
!(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
|
continue;
|
||||||
nft_data_copy(&elem->data, rbe->data);
|
}
|
||||||
elem->flags = rbe->flags;
|
nft_set_elem_change_active(set, &rbe->ext);
|
||||||
return 0;
|
return rbe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -ENOENT;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nft_rbtree_walk(const struct nft_ctx *ctx,
|
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)
|
struct nft_set_iter *iter)
|
||||||
{
|
{
|
||||||
const struct nft_rbtree *priv = nft_set_priv(set);
|
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 nft_set_elem elem;
|
||||||
struct rb_node *node;
|
struct rb_node *node;
|
||||||
|
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
|
||||||
|
|
||||||
spin_lock_bh(&nft_rbtree_lock);
|
spin_lock_bh(&nft_rbtree_lock);
|
||||||
for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
|
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)
|
if (iter->count < iter->skip)
|
||||||
goto cont;
|
goto cont;
|
||||||
|
if (!nft_set_elem_active(&rbe->ext, genmask))
|
||||||
|
goto cont;
|
||||||
|
|
||||||
rbe = rb_entry(node, struct nft_rbtree_elem, node);
|
elem.priv = rbe;
|
||||||
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;
|
|
||||||
|
|
||||||
iter->err = iter->fn(ctx, set, iter, &elem);
|
iter->err = iter->fn(ctx, set, iter, &elem);
|
||||||
if (iter->err < 0) {
|
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) {
|
while ((node = priv->root.rb_node) != NULL) {
|
||||||
rb_erase(node, &priv->root);
|
rb_erase(node, &priv->root);
|
||||||
rbe = rb_entry(node, struct nft_rbtree_elem, node);
|
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;
|
unsigned int nsize;
|
||||||
|
|
||||||
nsize = sizeof(struct nft_rbtree_elem);
|
nsize = sizeof(struct nft_rbtree_elem);
|
||||||
if (features & NFT_SET_MAP)
|
|
||||||
nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]);
|
|
||||||
|
|
||||||
if (desc->size)
|
if (desc->size)
|
||||||
est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
|
est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
|
||||||
else
|
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 = {
|
static struct nft_set_ops nft_rbtree_ops __read_mostly = {
|
||||||
.privsize = nft_rbtree_privsize,
|
.privsize = nft_rbtree_privsize,
|
||||||
|
.elemsize = offsetof(struct nft_rbtree_elem, ext),
|
||||||
.estimate = nft_rbtree_estimate,
|
.estimate = nft_rbtree_estimate,
|
||||||
.init = nft_rbtree_init,
|
.init = nft_rbtree_init,
|
||||||
.destroy = nft_rbtree_destroy,
|
.destroy = nft_rbtree_destroy,
|
||||||
.insert = nft_rbtree_insert,
|
.insert = nft_rbtree_insert,
|
||||||
.remove = nft_rbtree_remove,
|
.remove = nft_rbtree_remove,
|
||||||
.get = nft_rbtree_get,
|
.deactivate = nft_rbtree_deactivate,
|
||||||
|
.activate = nft_rbtree_activate,
|
||||||
.lookup = nft_rbtree_lookup,
|
.lookup = nft_rbtree_lookup,
|
||||||
.walk = nft_rbtree_walk,
|
.walk = nft_rbtree_walk,
|
||||||
.features = NFT_SET_INTERVAL | NFT_SET_MAP,
|
.features = NFT_SET_INTERVAL | NFT_SET_MAP,
|
||||||
|
|
|
@ -3127,7 +3127,7 @@ static struct pernet_operations __net_initdata netlink_net_ops = {
|
||||||
.exit = netlink_net_exit,
|
.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;
|
const struct netlink_sock *nlk = data;
|
||||||
struct netlink_compare_arg arg;
|
struct netlink_compare_arg arg;
|
||||||
|
|
Loading…
Add table
Reference in a new issue