]> git.hungrycats.org Git - linux/commitdiff
tunnels: Don't apply GRO to multiple layers of encapsulation.
authorJesse Gross <jesse@kernel.org>
Sat, 19 Mar 2016 16:32:01 +0000 (09:32 -0700)
committerSasha Levin <alexander.levin@verizon.com>
Sun, 15 Jan 2017 14:49:53 +0000 (09:49 -0500)
[ Upstream commit fac8e0f579695a3ecbc4d3cac369139d7f819971 ]

When drivers express support for TSO of encapsulated packets, they
only mean that they can do it for one layer of encapsulation.
Supporting additional levels would mean updating, at a minimum,
more IP length fields and they are unaware of this.

No encapsulation device expresses support for handling offloaded
encapsulated packets, so we won't generate these types of frames
in the transmit path. However, GRO doesn't have a check for
multiple levels of encapsulation and will attempt to build them.

UDP tunnel GRO actually does prevent this situation but it only
handles multiple UDP tunnels stacked on top of each other. This
generalizes that solution to prevent any kind of tunnel stacking
that would cause problems.

Fixes: bf5a755f ("net-gre-gro: Add GRE support to the GRO stack")
Signed-off-by: Jesse Gross <jesse@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
include/linux/netdevice.h
net/core/dev.c
net/ipv4/af_inet.c
net/ipv4/gre_offload.c
net/ipv4/udp_offload.c
net/ipv6/ip6_offload.c

index db3ad1860c2edc12a31af2d4dc2e6b3f1674ba76..49ac10f99da0e92aa74d17fff5abe9e3988b86ec 100644 (file)
@@ -1897,8 +1897,8 @@ struct napi_gro_cb {
        /* This is non-zero if the packet may be of the same flow. */
        u8      same_flow:1;
 
-       /* Used in udp_gro_receive */
-       u8      udp_mark:1;
+       /* Used in tunnel GRO receive */
+       u8      encap_mark:1;
 
        /* GRO checksum is valid */
        u8      csum_valid:1;
index 93612b2e3bbfeeb12bcb8cb57c0291022aef6bd4..2a24197f3a45520581d01f2d578c39b79285fdff 100644 (file)
@@ -4020,7 +4020,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
                NAPI_GRO_CB(skb)->same_flow = 0;
                NAPI_GRO_CB(skb)->flush = 0;
                NAPI_GRO_CB(skb)->free = 0;
-               NAPI_GRO_CB(skb)->udp_mark = 0;
+               NAPI_GRO_CB(skb)->encap_mark = 0;
 
                /* Setup for GRO checksum validation */
                switch (skb->ip_summed) {
index 40eb6fa6d4ceffa8542829529e274221ac21ee2a..1a621e1f1864521f2c84f769e93aed37b5685cd1 100644 (file)
@@ -1391,6 +1391,19 @@ out:
        return pp;
 }
 
+static struct sk_buff **ipip_gro_receive(struct sk_buff **head,
+                                        struct sk_buff *skb)
+{
+       if (NAPI_GRO_CB(skb)->encap_mark) {
+               NAPI_GRO_CB(skb)->flush = 1;
+               return NULL;
+       }
+
+       NAPI_GRO_CB(skb)->encap_mark = 1;
+
+       return inet_gro_receive(head, skb);
+}
+
 #define SECONDS_PER_DAY        86400
 
 /* inet_current_timestamp - Return IP network timestamp
@@ -1682,7 +1695,7 @@ static struct packet_offload ip_packet_offload __read_mostly = {
 static const struct net_offload ipip_offload = {
        .callbacks = {
                .gso_segment    = inet_gso_segment,
-               .gro_receive    = inet_gro_receive,
+               .gro_receive    = ipip_gro_receive,
                .gro_complete   = ipip_gro_complete,
        },
 };
index abc50b41bc39cb3b5aff9a633cab95afae8b5346..cc7b08232e92b2d9f688207b0c615da9e9b47f59 100644 (file)
@@ -128,6 +128,11 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
        struct packet_offload *ptype;
        __be16 type;
 
+       if (NAPI_GRO_CB(skb)->encap_mark)
+               goto out;
+
+       NAPI_GRO_CB(skb)->encap_mark = 1;
+
        off = skb_gro_offset(skb);
        hlen = off + sizeof(*greh);
        greh = skb_gro_header_fast(skb, off);
index 6480cea7aa53a6df85a970279b82e3b9ed5a33e6..e6d05ae1f22e6203c68fcdfca0c660b5a94f67c4 100644 (file)
@@ -266,14 +266,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
        unsigned int off = skb_gro_offset(skb);
        int flush = 1;
 
-       if (NAPI_GRO_CB(skb)->udp_mark ||
+       if (NAPI_GRO_CB(skb)->encap_mark ||
            (skb->ip_summed != CHECKSUM_PARTIAL &&
             NAPI_GRO_CB(skb)->csum_cnt == 0 &&
             !NAPI_GRO_CB(skb)->csum_valid))
                goto out;
 
-       /* mark that this skb passed once through the udp gro layer */
-       NAPI_GRO_CB(skb)->udp_mark = 1;
+       /* mark that this skb passed once through the tunnel gro layer */
+       NAPI_GRO_CB(skb)->encap_mark = 1;
 
        rcu_read_lock();
        uo_priv = rcu_dereference(udp_offload_base);
index 01e12d0d8fcc9284403629fd17d7de010463d480..f291813c7c0ca005118dcce5a2566dd114f6b7e5 100644 (file)
@@ -258,6 +258,19 @@ out:
        return pp;
 }
 
+static struct sk_buff **sit_gro_receive(struct sk_buff **head,
+                                       struct sk_buff *skb)
+{
+       if (NAPI_GRO_CB(skb)->encap_mark) {
+               NAPI_GRO_CB(skb)->flush = 1;
+               return NULL;
+       }
+
+       NAPI_GRO_CB(skb)->encap_mark = 1;
+
+       return ipv6_gro_receive(head, skb);
+}
+
 static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
 {
        const struct net_offload *ops;