| From: Herbert Xu <herbert@gondor.apana.org.au> |
| Date: Tue, 10 Jan 2017 12:24:15 -0800 |
| Subject: gro: Disable frag0 optimization on IPv6 ext headers |
| |
| commit 57ea52a865144aedbcd619ee0081155e658b6f7d upstream. |
| |
| The GRO fast path caches the frag0 address. This address becomes |
| invalid if frag0 is modified by pskb_may_pull or its variants. |
| So whenever that happens we must disable the frag0 optimization. |
| |
| This is usually done through the combination of gro_header_hard |
| and gro_header_slow, however, the IPv6 extension header path did |
| the pulling directly and would continue to use the GRO fast path |
| incorrectly. |
| |
| This patch fixes it by disabling the fast path when we enter the |
| IPv6 extension header path. |
| |
| Fixes: 78a478d0efd9 ("gro: Inline skb_gro_header and cache frag0 virtual address") |
| Reported-by: Slava Shwartsman <slavash@mellanox.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| [bwh: Backported to 3.2: adjust filename] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| include/linux/netdevice.h | 9 +++++++-- |
| net/ipv6/af_inet6.c | 1 + |
| 2 files changed, 8 insertions(+), 2 deletions(-) |
| |
| --- a/include/linux/netdevice.h |
| +++ b/include/linux/netdevice.h |
| @@ -1676,14 +1676,19 @@ static inline int skb_gro_header_hard(st |
| return NAPI_GRO_CB(skb)->frag0_len < hlen; |
| } |
| |
| +static inline void skb_gro_frag0_invalidate(struct sk_buff *skb) |
| +{ |
| + NAPI_GRO_CB(skb)->frag0 = NULL; |
| + NAPI_GRO_CB(skb)->frag0_len = 0; |
| +} |
| + |
| static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen, |
| unsigned int offset) |
| { |
| if (!pskb_may_pull(skb, hlen)) |
| return NULL; |
| |
| - NAPI_GRO_CB(skb)->frag0 = NULL; |
| - NAPI_GRO_CB(skb)->frag0_len = 0; |
| + skb_gro_frag0_invalidate(skb); |
| return skb->data + offset; |
| } |
| |
| --- a/net/ipv6/af_inet6.c |
| +++ b/net/ipv6/af_inet6.c |
| @@ -879,6 +879,7 @@ static struct sk_buff **ipv6_gro_receive |
| ops = rcu_dereference(inet6_protos[proto]); |
| if (!ops || !ops->gro_receive) { |
| __pskb_pull(skb, skb_gro_offset(skb)); |
| + skb_gro_frag0_invalidate(skb); |
| proto = ipv6_gso_pull_exthdrs(skb, proto); |
| skb_gro_pull(skb, -skb_transport_offset(skb)); |
| skb_reset_transport_header(skb); |