netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Unable to DNAT packets back into originating bridge port
@ 2014-06-27 17:48 Matthijs Kooijman
  2014-06-27 18:52 ` Florian Westphal
  0 siblings, 1 reply; 3+ messages in thread
From: Matthijs Kooijman @ 2014-06-27 17:48 UTC (permalink / raw)
  To: Stephen Hemminger, Bart De Schuymer; +Cc: bridge, netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 4585 bytes --]

Hey folks,

I recently stumbled upon an issue in my iptables setup. After some
extensive debugging, I've found that the problem occurs when trying to
DNAT (+SNAT) a packet that comes in through a bridge, back into the same bridge
port it originated from.

The code ultimately responsible for this is the should_deliver function
[1], which prevents packets from being delivered back to their
originating port (ultimately to prevent bouncing broadcast message, I
believe).

[1]: https://github.com/torvalds/linux/blob/v3.14/net/bridge/br_forward.c#L30-L36

Another requirement for this issue to occur is the
bridge-nf-call-iptables settings, which must be at the default 1
setting. Without that, the packets are passed up through
br_pass_frame_up normally.

Some more details about my setup:

matthijs@grubby:~$ sudo brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.5cff350f105e       no              eth0

matthijs@grubby:~$ sudo ifconfig br0|grep inet
          inet addr:192.168.1.175  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::5eff:35ff:fe0f:105e/64 Scope:Link

matthijs@grubby:~$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             anywhere             tcp dpt:81 to:192.168.1.252

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
SNAT       tcp  --  anywhere             anywhere             tcp dpt:81 to:192.168.1.175

When I now create a connection from a host on eth0, to 192.167.1.175:81, that
packet gets dropped instead of DNATed and SNATed back through eth0 to
192.168.1.184. Enable hairpin mode on eth0, or disabling
bridge-nf-call-iptables makes it work as expected.

Now, is this a bug in the kernel? Or should I not be expecting this setup to work?

While debugging, I reviewed the code to trace the path the packet takes
through the code, and this is what I found (this just from review, I
haven't verified with debug output):

 - The packet comes in br_handle_frame
 - The frame gets dumped into the NF_BR_PRE_ROUTING netfilter chain
   (e.g. the bridge / ebtables version, not the ip / iptables one).
 - The ebtables rules get called
 - The br_nf_pre_routing hook for NF_BR_PRE_ROUTING gets called. This
   interrupts (returns NF_STOLEN) the handling of the NF_BR_PRE_ROUTING
   chain, and calls the NF_INET_PRE_ROUTING chain.
 - The br_nf_pre_routing_finish finish handler gets called after
   completing the NF_INET_PRE_ROUTING chain.
 - This handler resumes the handling of the interrupted
   NF_BR_PRE_ROUTING chain. However, because it detects that DNAT has
   happened, it sets the finish handler to
   br_nf_pre_routing_finish_bridge instead of the regular
   br_handle_frame_finish finish handler.
 - br_nf_pre_routing_finish_bridge runs, this skb->dev to the parent
   bridge and sets the BRNF_BRIDGED_DNAT flag which calls
   neigh->output(neigh, skb); which presumably resolves to one of the
   neigh_*output functions, each of which again calls dev_queue_xmit,
   which should (eventually) call br_dev_xmit.
 - br_dev_xmit sees the BRNF_BRIDGED_DNAT flag and calls
   br_nf_pre_routing_finish_bridge_slow instead of actually delivering
   the packet.
 - br_nf_pre_routing_finish_bridge_slow sets up the destination MAC
   address, sets skb->dev back to skb->physindev and calls
   br_handle_frame_finish.
 - br_handle_frame_finish calls br_forward.
 - br_forward calls should_deliver, which returns false when skb->dev !=
   p->dev (and "hairpin mode" is not enabled) causing the packet to be
   dropped.

Some things to note:
 - Why does the packet get redirected to NF_INET_PRE_ROUTING in
   br_nf_pre_routing already? Is it important that it happens halfway
   through the NF_BR_PRE_ROUTING chain? If not, why not do it in
   br_handle_frame_finish / br_forward when it has actually been
   established that the packet will be bridged and not routed?
 - Should should_deliver make an exception for DNAT'ed packets? Or
   perhaps only block broadcasts.

Also see this blogpost for a bit more details about my original setup
and debugging process:
http://www.stderr.nl/Blog/Software/Linux/BouncingPacketsKernelBug.html

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Unable to DNAT packets back into originating bridge port
  2014-06-27 17:48 Unable to DNAT packets back into originating bridge port Matthijs Kooijman
@ 2014-06-27 18:52 ` Florian Westphal
  2014-06-27 20:00   ` Matthijs Kooijman
  0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2014-06-27 18:52 UTC (permalink / raw)
  To: Matthijs Kooijman, Stephen Hemminger, Bart De Schuymer, bridge,
	netfilter-devel

Matthijs Kooijman <matthijs@stdin.nl> wrote:
> I recently stumbled upon an issue in my iptables setup. After some
> extensive debugging, I've found that the problem occurs when trying to
> DNAT (+SNAT) a packet that comes in through a bridge, back into the same bridge
> port it originated from.
> 
> The code ultimately responsible for this is the should_deliver function
> [1], which prevents packets from being delivered back to their
> originating port (ultimately to prevent bouncing broadcast message, I
> believe).

Sounds like
http://marc.info/?t=136627796900001&r=1&w=2

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Unable to DNAT packets back into originating bridge port
  2014-06-27 18:52 ` Florian Westphal
@ 2014-06-27 20:00   ` Matthijs Kooijman
  0 siblings, 0 replies; 3+ messages in thread
From: Matthijs Kooijman @ 2014-06-27 20:00 UTC (permalink / raw)
  To: Florian Westphal
  Cc: Stephen Hemminger, Bart De Schuymer, bridge, netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 286 bytes --]

Hey Florian,

> Sounds like
> http://marc.info/?t=136627796900001&r=1&w=2
Yup, I'm pretty sure this is the same issue. I hadn't realize the need
for source mac address mangling yet. It didn't show up as a problem
in my setup, but I guess it can be needed in some setups.

Gr.

Matthijs

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-06-27 20:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-06-27 17:48 Unable to DNAT packets back into originating bridge port Matthijs Kooijman
2014-06-27 18:52 ` Florian Westphal
2014-06-27 20:00   ` Matthijs Kooijman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).