| From foo@baz Sat Mar 18 22:03:53 CST 2017 |
| From: Paolo Abeni <pabeni@redhat.com> |
| Date: Tue, 7 Mar 2017 18:33:31 +0100 |
| Subject: net/tunnel: set inner protocol in network gro hooks |
| |
| From: Paolo Abeni <pabeni@redhat.com> |
| |
| |
| [ Upstream commit 294acf1c01bace5cea5d30b510504238bf5f7c25 ] |
| |
| The gso code of several tunnels type (gre and udp tunnels) |
| takes for granted that the skb->inner_protocol is properly |
| initialized and drops the packet elsewhere. |
| |
| On the forwarding path no one is initializing such field, |
| so gro encapsulated packets are dropped on forward. |
| |
| Since commit 38720352412a ("gre: Use inner_proto to obtain |
| inner header protocol"), this can be reproduced when the |
| encapsulated packets use gre as the tunneling protocol. |
| |
| The issue happens also with vxlan and geneve tunnels since |
| commit 8bce6d7d0d1e ("udp: Generalize skb_udp_segment"), if the |
| forwarding host's ingress nic has h/w offload for such tunnel |
| and a vxlan/geneve device is configured on top of it, regardless |
| of the configured peer address and vni. |
| |
| To address the issue, this change initialize the inner_protocol |
| field for encapsulated packets in both ipv4 and ipv6 gro complete |
| callbacks. |
| |
| Fixes: 38720352412a ("gre: Use inner_proto to obtain inner header protocol") |
| Fixes: 8bce6d7d0d1e ("udp: Generalize skb_udp_segment") |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Acked-by: Alexander Duyck <alexander.h.duyck@intel.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/af_inet.c | 4 +++- |
| net/ipv6/ip6_offload.c | 4 +++- |
| 2 files changed, 6 insertions(+), 2 deletions(-) |
| |
| --- a/net/ipv4/af_inet.c |
| +++ b/net/ipv4/af_inet.c |
| @@ -1470,8 +1470,10 @@ int inet_gro_complete(struct sk_buff *sk |
| int proto = iph->protocol; |
| int err = -ENOSYS; |
| |
| - if (skb->encapsulation) |
| + if (skb->encapsulation) { |
| + skb_set_inner_protocol(skb, cpu_to_be16(ETH_P_IP)); |
| skb_set_inner_network_header(skb, nhoff); |
| + } |
| |
| csum_replace2(&iph->check, iph->tot_len, newlen); |
| iph->tot_len = newlen; |
| --- a/net/ipv6/ip6_offload.c |
| +++ b/net/ipv6/ip6_offload.c |
| @@ -294,8 +294,10 @@ static int ipv6_gro_complete(struct sk_b |
| struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff); |
| int err = -ENOSYS; |
| |
| - if (skb->encapsulation) |
| + if (skb->encapsulation) { |
| + skb_set_inner_protocol(skb, cpu_to_be16(ETH_P_IPV6)); |
| skb_set_inner_network_header(skb, nhoff); |
| + } |
| |
| iph->payload_len = htons(skb->len - nhoff - sizeof(*iph)); |
| |