__u8                    ndisc_nodetype:2;
 #endif
        __u8                    ipvs_property:1;
-       /* 5 or 7 bit hole */
+       __u8                    inner_protocol_type:1;
+       /* 4 or 6 bit hole */
 
 #ifdef CONFIG_NET_SCHED
        __u16                   tc_index;       /* traffic control index */
                __u32           reserved_tailroom;
        };
 
-       __be16                  inner_protocol;
+       union {
+               __be16          inner_protocol;
+               __u8            inner_ipproto;
+       };
+
        __u16                   inner_transport_header;
        __u16                   inner_network_header;
        __u16                   inner_mac_header;
        skb->tail += len;
 }
 
+#define ENCAP_TYPE_ETHER       0
+#define ENCAP_TYPE_IPPROTO     1
+
+static inline void skb_set_inner_protocol(struct sk_buff *skb,
+                                         __be16 protocol)
+{
+       skb->inner_protocol = protocol;
+       skb->inner_protocol_type = ENCAP_TYPE_ETHER;
+}
+
+static inline void skb_set_inner_ipproto(struct sk_buff *skb,
+                                        __u8 ipproto)
+{
+       skb->inner_ipproto = ipproto;
+       skb->inner_protocol_type = ENCAP_TYPE_IPPROTO;
+}
+
 static inline void skb_reset_inner_headers(struct sk_buff *skb)
 {
        skb->inner_mac_header = skb->mac_header;
 
 int udp_disconnect(struct sock *sk, int flags);
 unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
 struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
-                                      netdev_features_t features);
+                                      netdev_features_t features,
+                                      bool is_ipv6);
 int udp_lib_getsockopt(struct sock *sk, int level, int optname,
                       char __user *optval, int __user *optlen);
 int udp_lib_setsockopt(struct sock *sk, int level, int optname,
 
        struct udp_offload_priv __rcu *next;
 };
 
-struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
-                                      netdev_features_t features)
+static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
+       netdev_features_t features,
+       struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
+                                            netdev_features_t features),
+       __be16 new_protocol)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        u16 mac_offset = skb->mac_header;
        skb_reset_mac_header(skb);
        skb_set_network_header(skb, skb_inner_network_offset(skb));
        skb->mac_len = skb_inner_network_offset(skb);
-       skb->protocol = htons(ETH_P_TEB);
+       skb->protocol = new_protocol;
 
        need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
        if (need_csum)
 
        /* segment inner packet. */
        enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
-       segs = skb_mac_gso_segment(skb, enc_features);
+       segs = gso_inner_segment(skb, enc_features);
        if (IS_ERR_OR_NULL(segs)) {
                skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
                                     mac_len);
        return segs;
 }
 
+struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+                                      netdev_features_t features,
+                                      bool is_ipv6)
+{
+       __be16 protocol = skb->protocol;
+       const struct net_offload **offloads;
+       const struct net_offload *ops;
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
+                                            netdev_features_t features);
+
+       rcu_read_lock();
+
+       switch (skb->inner_protocol_type) {
+       case ENCAP_TYPE_ETHER:
+               protocol = skb->inner_protocol;
+               gso_inner_segment = skb_mac_gso_segment;
+               break;
+       case ENCAP_TYPE_IPPROTO:
+               offloads = is_ipv6 ? inet6_offloads : inet_offloads;
+               ops = rcu_dereference(offloads[skb->inner_ipproto]);
+               if (!ops || !ops->callbacks.gso_segment)
+                       goto out_unlock;
+               gso_inner_segment = ops->callbacks.gso_segment;
+               break;
+       default:
+               goto out_unlock;
+       }
+
+       segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment,
+                                       protocol);
+
+out_unlock:
+       rcu_read_unlock();
+
+       return segs;
+}
+
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
                                         netdev_features_t features)
 {
        if (skb->encapsulation &&
            (skb_shinfo(skb)->gso_type &
             (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
-               segs = skb_udp_tunnel_segment(skb, features);
+               segs = skb_udp_tunnel_segment(skb, features, false);
                goto out;
        }