Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: ip[6]t_REJECT: tcp-reset using wrong MAC source if bridged

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>

authored by

Phil Oester and committed by
Pablo Neira Ayuso
affe759d 35fdb94b

+39 -2
+20 -1
net/ipv4/netfilter/ipt_REJECT.c
··· 119 119 120 120 nf_ct_attach(nskb, oldskb); 121 121 122 - ip_local_out(nskb); 122 + #ifdef CONFIG_BRIDGE_NETFILTER 123 + /* If we use ip_local_out for bridged traffic, the MAC source on 124 + * the RST will be ours, instead of the destination's. This confuses 125 + * some routers/firewalls, and they drop the packet. So we need to 126 + * build the eth header using the original destination's MAC as the 127 + * source, and send the RST packet directly. 128 + */ 129 + if (oldskb->nf_bridge) { 130 + struct ethhdr *oeth = eth_hdr(oldskb); 131 + nskb->dev = oldskb->nf_bridge->physindev; 132 + niph->tot_len = htons(nskb->len); 133 + ip_send_check(niph); 134 + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 135 + oeth->h_source, oeth->h_dest, nskb->len) < 0) 136 + goto free_nskb; 137 + dev_queue_xmit(nskb); 138 + } else 139 + #endif 140 + ip_local_out(nskb); 141 + 123 142 return; 124 143 125 144 free_nskb:
+19 -1
net/ipv6/netfilter/ip6t_REJECT.c
··· 169 169 170 170 nf_ct_attach(nskb, oldskb); 171 171 172 - ip6_local_out(nskb); 172 + #ifdef CONFIG_BRIDGE_NETFILTER 173 + /* If we use ip6_local_out for bridged traffic, the MAC source on 174 + * the RST will be ours, instead of the destination's. This confuses 175 + * some routers/firewalls, and they drop the packet. So we need to 176 + * build the eth header using the original destination's MAC as the 177 + * source, and send the RST packet directly. 178 + */ 179 + if (oldskb->nf_bridge) { 180 + struct ethhdr *oeth = eth_hdr(oldskb); 181 + nskb->dev = oldskb->nf_bridge->physindev; 182 + nskb->protocol = htons(ETH_P_IPV6); 183 + ip6h->payload_len = htons(sizeof(struct tcphdr)); 184 + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 185 + oeth->h_source, oeth->h_dest, nskb->len) < 0) 186 + return; 187 + dev_queue_xmit(nskb); 188 + } else 189 + #endif 190 + ip6_local_out(nskb); 173 191 } 174 192 175 193 static inline void