netfilter: ip[6]t_REJECT: tcp-reset using wrong MAC source if bridged
authorPhil Oester <kernel@linuxace.com>
Wed, 26 Jun 2013 21:16:28 +0000 (17:16 -0400)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 27 Aug 2013 22:13:12 +0000 (00:13 +0200)
As reported by Casper Gripenberg, in a bridged setup, using ip[6]t_REJECT
with the tcp-reset option sends out reset packets with the src MAC address
of the local bridge interface, instead of the MAC address of the intended
destination.  This causes some routers/firewalls to drop the reset packet
as it appears to be spoofed.  Fix this by bypassing ip[6]_local_out and
setting the MAC of the sender in the tcp reset packet.

This closes netfilter bugzilla #531.

Signed-off-by: Phil Oester <kernel@linuxace.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter/ipt_REJECT.c
net/ipv6/netfilter/ip6t_REJECT.c

index 04b18c1ac3458503a2b5d79bd7fd1547c13e76d7..b969131ad1c198c6983b07fb1903abd2498f1374 100644 (file)
@@ -119,7 +119,26 @@ static void send_reset(struct sk_buff *oldskb, int hook)
 
        nf_ct_attach(nskb, oldskb);
 
-       ip_local_out(nskb);
+#ifdef CONFIG_BRIDGE_NETFILTER
+       /* If we use ip_local_out for bridged traffic, the MAC source on
+        * the RST will be ours, instead of the destination's.  This confuses
+        * some routers/firewalls, and they drop the packet.  So we need to
+        * build the eth header using the original destination's MAC as the
+        * source, and send the RST packet directly.
+        */
+       if (oldskb->nf_bridge) {
+               struct ethhdr *oeth = eth_hdr(oldskb);
+               nskb->dev = oldskb->nf_bridge->physindev;
+               niph->tot_len = htons(nskb->len);
+               ip_send_check(niph);
+               if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
+                                   oeth->h_source, oeth->h_dest, nskb->len) < 0)
+                       goto free_nskb;
+               dev_queue_xmit(nskb);
+       } else
+#endif
+               ip_local_out(nskb);
+
        return;
 
  free_nskb:
index 70f9abc0efe9a0ba0bc8fdbb6060f3bfe593291f..56eef30ee5f6afb2ad1338649b30ffe8a5c5b948 100644 (file)
@@ -169,7 +169,25 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
 
        nf_ct_attach(nskb, oldskb);
 
-       ip6_local_out(nskb);
+#ifdef CONFIG_BRIDGE_NETFILTER
+       /* If we use ip6_local_out for bridged traffic, the MAC source on
+        * the RST will be ours, instead of the destination's.  This confuses
+        * some routers/firewalls, and they drop the packet.  So we need to
+        * build the eth header using the original destination's MAC as the
+        * source, and send the RST packet directly.
+        */
+       if (oldskb->nf_bridge) {
+               struct ethhdr *oeth = eth_hdr(oldskb);
+               nskb->dev = oldskb->nf_bridge->physindev;
+               nskb->protocol = htons(ETH_P_IPV6);
+               ip6h->payload_len = htons(sizeof(struct tcphdr));
+               if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
+                                   oeth->h_source, oeth->h_dest, nskb->len) < 0)
+                       return;
+               dev_queue_xmit(nskb);
+       } else
+#endif
+               ip6_local_out(nskb);
 }
 
 static inline void