Commit 4035285f authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

netfilter: nf_tables: add nf_tables_addchain()



Wrap the chain addition path in a function to make it more maintainable.

Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 2c4a488a
Loading
Loading
Loading
Loading
+106 −93
Original line number Diff line number Diff line
@@ -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,