mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
35fdb94b45
commit
affe759dba
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue