Commit 2948d2eb authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[NETFILTER]: bridge: fix double POST_ROUTING invocation

The bridge code incorrectly causes two POST_ROUTING hook invocations
for DNATed packets that end up on the same bridge device. This
happens because packets with a changed destination address are passed
to dst_output() to make them go through the neighbour output function
again to build a new destination MAC address, before they will continue
through the IP hooks simulated by bridge netfilter.

The resulting hook order is:
 PREROUTING	(bridge netfilter)
 POSTROUTING	(dst_output -> ip_output)
 FORWARD	(bridge netfilter)
 POSTROUTING	(bridge netfilter)

The deferred hooks used to abort the first POST_ROUTING invocation,
but since the only thing bridge netfilter actually really wants is
a new MAC address, we can avoid going through the IP stack completely
by simply calling the neighbour output function directly.

Tested, reported and lots of data provided by: Damien Thebault <damien.thebault@gmail.com>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0ff4d77b
...@@ -247,8 +247,9 @@ static void __br_dnat_complain(void) ...@@ -247,8 +247,9 @@ static void __br_dnat_complain(void)
* Let us first consider the case that ip_route_input() succeeds: * Let us first consider the case that ip_route_input() succeeds:
* *
* If skb->dst->dev equals the logical bridge device the packet * If skb->dst->dev equals the logical bridge device the packet
* came in on, we can consider this bridging. We then call * came in on, we can consider this bridging. The packet is passed
* skb->dst->output() which will make the packet enter br_nf_local_out() * through the neighbour output function to build a new destination
* MAC address, which will make the packet enter br_nf_local_out()
* not much later. In that function it is assured that the iptables * not much later. In that function it is assured that the iptables
* FORWARD chain is traversed for the packet. * FORWARD chain is traversed for the packet.
* *
...@@ -285,12 +286,17 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) ...@@ -285,12 +286,17 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
skb->dev = bridge_parent(skb->dev); skb->dev = bridge_parent(skb->dev);
if (!skb->dev) if (skb->dev) {
kfree_skb(skb); struct dst_entry *dst = skb->dst;
else {
nf_bridge_pull_encap_header(skb); nf_bridge_pull_encap_header(skb);
skb->dst->output(skb);
if (dst->hh)
return neigh_hh_output(dst->hh, skb);
else if (dst->neighbour)
return dst->neighbour->output(skb);
} }
kfree_skb(skb);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment