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

netfilter: ip_tables: pass table pointer via nf_hook_ops



iptable_x modules rely on 'struct net' to contain a pointer to the
table that should be evaluated.

In order to remove these pointers from struct net, pass them via
the 'priv' pointer in a similar fashion as nf_tables passes the
rule data.

To do that, duplicate the nf_hook_info array passed in from the
iptable_x modules, update the ops->priv pointers of the copy to
refer to the table and then change the hookfn implementations to
just pass the 'priv' argument to the traverser.

After this patch, the xt_table pointers can already be removed
from struct net.

However, changes to struct net result in re-compile of the entire
network stack, so do the removal after arptables and ip6tables
have been converted as well.

Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent a4aeafa2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -229,6 +229,9 @@ struct xt_table {
	/* Man behind the curtain... */
	struct xt_table_info *private;

	/* hook ops that register the table with the netfilter core */
	struct nf_hook_ops *ops;

	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
	struct module *me;

+2 −4
Original line number Diff line number Diff line
@@ -24,11 +24,9 @@

int ipt_register_table(struct net *net, const struct xt_table *table,
		       const struct ipt_replace *repl,
		       const struct nf_hook_ops *ops, struct xt_table **res);

void ipt_unregister_table_pre_exit(struct net *net, const char *name,
		       const struct nf_hook_ops *ops);

void ipt_unregister_table_pre_exit(struct net *net, const char *name);
void ipt_unregister_table_exit(struct net *net, const char *name);

/* Standard entry. */
+36 −17
Original line number Diff line number Diff line
@@ -1716,9 +1716,11 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table)

int ipt_register_table(struct net *net, const struct xt_table *table,
		       const struct ipt_replace *repl,
		       const struct nf_hook_ops *ops, struct xt_table **res)
		       const struct nf_hook_ops *template_ops)
{
	int ret;
	struct nf_hook_ops *ops;
	unsigned int num_ops;
	int ret, i;
	struct xt_table_info *newinfo;
	struct xt_table_info bootstrap = {0};
	void *loc_cpu_entry;
@@ -1732,40 +1734,57 @@ int ipt_register_table(struct net *net, const struct xt_table *table,
	memcpy(loc_cpu_entry, repl->entries, repl->size);

	ret = translate_table(net, newinfo, loc_cpu_entry, repl);
	if (ret != 0)
		goto out_free;
	if (ret != 0) {
		xt_free_table_info(newinfo);
		return ret;
	}

	new_table = xt_register_table(net, table, &bootstrap, newinfo);
	if (IS_ERR(new_table)) {
		ret = PTR_ERR(new_table);
		goto out_free;
		xt_free_table_info(newinfo);
		return PTR_ERR(new_table);
	}

	/* set res now, will see skbs right after nf_register_net_hooks */
	WRITE_ONCE(*res, new_table);
	if (!ops)
	/* No template? No need to do anything. This is used by 'nat' table, it registers
	 * with the nat core instead of the netfilter core.
	 */
	if (!template_ops)
		return 0;

	ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
	if (ret != 0) {
		__ipt_unregister_table(net, new_table);
		*res = NULL;
	num_ops = hweight32(table->valid_hooks);
	if (num_ops == 0) {
		ret = -EINVAL;
		goto out_free;
	}

	ops = kmemdup(template_ops, sizeof(*ops) * num_ops, GFP_KERNEL);
	if (!ops) {
		ret = -ENOMEM;
		goto out_free;
	}

	for (i = 0; i < num_ops; i++)
		ops[i].priv = new_table;

	new_table->ops = ops;

	ret = nf_register_net_hooks(net, ops, num_ops);
	if (ret != 0)
		goto out_free;

	return ret;

out_free:
	xt_free_table_info(newinfo);
	__ipt_unregister_table(net, new_table);
	return ret;
}

void ipt_unregister_table_pre_exit(struct net *net, const char *name,
				   const struct nf_hook_ops *ops)
void ipt_unregister_table_pre_exit(struct net *net, const char *name)
{
	struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name);

	if (table)
		nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
		nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks));
}

void ipt_unregister_table_exit(struct net *net, const char *name)
+3 −5
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ static unsigned int
iptable_filter_hook(void *priv, struct sk_buff *skb,
		    const struct nf_hook_state *state)
{
	return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
	return ipt_do_table(skb, state, priv);
}

static struct nf_hook_ops *filter_ops __read_mostly;
@@ -55,8 +55,7 @@ static int __net_init iptable_filter_table_init(struct net *net)
	((struct ipt_standard *)repl->entries)[1].target.verdict =
		forward ? -NF_ACCEPT - 1 : -NF_DROP - 1;

	err = ipt_register_table(net, &packet_filter, repl, filter_ops,
				 &net->ipv4.iptable_filter);
	err = ipt_register_table(net, &packet_filter, repl, filter_ops);
	kfree(repl);
	return err;
}
@@ -71,13 +70,12 @@ static int __net_init iptable_filter_net_init(struct net *net)

static void __net_exit iptable_filter_net_pre_exit(struct net *net)
{
	ipt_unregister_table_pre_exit(net, "filter", filter_ops);
	ipt_unregister_table_pre_exit(net, "filter");
}

static void __net_exit iptable_filter_net_exit(struct net *net)
{
	ipt_unregister_table_exit(net, "filter");
	net->ipv4.iptable_filter = NULL;
}

static struct pernet_operations iptable_filter_net_ops = {
+6 −8
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static const struct xt_table packet_mangler = {
};

static unsigned int
ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state, void *priv)
{
	unsigned int ret;
	const struct iphdr *iph;
@@ -53,7 +53,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
	daddr = iph->daddr;
	tos = iph->tos;

	ret = ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
	ret = ipt_do_table(skb, state, priv);
	/* Reroute for ANY change. */
	if (ret != NF_DROP && ret != NF_STOLEN) {
		iph = ip_hdr(skb);
@@ -78,8 +78,8 @@ iptable_mangle_hook(void *priv,
		     const struct nf_hook_state *state)
{
	if (state->hook == NF_INET_LOCAL_OUT)
		return ipt_mangle_out(skb, state);
	return ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
		return ipt_mangle_out(skb, state, priv);
	return ipt_do_table(skb, state, priv);
}

static struct nf_hook_ops *mangle_ops __read_mostly;
@@ -91,21 +91,19 @@ static int __net_init iptable_mangle_table_init(struct net *net)
	repl = ipt_alloc_initial_table(&packet_mangler);
	if (repl == NULL)
		return -ENOMEM;
	ret = ipt_register_table(net, &packet_mangler, repl, mangle_ops,
				 &net->ipv4.iptable_mangle);
	ret = ipt_register_table(net, &packet_mangler, repl, mangle_ops);
	kfree(repl);
	return ret;
}

static void __net_exit iptable_mangle_net_pre_exit(struct net *net)
{
	ipt_unregister_table_pre_exit(net, "mangle", mangle_ops);
	ipt_unregister_table_pre_exit(net, "mangle");
}

static void __net_exit iptable_mangle_net_exit(struct net *net)
{
	ipt_unregister_table_exit(net, "mangle");
	net->ipv4.iptable_mangle = NULL;
}

static struct pernet_operations iptable_mangle_net_ops = {
Loading