netfilter: nf_tables: use net_generic infra for transaction data

[ 0854db2aae ]

This moves all nf_tables pernet data from struct net to a net_generic
extension, with the exception of the gencursor.

The latter is used in the data path and also outside of the nf_tables
core. All others are only used from the configuration plane.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Pablo Neira Ayuso 2023-07-05 18:55:11 +02:00 committed by Greg Kroah-Hartman
parent f2a0072f51
commit 88bae77d66
5 changed files with 210 additions and 125 deletions

View file

@ -1409,4 +1409,14 @@ struct nft_trans_flowtable {
int __init nft_chain_filter_init(void); int __init nft_chain_filter_init(void);
void nft_chain_filter_fini(void); void nft_chain_filter_fini(void);
struct nftables_pernet {
struct list_head tables;
struct list_head commit_list;
struct list_head module_list;
struct list_head notify_list;
struct mutex commit_mutex;
unsigned int base_seq;
u8 validate_state;
};
#endif /* _NET_NF_TABLES_H */ #endif /* _NET_NF_TABLES_H */

View file

@ -5,12 +5,7 @@
#include <linux/list.h> #include <linux/list.h>
struct netns_nftables { struct netns_nftables {
struct list_head tables;
struct list_head commit_list;
struct mutex commit_mutex;
unsigned int base_seq;
u8 gencursor; u8 gencursor;
u8 validate_state;
}; };
#endif #endif

View file

@ -22,10 +22,13 @@
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/sock.h> #include <net/sock.h>
#define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-")) #define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-"))
unsigned int nf_tables_net_id __read_mostly;
static LIST_HEAD(nf_tables_expressions); static LIST_HEAD(nf_tables_expressions);
static LIST_HEAD(nf_tables_objects); static LIST_HEAD(nf_tables_objects);
static LIST_HEAD(nf_tables_flowtables); static LIST_HEAD(nf_tables_flowtables);
@ -53,7 +56,9 @@ static const struct rhashtable_params nft_chain_ht_params = {
static void nft_validate_state_update(struct net *net, u8 new_validate_state) static void nft_validate_state_update(struct net *net, u8 new_validate_state)
{ {
switch (net->nft.validate_state) { struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
switch (nft_net->validate_state) {
case NFT_VALIDATE_SKIP: case NFT_VALIDATE_SKIP:
WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO); WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO);
break; break;
@ -64,7 +69,7 @@ static void nft_validate_state_update(struct net *net, u8 new_validate_state)
return; return;
} }
net->nft.validate_state = new_validate_state; nft_net->validate_state = new_validate_state;
} }
static void nft_ctx_init(struct nft_ctx *ctx, static void nft_ctx_init(struct nft_ctx *ctx,
@ -117,13 +122,15 @@ static void nft_trans_destroy(struct nft_trans *trans)
static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
{ {
struct nftables_pernet *nft_net;
struct net *net = ctx->net; struct net *net = ctx->net;
struct nft_trans *trans; struct nft_trans *trans;
if (!nft_set_is_anonymous(set)) if (!nft_set_is_anonymous(set))
return; return;
list_for_each_entry_reverse(trans, &net->nft.commit_list, list) { nft_net = net_generic(net, nf_tables_net_id);
list_for_each_entry_reverse(trans, &nft_net->commit_list, list) {
switch (trans->msg_type) { switch (trans->msg_type) {
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
if (nft_trans_set(trans) == set) if (nft_trans_set(trans) == set)
@ -137,6 +144,14 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
} }
} }
static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans)
{
struct nftables_pernet *nft_net;
nft_net = net_generic(net, nf_tables_net_id);
list_add_tail(&trans->list, &nft_net->commit_list);
}
static int nf_tables_register_hook(struct net *net, static int nf_tables_register_hook(struct net *net,
const struct nft_table *table, const struct nft_table *table,
struct nft_chain *chain) struct nft_chain *chain)
@ -187,7 +202,7 @@ static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
if (msg_type == NFT_MSG_NEWTABLE) if (msg_type == NFT_MSG_NEWTABLE)
nft_activate_next(ctx->net, ctx->table); nft_activate_next(ctx->net, ctx->table);
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
} }
@ -214,7 +229,7 @@ static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
if (msg_type == NFT_MSG_NEWCHAIN) if (msg_type == NFT_MSG_NEWCHAIN)
nft_activate_next(ctx->net, ctx->chain); nft_activate_next(ctx->net, ctx->chain);
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
} }
@ -287,7 +302,7 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID])); ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID]));
} }
nft_trans_rule(trans) = rule; nft_trans_rule(trans) = rule;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return trans; return trans;
} }
@ -342,7 +357,7 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
nft_activate_next(ctx->net, set); nft_activate_next(ctx->net, set);
} }
nft_trans_set(trans) = set; nft_trans_set(trans) = set;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
} }
@ -374,7 +389,7 @@ static int nft_trans_obj_add(struct nft_ctx *ctx, int msg_type,
nft_activate_next(ctx->net, obj); nft_activate_next(ctx->net, obj);
nft_trans_obj(trans) = obj; nft_trans_obj(trans) = obj;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
} }
@ -407,7 +422,7 @@ static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
nft_activate_next(ctx->net, flowtable); nft_activate_next(ctx->net, flowtable);
nft_trans_flowtable(trans) = flowtable; nft_trans_flowtable(trans) = flowtable;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
} }
@ -435,12 +450,14 @@ static struct nft_table *nft_table_lookup(const struct net *net,
const struct nlattr *nla, const struct nlattr *nla,
u8 family, u8 genmask) u8 family, u8 genmask)
{ {
struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
if (nla == NULL) if (nla == NULL)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
list_for_each_entry_rcu(table, &net->nft.tables, list) { nft_net = net_generic(net, nf_tables_net_id);
list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (!nla_strcmp(nla, table->name) && if (!nla_strcmp(nla, table->name) &&
table->family == family && table->family == family &&
nft_active_genmask(table, genmask)) nft_active_genmask(table, genmask))
@ -454,9 +471,11 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
const struct nlattr *nla, const struct nlattr *nla,
u8 genmask) u8 genmask)
{ {
struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
list_for_each_entry(table, &net->nft.tables, list) { nft_net = net_generic(net, nf_tables_net_id);
list_for_each_entry(table, &nft_net->tables, list) {
if (be64_to_cpu(nla_get_be64(nla)) == table->handle && if (be64_to_cpu(nla_get_be64(nla)) == table->handle &&
nft_active_genmask(table, genmask)) nft_active_genmask(table, genmask))
return table; return table;
@ -509,11 +528,13 @@ __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
static void nft_request_module(struct net *net, const char *fmt, ...) static void nft_request_module(struct net *net, const char *fmt, ...)
{ {
char module_name[MODULE_NAME_LEN]; char module_name[MODULE_NAME_LEN];
struct nftables_pernet *nft_net;
LIST_HEAD(commit_list); LIST_HEAD(commit_list);
va_list args; va_list args;
int ret; int ret;
list_splice_init(&net->nft.commit_list, &commit_list); nft_net = net_generic(net, nf_tables_net_id);
list_splice_init(&nft_net->commit_list, &commit_list);
va_start(args, fmt); va_start(args, fmt);
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
@ -521,12 +542,12 @@ static void nft_request_module(struct net *net, const char *fmt, ...)
if (ret >= MODULE_NAME_LEN) if (ret >= MODULE_NAME_LEN)
return; return;
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
request_module("%s", module_name); request_module("%s", module_name);
mutex_lock(&net->nft.commit_mutex); mutex_lock(&nft_net->commit_mutex);
WARN_ON_ONCE(!list_empty(&net->nft.commit_list)); WARN_ON_ONCE(!list_empty(&nft_net->commit_list));
list_splice(&commit_list, &net->nft.commit_list); list_splice(&commit_list, &nft_net->commit_list);
} }
#endif #endif
@ -563,7 +584,9 @@ nf_tables_chain_type_lookup(struct net *net, const struct nlattr *nla,
static __be16 nft_base_seq(const struct net *net) static __be16 nft_base_seq(const struct net *net)
{ {
return htons(net->nft.base_seq & 0xffff); struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
return htons(nft_net->base_seq & 0xffff);
} }
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
@ -631,15 +654,17 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
struct netlink_callback *cb) struct netlink_callback *cb)
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct nftables_pernet *nft_net;
const struct nft_table *table; const struct nft_table *table;
unsigned int idx = 0, s_idx = cb->args[0]; unsigned int idx = 0, s_idx = cb->args[0];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (family != NFPROTO_UNSPEC && family != table->family) if (family != NFPROTO_UNSPEC && family != table->family)
continue; continue;
@ -813,7 +838,7 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
goto err; goto err;
nft_trans_table_update(trans) = true; nft_trans_table_update(trans) = true;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
err: err:
nft_trans_destroy(trans); nft_trans_destroy(trans);
@ -848,6 +873,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
const struct nlattr * const nla[], const struct nlattr * const nla[],
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
@ -857,7 +883,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
struct nft_ctx ctx; struct nft_ctx ctx;
int err; int err;
lockdep_assert_held(&net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
attr = nla[NFTA_TABLE_NAME]; attr = nla[NFTA_TABLE_NAME];
table = nft_table_lookup(net, attr, family, genmask); table = nft_table_lookup(net, attr, family, genmask);
if (IS_ERR(table)) { if (IS_ERR(table)) {
@ -907,7 +933,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
if (err < 0) if (err < 0)
goto err_trans; goto err_trans;
list_add_tail_rcu(&table->list, &net->nft.tables); list_add_tail_rcu(&table->list, &nft_net->tables);
return 0; return 0;
err_trans: err_trans:
rhltable_destroy(&table->chains_ht); rhltable_destroy(&table->chains_ht);
@ -987,11 +1013,12 @@ static int nft_flush_table(struct nft_ctx *ctx)
static int nft_flush(struct nft_ctx *ctx, int family) static int nft_flush(struct nft_ctx *ctx, int family)
{ {
struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id);
struct nft_table *table, *nt; struct nft_table *table, *nt;
const struct nlattr * const *nla = ctx->nla; const struct nlattr * const *nla = ctx->nla;
int err = 0; int err = 0;
list_for_each_entry_safe(table, nt, &ctx->net->nft.tables, list) { list_for_each_entry_safe(table, nt, &nft_net->tables, list) {
if (family != AF_UNSPEC && table->family != family) if (family != AF_UNSPEC && table->family != family)
continue; continue;
@ -1105,7 +1132,9 @@ nft_chain_lookup_byhandle(const struct nft_table *table, u64 handle, u8 genmask)
static bool lockdep_commit_lock_is_held(struct net *net) static bool lockdep_commit_lock_is_held(struct net *net)
{ {
#ifdef CONFIG_PROVE_LOCKING #ifdef CONFIG_PROVE_LOCKING
return lockdep_is_held(&net->nft.commit_mutex); struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
return lockdep_is_held(&nft_net->commit_mutex);
#else #else
return true; return true;
#endif #endif
@ -1302,11 +1331,13 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
unsigned int idx = 0, s_idx = cb->args[0]; unsigned int idx = 0, s_idx = cb->args[0];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nftables_pernet *nft_net;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (family != NFPROTO_UNSPEC && family != table->family) if (family != NFPROTO_UNSPEC && family != table->family)
continue; continue;
@ -1499,12 +1530,13 @@ static int nft_chain_parse_hook(struct net *net,
struct nft_chain_hook *hook, u8 family, struct nft_chain_hook *hook, u8 family,
bool autoload) bool autoload)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nlattr *ha[NFTA_HOOK_MAX + 1]; struct nlattr *ha[NFTA_HOOK_MAX + 1];
const struct nft_chain_type *type; const struct nft_chain_type *type;
struct net_device *dev; struct net_device *dev;
int err; int err;
lockdep_assert_held(&net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
lockdep_nfnl_nft_mutex_not_held(); lockdep_nfnl_nft_mutex_not_held();
err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
@ -1773,6 +1805,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
if (nla[NFTA_CHAIN_HANDLE] && if (nla[NFTA_CHAIN_HANDLE] &&
nla[NFTA_CHAIN_NAME]) { nla[NFTA_CHAIN_NAME]) {
struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id);
struct nft_trans *tmp; struct nft_trans *tmp;
char *name; char *name;
@ -1782,7 +1815,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
goto err; goto err;
err = -EEXIST; err = -EEXIST;
list_for_each_entry(tmp, &ctx->net->nft.commit_list, list) { list_for_each_entry(tmp, &nft_net->commit_list, list) {
if (tmp->msg_type == NFT_MSG_NEWCHAIN && if (tmp->msg_type == NFT_MSG_NEWCHAIN &&
tmp->ctx.table == table && tmp->ctx.table == table &&
nft_trans_chain_update(tmp) && nft_trans_chain_update(tmp) &&
@ -1795,7 +1828,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
nft_trans_chain_name(trans) = name; nft_trans_chain_name(trans) = name;
} }
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
err: err:
@ -1809,6 +1842,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
const struct nlattr * const nla[], const struct nlattr * const nla[],
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
@ -1819,7 +1853,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
struct nft_ctx ctx; struct nft_ctx ctx;
u64 handle = 0; u64 handle = 0;
lockdep_assert_held(&net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask); table = nft_table_lookup(net, nla[NFTA_CHAIN_TABLE], family, genmask);
if (IS_ERR(table)) { if (IS_ERR(table)) {
@ -2342,11 +2376,13 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
unsigned int idx = 0, s_idx = cb->args[0]; unsigned int idx = 0, s_idx = cb->args[0];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nftables_pernet *nft_net;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (family != NFPROTO_UNSPEC && family != table->family) if (family != NFPROTO_UNSPEC && family != table->family)
continue; continue;
@ -2499,7 +2535,6 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
{ {
struct nft_expr *expr, *next; struct nft_expr *expr, *next;
lockdep_assert_held(&ctx->net->nft.commit_mutex);
/* /*
* Careful: some expressions might not be initialized in case this * Careful: some expressions might not be initialized in case this
* is called on error from nf_tables_newrule(). * is called on error from nf_tables_newrule().
@ -2579,6 +2614,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
const struct nlattr * const nla[], const struct nlattr * const nla[],
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
struct nft_expr_info *info = NULL; struct nft_expr_info *info = NULL;
@ -2595,7 +2631,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
int err, rem; int err, rem;
u64 handle, pos_handle; u64 handle, pos_handle;
lockdep_assert_held(&net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask); table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
if (IS_ERR(table)) { if (IS_ERR(table)) {
@ -2743,7 +2779,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
kvfree(info); kvfree(info);
chain->use++; chain->use++;
if (net->nft.validate_state == NFT_VALIDATE_DO) if (nft_net->validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, table); return nft_table_validate(net, table);
return 0; return 0;
@ -2765,10 +2801,11 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
const struct nft_chain *chain, const struct nft_chain *chain,
const struct nlattr *nla) const struct nlattr *nla)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
u32 id = ntohl(nla_get_be32(nla)); u32 id = ntohl(nla_get_be32(nla));
struct nft_trans *trans; struct nft_trans *trans;
list_for_each_entry(trans, &net->nft.commit_list, list) { list_for_each_entry(trans, &nft_net->commit_list, list) {
struct nft_rule *rule = nft_trans_rule(trans); struct nft_rule *rule = nft_trans_rule(trans);
if (trans->msg_type == NFT_MSG_NEWRULE && if (trans->msg_type == NFT_MSG_NEWRULE &&
@ -2887,12 +2924,13 @@ nft_select_set_ops(const struct nft_ctx *ctx,
const struct nft_set_desc *desc, const struct nft_set_desc *desc,
enum nft_set_policies policy) enum nft_set_policies policy)
{ {
struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id);
const struct nft_set_ops *ops, *bops; const struct nft_set_ops *ops, *bops;
struct nft_set_estimate est, best; struct nft_set_estimate est, best;
const struct nft_set_type *type; const struct nft_set_type *type;
u32 flags = 0; u32 flags = 0;
lockdep_assert_held(&ctx->net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
lockdep_nfnl_nft_mutex_not_held(); lockdep_nfnl_nft_mutex_not_held();
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
if (list_empty(&nf_tables_set_types)) { if (list_empty(&nf_tables_set_types)) {
@ -3038,10 +3076,11 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
const struct nft_table *table, const struct nft_table *table,
const struct nlattr *nla, u8 genmask) const struct nlattr *nla, u8 genmask)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_trans *trans; struct nft_trans *trans;
u32 id = ntohl(nla_get_be32(nla)); u32 id = ntohl(nla_get_be32(nla));
list_for_each_entry(trans, &net->nft.commit_list, list) { list_for_each_entry(trans, &nft_net->commit_list, list) {
if (trans->msg_type == NFT_MSG_NEWSET) { if (trans->msg_type == NFT_MSG_NEWSET) {
struct nft_set *set = nft_trans_set(trans); struct nft_set *set = nft_trans_set(trans);
@ -3257,14 +3296,16 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nft_ctx *ctx = cb->data, ctx_set; struct nft_ctx *ctx = cb->data, ctx_set;
struct nftables_pernet *nft_net;
if (cb->args[1]) if (cb->args[1])
return skb->len; return skb->len;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (ctx->family != NFPROTO_UNSPEC && if (ctx->family != NFPROTO_UNSPEC &&
ctx->family != table->family) ctx->family != table->family)
continue; continue;
@ -3971,6 +4012,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct nft_set_dump_ctx *dump_ctx = cb->data; struct nft_set_dump_ctx *dump_ctx = cb->data;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
struct nft_set *set; struct nft_set *set;
struct nft_set_dump_args args; struct nft_set_dump_args args;
@ -3981,7 +4023,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
int event; int event;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(table, &net->nft.tables, list) { nft_net = net_generic(net, nf_tables_net_id);
list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (dump_ctx->ctx.family != NFPROTO_UNSPEC && if (dump_ctx->ctx.family != NFPROTO_UNSPEC &&
dump_ctx->ctx.family != table->family) dump_ctx->ctx.family != table->family)
continue; continue;
@ -4571,7 +4614,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
} }
nft_trans_elem(trans) = elem; nft_trans_elem(trans) = elem;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
err6: err6:
@ -4596,6 +4639,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
const struct nlattr * const nla[], const struct nlattr * const nla[],
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
const struct nlattr *attr; const struct nlattr *attr;
struct nft_set *set; struct nft_set *set;
@ -4625,7 +4669,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
return err; return err;
} }
if (net->nft.validate_state == NFT_VALIDATE_DO) if (nft_net->validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, ctx.table); return nft_table_validate(net, ctx.table);
return 0; return 0;
@ -4738,7 +4782,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_elem_deactivate(ctx->net, set, &elem); nft_set_elem_deactivate(ctx->net, set, &elem);
nft_trans_elem(trans) = elem; nft_trans_elem(trans) = elem;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
fail_ops: fail_ops:
@ -4772,7 +4816,7 @@ static int nft_flush_set(const struct nft_ctx *ctx,
nft_set_elem_deactivate(ctx->net, set, elem); nft_set_elem_deactivate(ctx->net, set, elem);
nft_trans_elem_set(trans) = set; nft_trans_elem_set(trans) = set;
nft_trans_elem(trans) = *elem; nft_trans_elem(trans) = *elem;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); nft_trans_commit_list_add_tail(ctx->net, trans);
return 0; return 0;
err1: err1:
@ -5151,6 +5195,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
struct nft_obj_filter *filter = cb->data; struct nft_obj_filter *filter = cb->data;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nftables_pernet *nft_net;
struct nft_object *obj; struct nft_object *obj;
bool reset = false; bool reset = false;
@ -5158,9 +5203,10 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
reset = true; reset = true;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (family != NFPROTO_UNSPEC && family != table->family) if (family != NFPROTO_UNSPEC && family != table->family)
continue; continue;
@ -5826,12 +5872,14 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nft_flowtable *flowtable; struct nft_flowtable *flowtable;
struct nftables_pernet *nft_net;
const struct nft_table *table; const struct nft_table *table;
rcu_read_lock(); rcu_read_lock();
cb->seq = net->nft.base_seq; nft_net = net_generic(net, nf_tables_net_id);
cb->seq = nft_net->base_seq;
list_for_each_entry_rcu(table, &net->nft.tables, list) { list_for_each_entry_rcu(table, &nft_net->tables, list) {
if (family != NFPROTO_UNSPEC && family != table->family) if (family != NFPROTO_UNSPEC && family != table->family)
continue; continue;
@ -6001,6 +6049,7 @@ static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
u32 portid, u32 seq) u32 portid, u32 seq)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
char buf[TASK_COMM_LEN]; char buf[TASK_COMM_LEN];
int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN);
@ -6010,7 +6059,7 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
if (!nlh) if (!nlh)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || if (nla_put_be32(skb, NFTA_GEN_ID, htonl(nft_net->base_seq)) ||
nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) ||
nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current))) nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current)))
goto nla_put_failure; goto nla_put_failure;
@ -6043,6 +6092,7 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_flowtable *flowtable; struct nft_flowtable *flowtable;
struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
struct net *net; struct net *net;
@ -6050,13 +6100,14 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
return 0; return 0;
net = dev_net(dev); net = dev_net(dev);
mutex_lock(&net->nft.commit_mutex); nft_net = net_generic(net, nf_tables_net_id);
list_for_each_entry(table, &net->nft.tables, list) { mutex_lock(&nft_net->commit_mutex);
list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) { list_for_each_entry(flowtable, &table->flowtables, list) {
nft_flowtable_event(event, dev, flowtable); nft_flowtable_event(event, dev, flowtable);
} }
} }
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -6237,16 +6288,17 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
static int nf_tables_validate(struct net *net) static int nf_tables_validate(struct net *net)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_table *table; struct nft_table *table;
switch (net->nft.validate_state) { switch (nft_net->validate_state) {
case NFT_VALIDATE_SKIP: case NFT_VALIDATE_SKIP:
break; break;
case NFT_VALIDATE_NEED: case NFT_VALIDATE_NEED:
nft_validate_state_update(net, NFT_VALIDATE_DO); nft_validate_state_update(net, NFT_VALIDATE_DO);
/* fall through */ /* fall through */
case NFT_VALIDATE_DO: case NFT_VALIDATE_DO:
list_for_each_entry(table, &net->nft.tables, list) { list_for_each_entry(table, &nft_net->tables, list) {
if (nft_table_validate(net, table) < 0) if (nft_table_validate(net, table) < 0)
return -EAGAIN; return -EAGAIN;
} }
@ -6323,14 +6375,15 @@ static void nft_commit_release(struct nft_trans *trans)
static void nf_tables_commit_release(struct net *net) static void nf_tables_commit_release(struct net *net)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
if (list_empty(&net->nft.commit_list)) if (list_empty(&nft_net->commit_list))
return; return;
synchronize_rcu(); synchronize_rcu();
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
list_del(&trans->list); list_del(&trans->list);
nft_commit_release(trans); nft_commit_release(trans);
} }
@ -6369,9 +6422,10 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha
static void nf_tables_commit_chain_prepare_cancel(struct net *net) static void nf_tables_commit_chain_prepare_cancel(struct net *net)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
struct nft_chain *chain = trans->ctx.chain; struct nft_chain *chain = trans->ctx.chain;
if (trans->msg_type == NFT_MSG_NEWRULE || if (trans->msg_type == NFT_MSG_NEWRULE ||
@ -6463,6 +6517,7 @@ static void nft_chain_del(struct nft_chain *chain)
static int nf_tables_commit(struct net *net, struct sk_buff *skb) static int nf_tables_commit(struct net *net, struct sk_buff *skb)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
struct nft_trans_elem *te; struct nft_trans_elem *te;
struct nft_chain *chain; struct nft_chain *chain;
@ -6473,7 +6528,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
return -EAGAIN; return -EAGAIN;
/* 1. Allocate space for next generation rules_gen_X[] */ /* 1. Allocate space for next generation rules_gen_X[] */
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
int ret; int ret;
if (trans->msg_type == NFT_MSG_NEWRULE || if (trans->msg_type == NFT_MSG_NEWRULE ||
@ -6489,7 +6544,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
} }
/* step 2. Make rules_gen_X visible to packet path */ /* step 2. Make rules_gen_X visible to packet path */
list_for_each_entry(table, &net->nft.tables, list) { list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(chain, &table->chains, list) list_for_each_entry(chain, &table->chains, list)
nf_tables_commit_chain(net, chain); nf_tables_commit_chain(net, chain);
} }
@ -6498,12 +6553,13 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
* Bump generation counter, invalidate any dump in progress. * Bump generation counter, invalidate any dump in progress.
* Cannot fail after this point. * Cannot fail after this point.
*/ */
while (++net->nft.base_seq == 0); while (++nft_net->base_seq == 0)
;
/* step 3. Start new generation, rules_gen_X now in use. */ /* step 3. Start new generation, rules_gen_X now in use. */
net->nft.gencursor = nft_gencursor_next(net); net->nft.gencursor = nft_gencursor_next(net);
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &nft_net->commit_list, list) {
switch (trans->msg_type) { switch (trans->msg_type) {
case NFT_MSG_NEWTABLE: case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) { if (nft_trans_table_update(trans)) {
@ -6624,7 +6680,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_commit_release(net); nf_tables_commit_release(net);
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
return 0; return 0;
} }
@ -6660,10 +6716,11 @@ static void nf_tables_abort_release(struct nft_trans *trans)
static int __nf_tables_abort(struct net *net) static int __nf_tables_abort(struct net *net)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
struct nft_trans_elem *te; struct nft_trans_elem *te;
list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list, list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list,
list) { list) {
switch (trans->msg_type) { switch (trans->msg_type) {
case NFT_MSG_NEWTABLE: case NFT_MSG_NEWTABLE:
@ -6770,7 +6827,7 @@ static int __nf_tables_abort(struct net *net)
synchronize_rcu(); synchronize_rcu();
list_for_each_entry_safe_reverse(trans, next, list_for_each_entry_safe_reverse(trans, next,
&net->nft.commit_list, list) { &nft_net->commit_list, list) {
list_del(&trans->list); list_del(&trans->list);
nf_tables_abort_release(trans); nf_tables_abort_release(trans);
} }
@ -6780,22 +6837,24 @@ static int __nf_tables_abort(struct net *net)
static int nf_tables_abort(struct net *net, struct sk_buff *skb) static int nf_tables_abort(struct net *net, struct sk_buff *skb)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
int ret = __nf_tables_abort(net); int ret = __nf_tables_abort(net);
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
return ret; return ret;
} }
static bool nf_tables_valid_genid(struct net *net, u32 genid) static bool nf_tables_valid_genid(struct net *net, u32 genid)
{ {
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
bool genid_ok; bool genid_ok;
mutex_lock(&net->nft.commit_mutex); mutex_lock(&nft_net->commit_mutex);
genid_ok = genid == 0 || net->nft.base_seq == genid; genid_ok = genid == 0 || nft_net->base_seq == genid;
if (!genid_ok) if (!genid_ok)
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
/* else, commit mutex has to be released by commit or abort function */ /* else, commit mutex has to be released by commit or abort function */
return genid_ok; return genid_ok;
@ -7353,10 +7412,9 @@ int __nft_release_basechain(struct nft_ctx *ctx)
} }
EXPORT_SYMBOL_GPL(__nft_release_basechain); EXPORT_SYMBOL_GPL(__nft_release_basechain);
static void __nft_release_tables(struct net *net) static void __nft_release_table(struct net *net, struct nft_table *table)
{ {
struct nft_flowtable *flowtable, *nf; struct nft_flowtable *flowtable, *nf;
struct nft_table *table, *nt;
struct nft_chain *chain, *nc; struct nft_chain *chain, *nc;
struct nft_object *obj, *ne; struct nft_object *obj, *ne;
struct nft_rule *rule, *nr; struct nft_rule *rule, *nr;
@ -7366,71 +7424,84 @@ static void __nft_release_tables(struct net *net)
.family = NFPROTO_NETDEV, .family = NFPROTO_NETDEV,
}; };
list_for_each_entry_safe(table, nt, &net->nft.tables, list) { ctx.family = table->family;
ctx.family = table->family;
list_for_each_entry(chain, &table->chains, list) list_for_each_entry(chain, &table->chains, list)
nf_tables_unregister_hook(net, table, chain); nf_tables_unregister_hook(net, table, chain);
/* No packets are walking on these chains anymore. */ /* No packets are walking on these chains anymore. */
ctx.table = table; ctx.table = table;
list_for_each_entry(chain, &table->chains, list) { list_for_each_entry(chain, &table->chains, list) {
ctx.chain = chain; ctx.chain = chain;
list_for_each_entry_safe(rule, nr, &chain->rules, list) { list_for_each_entry_safe(rule, nr, &chain->rules, list) {
list_del(&rule->list); list_del(&rule->list);
chain->use--; chain->use--;
nf_tables_rule_release(&ctx, rule); nf_tables_rule_release(&ctx, rule);
}
} }
list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
list_del(&flowtable->list);
table->use--;
nf_tables_flowtable_destroy(flowtable);
}
list_for_each_entry_safe(set, ns, &table->sets, list) {
list_del(&set->list);
table->use--;
nft_set_destroy(set);
}
list_for_each_entry_safe(obj, ne, &table->objects, list) {
list_del(&obj->list);
table->use--;
nft_obj_destroy(&ctx, obj);
}
list_for_each_entry_safe(chain, nc, &table->chains, list) {
ctx.chain = chain;
nft_chain_del(chain);
table->use--;
nf_tables_chain_destroy(&ctx);
}
list_del(&table->list);
nf_tables_table_destroy(&ctx);
} }
list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
list_del(&flowtable->list);
table->use--;
nf_tables_flowtable_destroy(flowtable);
}
list_for_each_entry_safe(set, ns, &table->sets, list) {
list_del(&set->list);
table->use--;
nft_set_destroy(set);
}
list_for_each_entry_safe(obj, ne, &table->objects, list) {
list_del(&obj->list);
table->use--;
nft_obj_destroy(&ctx, obj);
}
list_for_each_entry_safe(chain, nc, &table->chains, list) {
ctx.chain = chain;
nft_chain_del(chain);
table->use--;
nf_tables_chain_destroy(&ctx);
}
list_del(&table->list);
nf_tables_table_destroy(&ctx);
}
static void __nft_release_tables(struct net *net)
{
struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
struct nft_table *table, *nt;
list_for_each_entry_safe(table, nt, &nft_net->tables, list)
__nft_release_table(net, table);
} }
static int __net_init nf_tables_init_net(struct net *net) static int __net_init nf_tables_init_net(struct net *net)
{ {
INIT_LIST_HEAD(&net->nft.tables); struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
INIT_LIST_HEAD(&net->nft.commit_list);
mutex_init(&net->nft.commit_mutex); INIT_LIST_HEAD(&nft_net->tables);
net->nft.base_seq = 1; INIT_LIST_HEAD(&nft_net->commit_list);
net->nft.validate_state = NFT_VALIDATE_SKIP; mutex_init(&nft_net->commit_mutex);
nft_net->base_seq = 1;
nft_net->validate_state = NFT_VALIDATE_SKIP;
return 0; return 0;
} }
static void __net_exit nf_tables_exit_net(struct net *net) static void __net_exit nf_tables_exit_net(struct net *net)
{ {
mutex_lock(&net->nft.commit_mutex); struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);
if (!list_empty(&net->nft.commit_list))
mutex_lock(&nft_net->commit_mutex);
if (!list_empty(&nft_net->commit_list))
__nf_tables_abort(net); __nf_tables_abort(net);
__nft_release_tables(net); __nft_release_tables(net);
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
WARN_ON_ONCE(!list_empty(&net->nft.tables)); WARN_ON_ONCE(!list_empty(&nft_net->tables));
} }
static struct pernet_operations nf_tables_net_ops = { static struct pernet_operations nf_tables_net_ops = {
.init = nf_tables_init_net, .init = nf_tables_init_net,
.exit = nf_tables_exit_net, .exit = nf_tables_exit_net,
.id = &nf_tables_net_id,
.size = sizeof(struct nftables_pernet),
}; };
static int __init nf_tables_module_init(void) static int __init nf_tables_module_init(void)

View file

@ -2,6 +2,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
@ -10,6 +11,8 @@
#include <net/netfilter/nf_tables_ipv4.h> #include <net/netfilter/nf_tables_ipv4.h>
#include <net/netfilter/nf_tables_ipv6.h> #include <net/netfilter/nf_tables_ipv6.h>
extern unsigned int nf_tables_net_id;
#ifdef CONFIG_NF_TABLES_IPV4 #ifdef CONFIG_NF_TABLES_IPV4
static unsigned int nft_do_chain_ipv4(void *priv, static unsigned int nft_do_chain_ipv4(void *priv,
struct sk_buff *skb, struct sk_buff *skb,
@ -315,6 +318,7 @@ static int nf_tables_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain, *nr; struct nft_chain *chain, *nr;
struct nft_ctx ctx = { struct nft_ctx ctx = {
@ -325,8 +329,9 @@ static int nf_tables_netdev_event(struct notifier_block *this,
event != NETDEV_CHANGENAME) event != NETDEV_CHANGENAME)
return NOTIFY_DONE; return NOTIFY_DONE;
mutex_lock(&ctx.net->nft.commit_mutex); nft_net = net_generic(ctx.net, nf_tables_net_id);
list_for_each_entry(table, &ctx.net->nft.tables, list) { mutex_lock(&nft_net->commit_mutex);
list_for_each_entry(table, &nft_net->tables, list) {
if (table->family != NFPROTO_NETDEV) if (table->family != NFPROTO_NETDEV)
continue; continue;
@ -340,7 +345,7 @@ static int nf_tables_netdev_event(struct notifier_block *this,
nft_netdev_event(event, dev, &ctx); nft_netdev_event(event, dev, &ctx);
} }
} }
mutex_unlock(&ctx.net->nft.commit_mutex); mutex_unlock(&nft_net->commit_mutex);
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View file

@ -15,6 +15,9 @@
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables_core.h>
#include <net/netns/generic.h>
extern unsigned int nf_tables_net_id;
struct nft_dynset { struct nft_dynset {
struct nft_set *set; struct nft_set *set;
@ -112,13 +115,14 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nftables_pernet *nft_net = net_generic(ctx->net, nf_tables_net_id);
struct nft_dynset *priv = nft_expr_priv(expr); struct nft_dynset *priv = nft_expr_priv(expr);
u8 genmask = nft_genmask_next(ctx->net); u8 genmask = nft_genmask_next(ctx->net);
struct nft_set *set; struct nft_set *set;
u64 timeout; u64 timeout;
int err; int err;
lockdep_assert_held(&ctx->net->nft.commit_mutex); lockdep_assert_held(&nft_net->commit_mutex);
if (tb[NFTA_DYNSET_SET_NAME] == NULL || if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
tb[NFTA_DYNSET_OP] == NULL || tb[NFTA_DYNSET_OP] == NULL ||