diff options
Diffstat (limited to 'net/netfilter/nf_tables_api.c')
-rw-r--r-- | net/netfilter/nf_tables_api.c | 199 |
1 files changed, 106 insertions, 93 deletions
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a910544acf59..d95dfafea1e7 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -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, |