diff options
author | Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> | 2015-07-28 06:05:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-07-29 15:20:16 -0400 |
commit | df356d5e81b04dd51dd9f23f2bce7d73dd929899 (patch) | |
tree | 4650281cf54709b5a679f3cfdee075d4371dad91 | |
parent | dbd46ab412b8fb395f2b0ff6f6a7eec9df311550 (diff) |
bridge: Fix network header pointer for vlan tagged packets
There are several devices that can receive vlan tagged packets with
CHECKSUM_PARTIAL like tap, possibly veth and xennet.
When (multiple) vlan tagged packets with CHECKSUM_PARTIAL are forwarded
by bridge to a device with the IP_CSUM feature, they end up with checksum
error because before entering bridge, the network header is set to
ETH_HLEN (not including vlan header length) in __netif_receive_skb_core(),
get_rps_cpu(), or drivers' rx functions, and nobody fixes the pointer later.
Since the network header is exepected to be ETH_HLEN in flow-dissection
and hash-calculation in RPS in rx path, and since the header pointer fix
is needed only in tx path, set the appropriate network header on forwarding
packets.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/bridge/br_forward.c | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 0ff6e1bbca91..fa7bfced888e 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c | |||
@@ -37,15 +37,30 @@ static inline int should_deliver(const struct net_bridge_port *p, | |||
37 | 37 | ||
38 | int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb) | 38 | int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb) |
39 | { | 39 | { |
40 | if (!is_skb_forwardable(skb->dev, skb)) { | 40 | if (!is_skb_forwardable(skb->dev, skb)) |
41 | kfree_skb(skb); | 41 | goto drop; |
42 | } else { | 42 | |
43 | skb_push(skb, ETH_HLEN); | 43 | skb_push(skb, ETH_HLEN); |
44 | br_drop_fake_rtable(skb); | 44 | br_drop_fake_rtable(skb); |
45 | skb_sender_cpu_clear(skb); | 45 | skb_sender_cpu_clear(skb); |
46 | dev_queue_xmit(skb); | 46 | |
47 | if (skb->ip_summed == CHECKSUM_PARTIAL && | ||
48 | (skb->protocol == htons(ETH_P_8021Q) || | ||
49 | skb->protocol == htons(ETH_P_8021AD))) { | ||
50 | int depth; | ||
51 | |||
52 | if (!__vlan_get_protocol(skb, skb->protocol, &depth)) | ||
53 | goto drop; | ||
54 | |||
55 | skb_set_network_header(skb, depth); | ||
47 | } | 56 | } |
48 | 57 | ||
58 | dev_queue_xmit(skb); | ||
59 | |||
60 | return 0; | ||
61 | |||
62 | drop: | ||
63 | kfree_skb(skb); | ||
49 | return 0; | 64 | return 0; |
50 | } | 65 | } |
51 | EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); | 66 | EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); |