|
|
@@ -1335,6 +1335,106 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook)
|
|
|
dev_put(hook->dev);
|
|
|
}
|
|
|
|
|
|
+static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
|
|
+ u8 policy, bool create)
|
|
|
+{
|
|
|
+ const struct nlattr * const *nla = ctx->nla;
|
|
|
+ struct nft_table *table = ctx->table;
|
|
|
+ struct nft_af_info *afi = ctx->afi;
|
|
|
+ struct nft_base_chain *basechain;
|
|
|
+ struct nft_stats __percpu *stats;
|
|
|
+ struct net *net = ctx->net;
|
|
|
+ struct nft_chain *chain;
|
|
|
+ unsigned int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (table->use == UINT_MAX)
|
|
|
+ return -EOVERFLOW;
|
|
|
+
|
|
|
+ if (nla[NFTA_CHAIN_HOOK]) {
|
|
|
+ struct nft_chain_hook hook;
|
|
|
+ struct nf_hook_ops *ops;
|
|
|
+ nf_hookfn *hookfn;
|
|
|
+
|
|
|
+ err = nft_chain_parse_hook(net, nla, afi, &hook, create);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
|
|
|
+ if (basechain == NULL) {
|
|
|
+ nft_chain_release_hook(&hook);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hook.dev != NULL)
|
|
|
+ strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ);
|
|
|
+
|
|
|
+ if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
|
+ stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
|
|
|
+ if (IS_ERR(stats)) {
|
|
|
+ nft_chain_release_hook(&hook);
|
|
|
+ kfree(basechain);
|
|
|
+ return PTR_ERR(stats);
|
|
|
+ }
|
|
|
+ basechain->stats = stats;
|
|
|
+ static_branch_inc(&nft_counters_enabled);
|
|
|
+ }
|
|
|
+
|
|
|
+ hookfn = hook.type->hooks[hook.num];
|
|
|
+ basechain->type = hook.type;
|
|
|
+ chain = &basechain->chain;
|
|
|
+
|
|
|
+ for (i = 0; i < afi->nops; i++) {
|
|
|
+ ops = &basechain->ops[i];
|
|
|
+ ops->pf = family;
|
|
|
+ ops->hooknum = hook.num;
|
|
|
+ ops->priority = hook.priority;
|
|
|
+ ops->priv = chain;
|
|
|
+ ops->hook = afi->hooks[ops->hooknum];
|
|
|
+ ops->dev = hook.dev;
|
|
|
+ if (hookfn)
|
|
|
+ ops->hook = hookfn;
|
|
|
+ if (afi->hook_ops_init)
|
|
|
+ afi->hook_ops_init(ops, i);
|
|
|
+ }
|
|
|
+
|
|
|
+ chain->flags |= NFT_BASE_CHAIN;
|
|
|
+ basechain->policy = policy;
|
|
|
+ } else {
|
|
|
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
|
|
+ if (chain == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ INIT_LIST_HEAD(&chain->rules);
|
|
|
+ chain->handle = nf_tables_alloc_handle(table);
|
|
|
+ chain->table = table;
|
|
|
+ chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
|
|
|
+ if (!chain->name) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err1;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = nf_tables_register_hooks(net, table, chain, afi->nops);
|
|
|
+ if (err < 0)
|
|
|
+ goto err1;
|
|
|
+
|
|
|
+ ctx->chain = chain;
|
|
|
+ err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
|
|
|
+ if (err < 0)
|
|
|
+ goto err2;
|
|
|
+
|
|
|
+ table->use++;
|
|
|
+ list_add_tail_rcu(&chain->list, &table->chains);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+err2:
|
|
|
+ nf_tables_unregister_hooks(net, table, chain, afi->nops);
|
|
|
+err1:
|
|
|
+ nf_tables_chain_destroy(chain);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
|
|
bool create)
|
|
|
{
|
|
|
@@ -1433,19 +1533,15 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
|
|
|
{
|
|
|
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
|
|
|
const struct nlattr * uninitialized_var(name);
|
|
|
+ u8 genmask = nft_genmask_next(net);
|
|
|
+ int family = nfmsg->nfgen_family;
|
|
|
struct nft_af_info *afi;
|
|
|
struct nft_table *table;
|
|
|
struct nft_chain *chain;
|
|
|
- struct nft_base_chain *basechain = NULL;
|
|
|
- u8 genmask = nft_genmask_next(net);
|
|
|
- int family = nfmsg->nfgen_family;
|
|
|
u8 policy = NF_ACCEPT;
|
|
|
+ struct nft_ctx ctx;
|
|
|
u64 handle = 0;
|
|
|
- unsigned int i;
|
|
|
- struct nft_stats __percpu *stats;
|
|
|
- int err;
|
|
|
bool create;
|
|
|
- struct nft_ctx ctx;
|
|
|
|
|
|
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
|
|
|
|
|
|
@@ -1493,101 +1589,18 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
|
|
|
+
|
|
|
if (chain != NULL) {
|
|
|
if (nlh->nlmsg_flags & NLM_F_EXCL)
|
|
|
return -EEXIST;
|
|
|
if (nlh->nlmsg_flags & NLM_F_REPLACE)
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
- nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
|
|
|
-
|
|
|
return nf_tables_updchain(&ctx, genmask, policy, create);
|
|
|
}
|
|
|
|
|
|
- if (table->use == UINT_MAX)
|
|
|
- return -EOVERFLOW;
|
|
|
-
|
|
|
- if (nla[NFTA_CHAIN_HOOK]) {
|
|
|
- struct nft_chain_hook hook;
|
|
|
- struct nf_hook_ops *ops;
|
|
|
- nf_hookfn *hookfn;
|
|
|
-
|
|
|
- err = nft_chain_parse_hook(net, nla, afi, &hook, create);
|
|
|
- if (err < 0)
|
|
|
- return err;
|
|
|
-
|
|
|
- basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
|
|
|
- if (basechain == NULL) {
|
|
|
- nft_chain_release_hook(&hook);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- if (hook.dev != NULL)
|
|
|
- strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ);
|
|
|
-
|
|
|
- if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
|
- stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
|
|
|
- if (IS_ERR(stats)) {
|
|
|
- nft_chain_release_hook(&hook);
|
|
|
- kfree(basechain);
|
|
|
- return PTR_ERR(stats);
|
|
|
- }
|
|
|
- basechain->stats = stats;
|
|
|
- static_branch_inc(&nft_counters_enabled);
|
|
|
- }
|
|
|
-
|
|
|
- hookfn = hook.type->hooks[hook.num];
|
|
|
- basechain->type = hook.type;
|
|
|
- chain = &basechain->chain;
|
|
|
-
|
|
|
- for (i = 0; i < afi->nops; i++) {
|
|
|
- ops = &basechain->ops[i];
|
|
|
- ops->pf = family;
|
|
|
- ops->hooknum = hook.num;
|
|
|
- ops->priority = hook.priority;
|
|
|
- ops->priv = chain;
|
|
|
- ops->hook = afi->hooks[ops->hooknum];
|
|
|
- ops->dev = hook.dev;
|
|
|
- if (hookfn)
|
|
|
- ops->hook = hookfn;
|
|
|
- if (afi->hook_ops_init)
|
|
|
- afi->hook_ops_init(ops, i);
|
|
|
- }
|
|
|
-
|
|
|
- chain->flags |= NFT_BASE_CHAIN;
|
|
|
- basechain->policy = policy;
|
|
|
- } else {
|
|
|
- chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
|
|
- if (chain == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&chain->rules);
|
|
|
- chain->handle = nf_tables_alloc_handle(table);
|
|
|
- chain->table = table;
|
|
|
- chain->name = nla_strdup(name, GFP_KERNEL);
|
|
|
- if (!chain->name) {
|
|
|
- err = -ENOMEM;
|
|
|
- goto err1;
|
|
|
- }
|
|
|
-
|
|
|
- err = nf_tables_register_hooks(net, table, chain, afi->nops);
|
|
|
- if (err < 0)
|
|
|
- goto err1;
|
|
|
-
|
|
|
- nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
|
|
|
- err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
|
|
|
- if (err < 0)
|
|
|
- goto err2;
|
|
|
-
|
|
|
- table->use++;
|
|
|
- list_add_tail_rcu(&chain->list, &table->chains);
|
|
|
- return 0;
|
|
|
-err2:
|
|
|
- nf_tables_unregister_hooks(net, table, chain, afi->nops);
|
|
|
-err1:
|
|
|
- nf_tables_chain_destroy(chain);
|
|
|
- return err;
|
|
|
+ return nf_tables_addchain(&ctx, family, genmask, policy, create);
|
|
|
}
|
|
|
|
|
|
static int nf_tables_delchain(struct net *net, struct sock *nlsk,
|