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

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

netfilter: bridge: add and use br_nf_hook_thresh



This replaces the last uses of NF_HOOK_THRESH().
Followup patch will remove it and rename nf_hook_thresh.

The reason is that inet (non-bridge) netfilter no longer invokes the
hooks from hooks, so we do no longer need the thresh value to skip hooks
with a lower priority.

The bridge netfilter however may need to do this. br_nf_hook_thresh is a
wrapper that is supposed to do this, i.e. only call hooks with a
priority that exceeds NF_BR_PRI_BRNF.

It's used only in the recursion cases of br_netfilter.  It invokes
nf_hook_slow while holding an rcu read-side critical section to make a
future cleanup simpler.

Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarAaron Conole <aconole@bytheb.org>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 50f4c7b7
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -15,6 +15,12 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)

void nf_bridge_update_protocol(struct sk_buff *skb);

int br_nf_hook_thresh(unsigned int hook, struct net *net, struct sock *sk,
		      struct sk_buff *skb, struct net_device *indev,
		      struct net_device *outdev,
		      int (*okfn)(struct net *, struct sock *,
				  struct sk_buff *));

static inline struct nf_bridge_info *
nf_bridge_info_get(const struct sk_buff *skb)
{
+51 −9
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h>
#include <linux/in_route.h>
#include <linux/rculist.h>
#include <linux/inetdevice.h>

#include <net/ip.h>
@@ -395,11 +396,10 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
				skb->dev = nf_bridge->physindev;
				nf_bridge_update_protocol(skb);
				nf_bridge_push_encap_header(skb);
				NF_HOOK_THRESH(NFPROTO_BRIDGE,
					       NF_BR_PRE_ROUTING,
					       net, sk, skb, skb->dev, NULL,
					       br_nf_pre_routing_finish_bridge,
					       1);
				br_nf_hook_thresh(NF_BR_PRE_ROUTING,
						  net, sk, skb, skb->dev,
						  NULL,
						  br_nf_pre_routing_finish);
				return 0;
			}
			ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
@@ -417,10 +417,8 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
	skb->dev = nf_bridge->physindev;
	nf_bridge_update_protocol(skb);
	nf_bridge_push_encap_header(skb);
	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, net, sk, skb,
		       skb->dev, NULL,
		       br_handle_frame_finish, 1);

	br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb, skb->dev, NULL,
			  br_handle_frame_finish);
	return 0;
}

@@ -992,6 +990,50 @@ static struct notifier_block brnf_notifier __read_mostly = {
	.notifier_call = brnf_device_event,
};

/* recursively invokes nf_hook_slow (again), skipping already-called
 * hooks (< NF_BR_PRI_BRNF).
 *
 * Called with rcu read lock held.
 */
int br_nf_hook_thresh(unsigned int hook, struct net *net,
		      struct sock *sk, struct sk_buff *skb,
		      struct net_device *indev,
		      struct net_device *outdev,
		      int (*okfn)(struct net *, struct sock *,
				  struct sk_buff *))
{
	struct nf_hook_ops *elem;
	struct nf_hook_state state;
	struct list_head *head;
	int ret;

	head = &net->nf.hooks[NFPROTO_BRIDGE][hook];

	list_for_each_entry_rcu(elem, head, list) {
		struct nf_hook_ops *next;

		next = list_entry_rcu(list_next_rcu(&elem->list),
				      struct nf_hook_ops, list);
		if (next->priority <= NF_BR_PRI_BRNF)
			continue;
	}

	if (&elem->list == head)
		return okfn(net, sk, skb);

	/* We may already have this, but read-locks nest anyway */
	rcu_read_lock();
	nf_hook_state_init(&state, head, hook, NF_BR_PRI_BRNF + 1,
			   NFPROTO_BRIDGE, indev, outdev, sk, net, okfn);

	ret = nf_hook_slow(skb, &state);
	rcu_read_unlock();
	if (ret == 1)
		ret = okfn(net, sk, skb);

	return ret;
}

#ifdef CONFIG_SYSCTL
static
int brnf_sysctl_call_tables(struct ctl_table *ctl, int write,
+5 −7
Original line number Diff line number Diff line
@@ -187,10 +187,9 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
			skb->dev = nf_bridge->physindev;
			nf_bridge_update_protocol(skb);
			nf_bridge_push_encap_header(skb);
			NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
			br_nf_hook_thresh(NF_BR_PRE_ROUTING,
					  net, sk, skb, skb->dev, NULL,
				       br_nf_pre_routing_finish_bridge,
				       1);
					  br_nf_pre_routing_finish_bridge);
			return 0;
		}
		ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
@@ -207,9 +206,8 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
	skb->dev = nf_bridge->physindev;
	nf_bridge_update_protocol(skb);
	nf_bridge_push_encap_header(skb);
	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, net, sk, skb,
		       skb->dev, NULL,
		       br_handle_frame_finish, 1);
	br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb,
			  skb->dev, NULL, br_handle_frame_finish);

	return 0;
}