| From foo@baz Wed Sep 26 11:28:02 CEST 2018 |
| From: Toke Høiland-Jørgensen <toke@toke.dk> |
| Date: Thu, 13 Sep 2018 16:43:07 +0200 |
| Subject: gso_segment: Reset skb->mac_len after modifying network header |
| |
| From: Toke Høiland-Jørgensen <toke@toke.dk> |
| |
| [ Upstream commit c56cae23c6b167acc68043c683c4573b80cbcc2c ] |
| |
| When splitting a GSO segment that consists of encapsulated packets, the |
| skb->mac_len of the segments can end up being set wrong, causing packet |
| drops in particular when using act_mirred and ifb interfaces in |
| combination with a qdisc that splits GSO packets. |
| |
| This happens because at the time skb_segment() is called, network_header |
| will point to the inner header, throwing off the calculation in |
| skb_reset_mac_len(). The network_header is subsequently adjust by the |
| outer IP gso_segment handlers, but they don't set the mac_len. |
| |
| Fix this by adding skb_reset_mac_len() calls to both the IPv4 and IPv6 |
| gso_segment handlers, after they modify the network_header. |
| |
| Many thanks to Eric Dumazet for his help in identifying the cause of |
| the bug. |
| |
| Acked-by: Dave Taht <dave.taht@gmail.com> |
| Reviewed-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/af_inet.c | 1 + |
| net/ipv6/ip6_offload.c | 1 + |
| 2 files changed, 2 insertions(+) |
| |
| --- a/net/ipv4/af_inet.c |
| +++ b/net/ipv4/af_inet.c |
| @@ -1307,6 +1307,7 @@ struct sk_buff *inet_gso_segment(struct |
| if (encap) |
| skb_reset_inner_headers(skb); |
| skb->network_header = (u8 *)iph - skb->head; |
| + skb_reset_mac_len(skb); |
| } while ((skb = skb->next)); |
| |
| out: |
| --- a/net/ipv6/ip6_offload.c |
| +++ b/net/ipv6/ip6_offload.c |
| @@ -113,6 +113,7 @@ static struct sk_buff *ipv6_gso_segment( |
| payload_len = skb->len - nhoff - sizeof(*ipv6h); |
| ipv6h->payload_len = htons(payload_len); |
| skb->network_header = (u8 *)ipv6h - skb->head; |
| + skb_reset_mac_len(skb); |
| |
| if (udpfrag) { |
| int err = ip6_find_1stfragopt(skb, &prevhdr); |