#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
+#include <net/net_namespace.h>
#include <net/sock.h>
-static LIST_HEAD(nf_tables_afinfo);
static LIST_HEAD(nf_tables_expressions);
/**
* Register the address family for use with nf_tables. Returns zero on
* success or a negative errno code otherwise.
*/
-int nft_register_afinfo(struct nft_af_info *afi)
+int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
{
INIT_LIST_HEAD(&afi->tables);
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- list_add_tail(&afi->list, &nf_tables_afinfo);
+ list_add_tail(&afi->list, &net->nft.af_info);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return 0;
}
}
EXPORT_SYMBOL_GPL(nft_unregister_afinfo);
-static struct nft_af_info *nft_afinfo_lookup(int family)
+static struct nft_af_info *nft_afinfo_lookup(struct net *net, int family)
{
struct nft_af_info *afi;
- list_for_each_entry(afi, &nf_tables_afinfo, list) {
+ list_for_each_entry(afi, &net->nft.af_info, list) {
if (afi->family == family)
return afi;
}
return NULL;
}
-static struct nft_af_info *nf_tables_afinfo_lookup(int family, bool autoload)
+static struct nft_af_info *
+nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
{
struct nft_af_info *afi;
- afi = nft_afinfo_lookup(family);
+ afi = nft_afinfo_lookup(net, family);
if (afi != NULL)
return afi;
#ifdef CONFIG_MODULES
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
request_module("nft-afinfo-%u", family);
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- afi = nft_afinfo_lookup(family);
+ afi = nft_afinfo_lookup(net, family);
if (afi != NULL)
return ERR_PTR(-EAGAIN);
}
}
static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi,
- const struct nlattr *nla,
- bool autoload)
+ const struct nlattr *nla)
{
struct nft_table *table;
if (table != NULL)
return table;
-#ifdef CONFIG_MODULES
- if (autoload) {
- nfnl_unlock(NFNL_SUBSYS_NFTABLES);
- request_module("nft-table-%u-%*.s", afi->family,
- nla_len(nla)-1, (const char *)nla_data(nla));
- nfnl_lock(NFNL_SUBSYS_NFTABLES);
- if (nft_table_lookup(afi, nla))
- return ERR_PTR(-EAGAIN);
- }
-#endif
return ERR_PTR(-ENOENT);
}
return ++table->hgenerator;
}
+static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
+
+static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
+{
+ int i;
+
+ for (i=0; i<NFT_CHAIN_T_MAX; i++) {
+ if (chain_type[family][i] != NULL &&
+ !nla_strcmp(nla, chain_type[family][i]->name))
+ return i;
+ }
+ return -1;
+}
+
+static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
+ const struct nlattr *nla,
+ bool autoload)
+{
+ int type;
+
+ type = __nf_tables_chain_type_lookup(afi->family, nla);
+#ifdef CONFIG_MODULES
+ if (type < 0 && autoload) {
+ nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+ request_module("nft-chain-%u-%*.s", afi->family,
+ nla_len(nla)-1, (const char *)nla_data(nla));
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ type = __nf_tables_chain_type_lookup(afi->family, nla);
+ }
+#endif
+ return type;
+}
+
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
[NFTA_TABLE_NAME] = { .type = NLA_STRING },
+ [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
};
static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
- if (nla_put_string(skb, NFTA_TABLE_NAME, table->name))
+ if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
+ nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)))
goto nla_put_failure;
return nlmsg_end(skb, nlh);
const struct nft_af_info *afi;
const struct nft_table *table;
unsigned int idx = 0, s_idx = cb->args[0];
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
- list_for_each_entry(afi, &nf_tables_afinfo, list) {
+ list_for_each_entry(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family)
continue;
const struct nft_af_info *afi;
const struct nft_table *table;
struct sk_buff *skb2;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
return netlink_dump_start(nlsk, skb, nlh, &c);
}
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table))
return PTR_ERR(table);
return err;
}
+static int nf_tables_table_enable(struct nft_table *table)
+{
+ struct nft_chain *chain;
+ int err, i = 0;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ err = nf_register_hook(&nft_base_chain(chain)->ops);
+ if (err < 0)
+ goto err;
+
+ i++;
+ }
+ return 0;
+err:
+ list_for_each_entry(chain, &table->chains, list) {
+ if (i-- <= 0)
+ break;
+
+ nf_unregister_hook(&nft_base_chain(chain)->ops);
+ }
+ return err;
+}
+
+static int nf_tables_table_disable(struct nft_table *table)
+{
+ struct nft_chain *chain;
+
+ list_for_each_entry(chain, &table->chains, list)
+ nf_unregister_hook(&nft_base_chain(chain)->ops);
+
+ return 0;
+}
+
+static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[],
+ struct nft_af_info *afi, struct nft_table *table)
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ int family = nfmsg->nfgen_family, ret = 0;
+
+ if (nla[NFTA_TABLE_FLAGS]) {
+ __be32 flags;
+
+ flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
+ if (flags & ~NFT_TABLE_F_DORMANT)
+ return -EINVAL;
+
+ if ((flags & NFT_TABLE_F_DORMANT) &&
+ !(table->flags & NFT_TABLE_F_DORMANT)) {
+ ret = nf_tables_table_disable(table);
+ if (ret >= 0)
+ table->flags |= NFT_TABLE_F_DORMANT;
+ } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+ table->flags & NFT_TABLE_F_DORMANT) {
+ ret = nf_tables_table_enable(table);
+ if (ret >= 0)
+ table->flags &= ~NFT_TABLE_F_DORMANT;
+ }
+ if (ret < 0)
+ goto err;
+ }
+
+ nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+err:
+ return ret;
+}
+
static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
const struct nlattr *name;
struct nft_af_info *afi;
struct nft_table *table;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
- afi = nf_tables_afinfo_lookup(family, true);
+ afi = nf_tables_afinfo_lookup(net, family, true);
if (IS_ERR(afi))
return PTR_ERR(afi);
name = nla[NFTA_TABLE_NAME];
- table = nf_tables_table_lookup(afi, name, false);
+ table = nf_tables_table_lookup(afi, name);
if (IS_ERR(table)) {
if (PTR_ERR(table) != -ENOENT)
return PTR_ERR(table);
return -EEXIST;
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
- return 0;
+ return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
}
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
+ if (nla[NFTA_TABLE_FLAGS]) {
+ __be32 flags;
+
+ flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
+ if (flags & ~NFT_TABLE_F_DORMANT) {
+ kfree(table);
+ return -EINVAL;
+ }
+
+ table->flags |= flags;
+ }
+
list_add_tail(&table->list, &afi->tables);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
return 0;
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
struct nft_table *table;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_BUILTIN)
- return -EOPNOTSUPP;
-
if (table->use)
return -EBUSY;
return 0;
}
-static struct nft_table *__nf_tables_table_lookup(const struct nft_af_info *afi,
- const char *name)
-{
- struct nft_table *table;
-
- list_for_each_entry(table, &afi->tables, list) {
- if (!strcmp(name, table->name))
- return table;
- }
-
- return ERR_PTR(-ENOENT);
-}
-
-static int nf_tables_chain_notify(const struct sk_buff *oskb,
- const struct nlmsghdr *nlh,
- const struct nft_table *table,
- const struct nft_chain *chain,
- int event, int family);
-
-/**
- * nft_register_table - register a built-in table
- *
- * @table: the table to register
- * @family: protocol family to register table with
- *
- * Register a built-in table for use with nf_tables. Returns zero on
- * success or a negative errno code otherwise.
- */
-int nft_register_table(struct nft_table *table, int family)
+int nft_register_chain_type(struct nf_chain_type *ctype)
{
- struct nft_af_info *afi;
- struct nft_table *t;
- struct nft_chain *chain;
- int err;
+ int err = 0;
nfnl_lock(NFNL_SUBSYS_NFTABLES);
-again:
- afi = nf_tables_afinfo_lookup(family, true);
- if (IS_ERR(afi)) {
- err = PTR_ERR(afi);
- if (err == -EAGAIN)
- goto again;
- goto err;
- }
-
- t = __nf_tables_table_lookup(afi, table->name);
- if (IS_ERR(t)) {
- err = PTR_ERR(t);
- if (err != -ENOENT)
- goto err;
- t = NULL;
+ if (chain_type[ctype->family][ctype->type] != NULL) {
+ err = -EBUSY;
+ goto out;
}
- if (t != NULL) {
- err = -EEXIST;
- goto err;
- }
+ if (!try_module_get(ctype->me))
+ goto out;
- table->flags |= NFT_TABLE_BUILTIN;
- INIT_LIST_HEAD(&table->sets);
- list_add_tail(&table->list, &afi->tables);
- nf_tables_table_notify(NULL, NULL, table, NFT_MSG_NEWTABLE, family);
- list_for_each_entry(chain, &table->chains, list)
- nf_tables_chain_notify(NULL, NULL, table, chain,
- NFT_MSG_NEWCHAIN, family);
- err = 0;
-err:
+ chain_type[ctype->family][ctype->type] = ctype;
+out:
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return err;
}
-EXPORT_SYMBOL_GPL(nft_register_table);
+EXPORT_SYMBOL_GPL(nft_register_chain_type);
-/**
- * nft_unregister_table - unregister a built-in table
- *
- * @table: the table to unregister
- * @family: protocol family to unregister table with
- *
- * Unregister a built-in table for use with nf_tables.
- */
-void nft_unregister_table(struct nft_table *table, int family)
+void nft_unregister_chain_type(struct nf_chain_type *ctype)
{
- struct nft_chain *chain;
-
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- list_del(&table->list);
- list_for_each_entry(chain, &table->chains, list)
- nf_tables_chain_notify(NULL, NULL, table, chain,
- NFT_MSG_DELCHAIN, family);
- nf_tables_table_notify(NULL, NULL, table, NFT_MSG_DELTABLE, family);
+ chain_type[ctype->family][ctype->type] = NULL;
+ module_put(ctype->me);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
-EXPORT_SYMBOL_GPL(nft_unregister_table);
+EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
/*
* Chains
[NFTA_CHAIN_NAME] = { .type = NLA_STRING,
.len = NFT_CHAIN_MAXNAMELEN - 1 },
[NFTA_CHAIN_HOOK] = { .type = NLA_NESTED },
+ [NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
+ [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING },
+ [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
};
static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
[NFTA_HOOK_PRIORITY] = { .type = NLA_U32 },
};
+static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
+{
+ struct nft_stats *cpu_stats, total;
+ struct nlattr *nest;
+ int cpu;
+
+ memset(&total, 0, sizeof(total));
+ for_each_possible_cpu(cpu) {
+ cpu_stats = per_cpu_ptr(stats, cpu);
+ total.pkts += cpu_stats->pkts;
+ total.bytes += cpu_stats->bytes;
+ }
+ nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) ||
+ nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes)))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
int event, u32 flags, int family,
const struct nft_table *table,
goto nla_put_failure;
if (chain->flags & NFT_BASE_CHAIN) {
- const struct nf_hook_ops *ops = &nft_base_chain(chain)->ops;
- struct nlattr *nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
+ const struct nft_base_chain *basechain = nft_base_chain(chain);
+ const struct nf_hook_ops *ops = &basechain->ops;
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
if (nest == NULL)
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
goto nla_put_failure;
nla_nest_end(skb, nest);
+
+ if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
+ htonl(basechain->policy)))
+ goto nla_put_failure;
+
+ if (nla_put_string(skb, NFTA_CHAIN_TYPE,
+ chain_type[ops->pf][nft_base_chain(chain)->type]->name))
+ goto nla_put_failure;
+
+ if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
+ goto nla_put_failure;
}
+ if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
+ goto nla_put_failure;
+
return nlmsg_end(skb, nlh);
nla_put_failure:
const struct nft_table *table;
const struct nft_chain *chain;
unsigned int idx = 0, s_idx = cb->args[0];
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
- list_for_each_entry(afi, &nf_tables_afinfo, list) {
+ list_for_each_entry(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family)
continue;
const struct nft_table *table;
const struct nft_chain *chain;
struct sk_buff *skb2;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
return netlink_dump_start(nlsk, skb, nlh, &c);
}
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
return err;
}
+static int
+nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr)
+{
+ switch (ntohl(nla_get_be32(attr))) {
+ case NF_DROP:
+ chain->policy = NF_DROP;
+ break;
+ case NF_ACCEPT:
+ chain->policy = NF_ACCEPT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+ [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
+ [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
+};
+
+static int
+nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
+{
+ struct nlattr *tb[NFTA_COUNTER_MAX+1];
+ struct nft_stats __percpu *newstats;
+ struct nft_stats *stats;
+ int err;
+
+ err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
+ if (err < 0)
+ return err;
+
+ if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
+ return -EINVAL;
+
+ newstats = alloc_percpu(struct nft_stats);
+ if (newstats == NULL)
+ return -ENOMEM;
+
+ /* Restore old counters on this cpu, no problem. Per-cpu statistics
+ * are not exposed to userspace.
+ */
+ stats = this_cpu_ptr(newstats);
+ stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
+ stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
+
+ if (chain->stats) {
+ /* nfnl_lock is held, add some nfnl function for this, later */
+ struct nft_stats __percpu *oldstats =
+ rcu_dereference_protected(chain->stats, 1);
+
+ rcu_assign_pointer(chain->stats, newstats);
+ synchronize_rcu();
+ free_percpu(oldstats);
+ } else
+ rcu_assign_pointer(chain->stats, newstats);
+
+ return 0;
+}
+
static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
const struct nft_af_info *afi;
struct nft_table *table;
struct nft_chain *chain;
- struct nft_base_chain *basechain;
+ struct nft_base_chain *basechain = NULL;
struct nlattr *ha[NFTA_HOOK_MAX + 1];
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
u64 handle = 0;
int err;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
- afi = nf_tables_afinfo_lookup(family, true);
+ afi = nf_tables_afinfo_lookup(net, family, true);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], create);
+ table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
!IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
return -EEXIST;
+ if (nla[NFTA_CHAIN_POLICY]) {
+ if (!(chain->flags & NFT_BASE_CHAIN))
+ return -EOPNOTSUPP;
+
+ err = nf_tables_chain_policy(nft_base_chain(chain),
+ nla[NFTA_CHAIN_POLICY]);
+ if (err < 0)
+ return err;
+ }
+
+ if (nla[NFTA_CHAIN_COUNTERS]) {
+ if (!(chain->flags & NFT_BASE_CHAIN))
+ return -EOPNOTSUPP;
+
+ err = nf_tables_counters(nft_base_chain(chain),
+ nla[NFTA_CHAIN_COUNTERS]);
+ if (err < 0)
+ return err;
+ }
+
if (nla[NFTA_CHAIN_HANDLE] && name)
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
if (nla[NFTA_CHAIN_HOOK]) {
struct nf_hook_ops *ops;
+ nf_hookfn *hookfn;
+ u32 hooknum;
+ int type = NFT_CHAIN_T_DEFAULT;
+
+ if (nla[NFTA_CHAIN_TYPE]) {
+ type = nf_tables_chain_type_lookup(afi,
+ nla[NFTA_CHAIN_TYPE],
+ create);
+ if (type < 0)
+ return -ENOENT;
+ }
err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
nft_hook_policy);
if (ha[NFTA_HOOK_HOOKNUM] == NULL ||
ha[NFTA_HOOK_PRIORITY] == NULL)
return -EINVAL;
- if (ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])) >= afi->nhooks)
+
+ hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
+ if (hooknum >= afi->nhooks)
return -EINVAL;
+ hookfn = chain_type[family][type]->fn[hooknum];
+ if (hookfn == NULL)
+ return -EOPNOTSUPP;
+
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
if (basechain == NULL)
return -ENOMEM;
+
+ basechain->type = type;
chain = &basechain->chain;
ops = &basechain->ops;
ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
ops->priv = chain;
- ops->hook = nft_do_chain;
+ ops->hook = hookfn;
if (afi->hooks[ops->hooknum])
ops->hook = afi->hooks[ops->hooknum];
chain->flags |= NFT_BASE_CHAIN;
+
+ if (nla[NFTA_CHAIN_POLICY]) {
+ err = nf_tables_chain_policy(basechain,
+ nla[NFTA_CHAIN_POLICY]);
+ if (err < 0) {
+ free_percpu(basechain->stats);
+ kfree(basechain);
+ return err;
+ }
+ } else
+ basechain->policy = NF_ACCEPT;
+
+ if (nla[NFTA_CHAIN_COUNTERS]) {
+ err = nf_tables_counters(basechain,
+ nla[NFTA_CHAIN_COUNTERS]);
+ if (err < 0) {
+ free_percpu(basechain->stats);
+ kfree(basechain);
+ return err;
+ }
+ } else {
+ struct nft_stats __percpu *newstats;
+
+ newstats = alloc_percpu(struct nft_stats);
+ if (newstats == NULL)
+ return -ENOMEM;
+
+ rcu_assign_pointer(nft_base_chain(chain)->stats,
+ newstats);
+ }
} else {
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
INIT_LIST_HEAD(&chain->rules);
chain->handle = nf_tables_alloc_handle(table);
+ chain->net = net;
+ chain->table = table;
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+ if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+ chain->flags & NFT_BASE_CHAIN) {
+ err = nf_register_hook(&nft_base_chain(chain)->ops);
+ if (err < 0) {
+ free_percpu(basechain->stats);
+ kfree(basechain);
+ return err;
+ }
+ }
list_add_tail(&chain->list, &table->chains);
table->use++;
notify:
BUG_ON(chain->use > 0);
- if (chain->flags & NFT_BASE_CHAIN)
+ if (chain->flags & NFT_BASE_CHAIN) {
+ free_percpu(nft_base_chain(chain)->stats);
kfree(nft_base_chain(chain));
- else
+ } else
kfree(chain);
}
const struct nft_af_info *afi;
struct nft_table *table;
struct nft_chain *chain;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
if (IS_ERR(chain))
return PTR_ERR(chain);
- if (chain->flags & NFT_CHAIN_BUILTIN)
- return -EOPNOTSUPP;
-
if (!list_empty(&chain->rules))
return -EBUSY;
list_del(&chain->list);
table->use--;
- if (chain->flags & NFT_BASE_CHAIN)
+ if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+ chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops);
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
const struct nlmsghdr *nlh,
const struct nft_af_info *afi,
const struct nft_table *table,
- const struct nft_chain *chain)
+ const struct nft_chain *chain,
+ const struct nlattr * const *nla)
{
+ ctx->net = sock_net(skb->sk);
ctx->skb = skb;
ctx->nlh = nlh;
ctx->afi = afi;
ctx->table = table;
ctx->chain = chain;
+ ctx->nla = nla;
}
/*
*/
/**
- * nft_register_expr - register nf_tables expr operations
- * @ops: expr operations
+ * nft_register_expr - register nf_tables expr type
+ * @ops: expr type
*
- * Registers the expr operations for use with nf_tables. Returns zero on
+ * Registers the expr type for use with nf_tables. Returns zero on
* success or a negative errno code otherwise.
*/
-int nft_register_expr(struct nft_expr_ops *ops)
+int nft_register_expr(struct nft_expr_type *type)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- list_add_tail(&ops->list, &nf_tables_expressions);
+ list_add_tail(&type->list, &nf_tables_expressions);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return 0;
}
EXPORT_SYMBOL_GPL(nft_register_expr);
/**
- * nft_unregister_expr - unregister nf_tables expr operations
- * @ops: expr operations
+ * nft_unregister_expr - unregister nf_tables expr type
+ * @ops: expr type
*
- * Unregisters the expr operations for use with nf_tables.
+ * Unregisters the expr typefor use with nf_tables.
*/
-void nft_unregister_expr(struct nft_expr_ops *ops)
+void nft_unregister_expr(struct nft_expr_type *type)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- list_del(&ops->list);
+ list_del(&type->list);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_unregister_expr);
-static const struct nft_expr_ops *__nft_expr_ops_get(struct nlattr *nla)
+static const struct nft_expr_type *__nft_expr_type_get(struct nlattr *nla)
{
- const struct nft_expr_ops *ops;
+ const struct nft_expr_type *type;
- list_for_each_entry(ops, &nf_tables_expressions, list) {
- if (!nla_strcmp(nla, ops->name))
- return ops;
+ list_for_each_entry(type, &nf_tables_expressions, list) {
+ if (!nla_strcmp(nla, type->name))
+ return type;
}
return NULL;
}
-static const struct nft_expr_ops *nft_expr_ops_get(struct nlattr *nla)
+static const struct nft_expr_type *nft_expr_type_get(struct nlattr *nla)
{
- const struct nft_expr_ops *ops;
+ const struct nft_expr_type *type;
if (nla == NULL)
return ERR_PTR(-EINVAL);
- ops = __nft_expr_ops_get(nla);
- if (ops != NULL && try_module_get(ops->owner))
- return ops;
+ type = __nft_expr_type_get(nla);
+ if (type != NULL && try_module_get(type->owner))
+ return type;
#ifdef CONFIG_MODULES
- if (ops == NULL) {
+ if (type == NULL) {
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
request_module("nft-expr-%.*s",
nla_len(nla), (char *)nla_data(nla));
nfnl_lock(NFNL_SUBSYS_NFTABLES);
- if (__nft_expr_ops_get(nla))
+ if (__nft_expr_type_get(nla))
return ERR_PTR(-EAGAIN);
}
#endif
static int nf_tables_fill_expr_info(struct sk_buff *skb,
const struct nft_expr *expr)
{
- if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->name))
+ if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
goto nla_put_failure;
if (expr->ops->dump) {
struct nft_expr_info {
const struct nft_expr_ops *ops;
- struct nlattr *tb[NFTA_EXPR_MAX + 1];
+ struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
};
-static int nf_tables_expr_parse(const struct nlattr *nla,
+static int nf_tables_expr_parse(const struct nft_ctx *ctx,
+ const struct nlattr *nla,
struct nft_expr_info *info)
{
+ const struct nft_expr_type *type;
const struct nft_expr_ops *ops;
+ struct nlattr *tb[NFTA_EXPR_MAX + 1];
int err;
- err = nla_parse_nested(info->tb, NFTA_EXPR_MAX, nla, nft_expr_policy);
+ err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy);
if (err < 0)
return err;
- ops = nft_expr_ops_get(info->tb[NFTA_EXPR_NAME]);
- if (IS_ERR(ops))
- return PTR_ERR(ops);
+ type = nft_expr_type_get(tb[NFTA_EXPR_NAME]);
+ if (IS_ERR(type))
+ return PTR_ERR(type);
+
+ if (tb[NFTA_EXPR_DATA]) {
+ err = nla_parse_nested(info->tb, type->maxattr,
+ tb[NFTA_EXPR_DATA], type->policy);
+ if (err < 0)
+ goto err1;
+ } else
+ memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1));
+
+ if (type->select_ops != NULL) {
+ ops = type->select_ops(ctx,
+ (const struct nlattr * const *)info->tb);
+ if (IS_ERR(ops)) {
+ err = PTR_ERR(ops);
+ goto err1;
+ }
+ } else
+ ops = type->ops;
+
info->ops = ops;
return 0;
+
+err1:
+ module_put(type->owner);
+ return err;
}
static int nf_tables_newexpr(const struct nft_ctx *ctx,
- struct nft_expr_info *info,
+ const struct nft_expr_info *info,
struct nft_expr *expr)
{
const struct nft_expr_ops *ops = info->ops;
expr->ops = ops;
if (ops->init) {
- struct nlattr *ma[ops->maxattr + 1];
-
- if (info->tb[NFTA_EXPR_DATA]) {
- err = nla_parse_nested(ma, ops->maxattr,
- info->tb[NFTA_EXPR_DATA],
- ops->policy);
- if (err < 0)
- goto err1;
- } else
- memset(ma, 0, sizeof(ma[0]) * (ops->maxattr + 1));
-
- err = ops->init(ctx, expr, (const struct nlattr **)ma);
+ err = ops->init(ctx, expr, (const struct nlattr **)info->tb);
if (err < 0)
goto err1;
}
- info->ops = NULL;
return 0;
err1:
{
if (expr->ops->destroy)
expr->ops->destroy(expr);
- module_put(expr->ops->owner);
+ module_put(expr->ops->type->owner);
}
/*
.len = NFT_CHAIN_MAXNAMELEN - 1 },
[NFTA_RULE_HANDLE] = { .type = NLA_U64 },
[NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
+ [NFTA_RULE_COMPAT] = { .type = NLA_NESTED },
+ [NFTA_RULE_POSITION] = { .type = NLA_U64 },
};
static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
struct nfgenmsg *nfmsg;
const struct nft_expr *expr, *next;
struct nlattr *list;
+ const struct nft_rule *prule;
+ int type = event | NFNL_SUBSYS_NFTABLES << 8;
- event |= NFNL_SUBSYS_NFTABLES << 8;
- nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+ nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg),
flags);
if (nlh == NULL)
goto nla_put_failure;
if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle)))
goto nla_put_failure;
+ if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) {
+ prule = list_entry(rule->list.prev, struct nft_rule, list);
+ if (nla_put_be64(skb, NFTA_RULE_POSITION,
+ cpu_to_be64(prule->handle)))
+ goto nla_put_failure;
+ }
+
list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS);
if (list == NULL)
goto nla_put_failure;
return err;
}
+static inline bool
+nft_rule_is_active(struct net *net, const struct nft_rule *rule)
+{
+ return (rule->genmask & (1 << net->nft.gencursor)) == 0;
+}
+
+static inline int gencursor_next(struct net *net)
+{
+ return net->nft.gencursor+1 == 1 ? 1 : 0;
+}
+
+static inline int
+nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
+{
+ return (rule->genmask & (1 << gencursor_next(net))) == 0;
+}
+
+static inline void
+nft_rule_activate_next(struct net *net, struct nft_rule *rule)
+{
+ /* Now inactive, will be active in the future */
+ rule->genmask = (1 << net->nft.gencursor);
+}
+
+static inline void
+nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
+{
+ rule->genmask = (1 << gencursor_next(net));
+}
+
+static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
+{
+ rule->genmask = 0;
+}
+
static int nf_tables_dump_rules(struct sk_buff *skb,
struct netlink_callback *cb)
{
const struct nft_chain *chain;
const struct nft_rule *rule;
unsigned int idx = 0, s_idx = cb->args[0];
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
+ u8 genctr = ACCESS_ONCE(net->nft.genctr);
+ u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
- list_for_each_entry(afi, &nf_tables_afinfo, list) {
+ list_for_each_entry(afi, &net->nft.af_info, list) {
if (family != NFPROTO_UNSPEC && family != afi->family)
continue;
list_for_each_entry(table, &afi->tables, list) {
list_for_each_entry(chain, &table->chains, list) {
list_for_each_entry(rule, &chain->rules, list) {
+ if (!nft_rule_is_active(net, rule))
+ goto cont;
if (idx < s_idx)
goto cont;
if (idx > s_idx)
}
}
done:
+ /* Invalidate this dump, a transition to the new generation happened */
+ if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
+ return -EBUSY;
+
cb->args[0] = idx;
return skb->len;
}
const struct nft_chain *chain;
const struct nft_rule *rule;
struct sk_buff *skb2;
+ struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
return netlink_dump_start(nlsk, skb, nlh, &c);
}
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
static struct nft_expr_info *info;
+static struct nft_rule_trans *
+nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx)
+{
+ struct nft_rule_trans *rupd;
+
+ rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
+ if (rupd == NULL)
+ return NULL;
+
+ rupd->chain = ctx->chain;
+ rupd->table = ctx->table;
+ rupd->rule = rule;
+ rupd->family = ctx->afi->family;
+ rupd->nlh = ctx->nlh;
+ list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
+
+ return rupd;
+}
+
static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
+ struct net *net = sock_net(skb->sk);
struct nft_table *table;
struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL;
+ struct nft_rule_trans *repl = NULL;
struct nft_expr *expr;
struct nft_ctx ctx;
struct nlattr *tmp;
unsigned int size, i, n;
int err, rem;
bool create;
- u64 handle;
+ u64 handle, pos_handle;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
- afi = nf_tables_afinfo_lookup(nfmsg->nfgen_family, create);
+ afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], create);
+ table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
handle = nf_tables_alloc_handle(table);
}
+ if (nla[NFTA_RULE_POSITION]) {
+ if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+ return -EOPNOTSUPP;
+
+ pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
+ old_rule = __nf_tables_rule_lookup(chain, pos_handle);
+ if (IS_ERR(old_rule))
+ return PTR_ERR(old_rule);
+ }
+
+ nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+
n = 0;
size = 0;
if (nla[NFTA_RULE_EXPRESSIONS]) {
goto err1;
if (n == NFT_RULE_MAXEXPRS)
goto err1;
- err = nf_tables_expr_parse(tmp, &info[n]);
+ err = nf_tables_expr_parse(&ctx, tmp, &info[n]);
if (err < 0)
goto err1;
size += info[n].ops->size;
if (rule == NULL)
goto err1;
+ nft_rule_activate_next(net, rule);
+
rule->handle = handle;
rule->dlen = size;
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain);
expr = nft_expr_first(rule);
for (i = 0; i < n; i++) {
err = nf_tables_newexpr(&ctx, &info[i], expr);
if (err < 0)
goto err2;
+ info[i].ops = NULL;
expr = nft_expr_next(expr);
}
- /* Register hook when first rule is inserted into a base chain */
- if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN) {
- err = nf_register_hook(&nft_base_chain(chain)->ops);
- if (err < 0)
- goto err2;
- }
-
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
- list_replace_rcu(&old_rule->list, &rule->list);
- nf_tables_rule_destroy(old_rule);
+ if (nft_rule_is_active_next(net, old_rule)) {
+ repl = nf_tables_trans_add(old_rule, &ctx);
+ if (repl == NULL) {
+ err = -ENOMEM;
+ goto err2;
+ }
+ nft_rule_disactivate_next(net, old_rule);
+ list_add_tail(&rule->list, &old_rule->list);
+ } else {
+ err = -ENOENT;
+ goto err2;
+ }
} else if (nlh->nlmsg_flags & NLM_F_APPEND)
- list_add_tail_rcu(&rule->list, &chain->rules);
- else
- list_add_rcu(&rule->list, &chain->rules);
+ if (old_rule)
+ list_add_rcu(&rule->list, &old_rule->list);
+ else
+ list_add_tail_rcu(&rule->list, &chain->rules);
+ else {
+ if (old_rule)
+ list_add_tail_rcu(&rule->list, &old_rule->list);
+ else
+ list_add_rcu(&rule->list, &chain->rules);
+ }
- nf_tables_rule_notify(skb, nlh, table, chain, rule, NFT_MSG_NEWRULE,
- nlh->nlmsg_flags & (NLM_F_APPEND | NLM_F_REPLACE),
- nfmsg->nfgen_family);
+ if (nf_tables_trans_add(rule, &ctx) == NULL) {
+ err = -ENOMEM;
+ goto err3;
+ }
return 0;
+err3:
+ list_del_rcu(&rule->list);
+ if (repl) {
+ list_del_rcu(&repl->rule->list);
+ list_del(&repl->list);
+ nft_rule_clear(net, repl->rule);
+ kfree(repl);
+ }
err2:
nf_tables_rule_destroy(rule);
err1:
for (i = 0; i < n; i++) {
if (info[i].ops != NULL)
- module_put(info[i].ops->owner);
+ module_put(info[i].ops->type->owner);
}
return err;
}
+static int
+nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
+{
+ /* You cannot delete the same rule twice */
+ if (nft_rule_is_active_next(ctx->net, rule)) {
+ if (nf_tables_trans_add(rule, ctx) == NULL)
+ return -ENOMEM;
+ nft_rule_disactivate_next(ctx->net, rule);
+ return 0;
+ }
+ return -ENOENT;
+}
+
static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
+ struct net *net = sock_net(skb->sk);
const struct nft_table *table;
struct nft_chain *chain;
struct nft_rule *rule, *tmp;
- int family = nfmsg->nfgen_family;
+ int family = nfmsg->nfgen_family, err = 0;
+ struct nft_ctx ctx;
- afi = nf_tables_afinfo_lookup(family, false);
+ afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
if (IS_ERR(chain))
return PTR_ERR(chain);
+ nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+
if (nla[NFTA_RULE_HANDLE]) {
rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
if (IS_ERR(rule))
return PTR_ERR(rule);
- /* List removal must be visible before destroying expressions */
- list_del_rcu(&rule->list);
-
- nf_tables_rule_notify(skb, nlh, table, chain, rule,
- NFT_MSG_DELRULE, 0, family);
- nf_tables_rule_destroy(rule);
+ err = nf_tables_delrule_one(&ctx, rule);
} else {
/* Remove all rules in this chain */
list_for_each_entry_safe(rule, tmp, &chain->rules, list) {
- list_del_rcu(&rule->list);
+ err = nf_tables_delrule_one(&ctx, rule);
+ if (err < 0)
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int nf_tables_commit(struct sk_buff *skb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct nft_rule_trans *rupd, *tmp;
+
+ /* Bump generation counter, invalidate any dump in progress */
+ net->nft.genctr++;
+
+ /* A new generation has just started */
+ net->nft.gencursor = gencursor_next(net);
+
+ /* Make sure all packets have left the previous generation before
+ * purging old rules.
+ */
+ synchronize_rcu();
+
+ list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
+ /* Delete this rule from the dirty list */
+ list_del(&rupd->list);
- nf_tables_rule_notify(skb, nlh, table, chain, rule,
- NFT_MSG_DELRULE, 0, family);
- nf_tables_rule_destroy(rule);
+ /* This rule was inactive in the past and just became active.
+ * Clear the next bit of the genmask since its meaning has
+ * changed, now it is the future.
+ */
+ if (nft_rule_is_active(net, rupd->rule)) {
+ nft_rule_clear(net, rupd->rule);
+ nf_tables_rule_notify(skb, rupd->nlh, rupd->table,
+ rupd->chain, rupd->rule,
+ NFT_MSG_NEWRULE, 0,
+ rupd->family);
+ kfree(rupd);
+ continue;
}
+
+ /* This rule is in the past, get rid of it */
+ list_del_rcu(&rupd->rule->list);
+ nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain,
+ rupd->rule, NFT_MSG_DELRULE, 0,
+ rupd->family);
+ nf_tables_rule_destroy(rupd->rule);
+ kfree(rupd);
}
- /* Unregister hook when last rule from base chain is deleted */
- if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN)
- nf_unregister_hook(&nft_base_chain(chain)->ops);
+ return 0;
+}
+
+static int nf_tables_abort(struct sk_buff *skb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct nft_rule_trans *rupd, *tmp;
+ list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
+ /* Delete all rules from the dirty list */
+ list_del(&rupd->list);
+
+ if (!nft_rule_is_active_next(net, rupd->rule)) {
+ nft_rule_clear(net, rupd->rule);
+ kfree(rupd);
+ continue;
+ }
+
+ /* This rule is inactive, get rid of it */
+ list_del_rcu(&rupd->rule->list);
+ nf_tables_rule_destroy(rupd->rule);
+ kfree(rupd);
+ }
return 0;
}
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
+ struct net *net = sock_net(skb->sk);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
const struct nft_table *table = NULL;
- afi = nf_tables_afinfo_lookup(nfmsg->nfgen_family, false);
+ afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
if (nla[NFTA_SET_TABLE] != NULL) {
- table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
}
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
return 0;
}
{
struct sk_buff *skb;
u32 portid = NETLINK_CB(ctx->skb).portid;
- struct net *net = sock_net(ctx->skb->sk);
bool report;
int err;
report = nlmsg_report(ctx->nlh);
- if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+ if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
return 0;
err = -ENOBUFS;
goto err;
}
- err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+ err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
GFP_KERNEL);
err:
if (err < 0)
- nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+ nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
return err;
}
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_set_ops *ops;
const struct nft_af_info *afi;
+ struct net *net = sock_net(skb->sk);
struct nft_table *table;
struct nft_set *set;
struct nft_ctx ctx;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
- afi = nf_tables_afinfo_lookup(nfmsg->nfgen_family, create);
+ afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE], create);
+ table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(&ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
const struct nft_table *table;
+ struct net *net = sock_net(skb->sk);
- afi = nf_tables_afinfo_lookup(nfmsg->nfgen_family, false);
+ afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
if (IS_ERR(afi))
return PTR_ERR(afi);
- table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE], false);
+ table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
return 0;
}
.policy = nft_chain_policy,
},
[NFT_MSG_NEWRULE] = {
- .call = nf_tables_newrule,
+ .call_batch = nf_tables_newrule,
.attr_count = NFTA_RULE_MAX,
.policy = nft_rule_policy,
},
.policy = nft_rule_policy,
},
[NFT_MSG_DELRULE] = {
- .call = nf_tables_delrule,
+ .call_batch = nf_tables_delrule,
.attr_count = NFTA_RULE_MAX,
.policy = nft_rule_policy,
},
.subsys_id = NFNL_SUBSYS_NFTABLES,
.cb_count = NFT_MSG_MAX,
.cb = nf_tables_cb,
+ .commit = nf_tables_commit,
+ .abort = nf_tables_abort,
};
/*
{
const struct nft_rule *rule;
const struct nft_expr *expr, *last;
- const struct nft_data *data;
const struct nft_set *set;
struct nft_set_binding *binding;
struct nft_set_iter iter;
- int err;
if (ctx->chain == chain)
return -ELOOP;
list_for_each_entry(rule, &chain->rules, list) {
nft_rule_for_each_expr(expr, last, rule) {
- if (!expr->ops->get_verdict)
+ const struct nft_data *data = NULL;
+ int err;
+
+ if (!expr->ops->validate)
continue;
- data = expr->ops->get_verdict(expr);
+ err = expr->ops->validate(ctx, expr, &data);
+ if (err < 0)
+ return err;
+
if (data == NULL)
- break;
+ continue;
switch (data->verdict) {
case NFT_JUMP:
}
EXPORT_SYMBOL_GPL(nft_data_dump);
+static int nf_tables_init_net(struct net *net)
+{
+ INIT_LIST_HEAD(&net->nft.af_info);
+ INIT_LIST_HEAD(&net->nft.commit_list);
+ return 0;
+}
+
+static struct pernet_operations nf_tables_net_ops = {
+ .init = nf_tables_init_net,
+};
+
static int __init nf_tables_module_init(void)
{
int err;
goto err3;
pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
- return 0;
+ return register_pernet_subsys(&nf_tables_net_ops);
err3:
nf_tables_core_module_exit();
err2:
static void __exit nf_tables_module_exit(void)
{
+ unregister_pernet_subsys(&nf_tables_net_ops);
nfnetlink_subsys_unregister(&nf_tables_subsys);
nf_tables_core_module_exit();
kfree(info);