guehdr = (struct guehdr *)&udp_hdr(skb)[1];
 
+       switch (guehdr->version) {
+       case 0: /* Full GUE header present */
+               break;
+
+       case 1: {
+               /* Direct encasulation of IPv4 or IPv6 */
+
+               int prot;
+
+               switch (((struct iphdr *)guehdr)->version) {
+               case 4:
+                       prot = IPPROTO_IPIP;
+                       break;
+               case 6:
+                       prot = IPPROTO_IPV6;
+                       break;
+               default:
+                       goto drop;
+               }
+
+               if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
+                       goto drop;
+
+               return -prot;
+       }
+
+       default: /* Undefined version */
+               goto drop;
+       }
+
        optlen = guehdr->hlen << 2;
        len += optlen;
 
        int flush = 1;
        struct fou *fou = fou_from_sock(sk);
        struct gro_remcsum grc;
+       u8 proto;
 
        skb_gro_remcsum_init(&grc);
 
                        goto out;
        }
 
+       switch (guehdr->version) {
+       case 0:
+               break;
+       case 1:
+               switch (((struct iphdr *)guehdr)->version) {
+               case 4:
+                       proto = IPPROTO_IPIP;
+                       break;
+               case 6:
+                       proto = IPPROTO_IPV6;
+                       break;
+               default:
+                       goto out;
+               }
+               goto next_proto;
+       default:
+               goto out;
+       }
+
        optlen = guehdr->hlen << 2;
        len += optlen;
 
                }
        }
 
+       proto = guehdr->proto_ctype;
+
+next_proto:
+
        /* We can clear the encap_mark for GUE as we are essentially doing
         * one of two possible things.  We are either adding an L4 tunnel
         * header to the outer L3 tunnel header, or we are are simply
 
        rcu_read_lock();
        offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
-       ops = rcu_dereference(offloads[guehdr->proto_ctype]);
+       ops = rcu_dereference(offloads[proto]);
        if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
                goto out_unlock;
 
        const struct net_offload **offloads;
        struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
        const struct net_offload *ops;
-       unsigned int guehlen;
+       unsigned int guehlen = 0;
        u8 proto;
        int err = -ENOENT;
 
-       proto = guehdr->proto_ctype;
-
-       guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+       switch (guehdr->version) {
+       case 0:
+               proto = guehdr->proto_ctype;
+               guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+               break;
+       case 1:
+               switch (((struct iphdr *)guehdr)->version) {
+               case 4:
+                       proto = IPPROTO_IPIP;
+                       break;
+               case 6:
+                       proto = IPPROTO_IPV6;
+                       break;
+               default:
+                       return err;
+               }
+               break;
+       default:
+               return err;
+       }
 
        rcu_read_lock();
        offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;