Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a37061a6 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

netfilter: lift one-nat-hook-only restriction



This reverts commit f92b40a8
("netfilter: core: only allow one nat hook per hook point"), this
limitation is no longer needed.  The nat core now invokes these
functions and makes sure that hook evaluation stops after a mapping is
created and a null binding is created otherwise.

Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 9971a514
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -67,7 +67,6 @@ struct nf_hook_ops {
	struct net_device	*dev;
	void			*priv;
	u_int8_t		pf;
	bool			nat_hook;
	unsigned int		hooknum;
	/* Hooks are ordered in ascending priority. */
	int			priority;
+0 −5
Original line number Diff line number Diff line
@@ -138,11 +138,6 @@ nf_hook_entries_grow(const struct nf_hook_entries *old,
			continue;
		}

		if (reg->nat_hook && orig_ops[i]->nat_hook) {
			kvfree(new);
			return ERR_PTR(-EBUSY);
		}

		if (inserted || reg->priority > orig_ops[i]->priority) {
			new_ops[nhooks] = (void *)orig_ops[i];
			new->hooks[nhooks] = old->hooks[i];
+2 −64
Original line number Diff line number Diff line
@@ -74,64 +74,12 @@ static void nft_trans_destroy(struct nft_trans *trans)
	kfree(trans);
}

/* removal requests are queued in the commit_list, but not acted upon
 * until after all new rules are in place.
 *
 * Therefore, nf_register_net_hook(net, &nat_hook) runs before pending
 * nf_unregister_net_hook().
 *
 * nf_register_net_hook thus fails if a nat hook is already in place
 * even if the conflicting hook is about to be removed.
 *
 * If collision is detected, search commit_log for DELCHAIN matching
 * the new nat hooknum; if we find one collision is temporary:
 *
 * Either transaction is aborted (new/colliding hook is removed), or
 * transaction is committed (old hook is removed).
 */
static bool nf_tables_allow_nat_conflict(const struct net *net,
					 const struct nf_hook_ops *ops)
{
	const struct nft_trans *trans;
	bool ret = false;

	if (!ops->nat_hook)
		return false;

	list_for_each_entry(trans, &net->nft.commit_list, list) {
		const struct nf_hook_ops *pending_ops;
		const struct nft_chain *pending;

		if (trans->msg_type != NFT_MSG_NEWCHAIN &&
		    trans->msg_type != NFT_MSG_DELCHAIN)
			continue;

		pending = trans->ctx.chain;
		if (!nft_is_base_chain(pending))
			continue;

		pending_ops = &nft_base_chain(pending)->ops;
		if (pending_ops->nat_hook &&
		    pending_ops->pf == ops->pf &&
		    pending_ops->hooknum == ops->hooknum) {
			/* other hook registration already pending? */
			if (trans->msg_type == NFT_MSG_NEWCHAIN)
				return false;

			ret = true;
		}
	}

	return ret;
}

static int nf_tables_register_hook(struct net *net,
				   const struct nft_table *table,
				   struct nft_chain *chain)
{
	const struct nft_base_chain *basechain;
	struct nf_hook_ops *ops;
	int ret;
	const struct nf_hook_ops *ops;

	if (table->flags & NFT_TABLE_F_DORMANT ||
	    !nft_is_base_chain(chain))
@@ -143,14 +91,7 @@ static int nf_tables_register_hook(struct net *net,
	if (basechain->type->ops_register)
		return basechain->type->ops_register(net, ops);

	ret = nf_register_net_hook(net, ops);
	if (ret == -EBUSY && nf_tables_allow_nat_conflict(net, ops)) {
		ops->nat_hook = false;
		ret = nf_register_net_hook(net, ops);
		ops->nat_hook = true;
	}

	return ret;
	return nf_register_net_hook(net, ops);
}

static void nf_tables_unregister_hook(struct net *net,
@@ -1418,9 +1359,6 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
		ops->hook	= hook.type->hooks[ops->hooknum];
		ops->dev	= hook.dev;

		if (basechain->type->type == NFT_CHAIN_T_NAT)
			ops->nat_hook = true;

		chain->flags |= NFT_BASE_CHAIN;
		basechain->policy = policy;
	} else {