NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
+       NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
-               NETIF_F_GSO_UDP_TUNNEL_BIT,
+               NETIF_F_GSO_MPLS_BIT,
 
        NETIF_F_FCOE_CRC_BIT,           /* FCoE CRC32 */
        NETIF_F_SCTP_CSUM_BIT,          /* SCTP checksum offload */
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
 #define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
+#define NETIF_F_GSO_MPLS       __NETIF_F(GSO_MPLS)
 #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
 #define NETIF_F_HW_VLAN_STAG_RX        __NETIF_F(HW_VLAN_STAG_RX)
 #define NETIF_F_HW_VLAN_STAG_TX        __NETIF_F(HW_VLAN_STAG_TX)
 
         * need to set them appropriately.
         */
        netdev_features_t       hw_enc_features;
+       /* mask of fetures inheritable by MPLS */
+       netdev_features_t       mpls_features;
 
        /* Interface index. Unique device identifier    */
        int                     ifindex;
 
        SKB_GSO_GRE = 1 << 6,
 
        SKB_GSO_UDP_TUNNEL = 1 << 7,
+
+       SKB_GSO_MPLS = 1 << 8,
 };
 
 #if BITS_PER_LONG > 32
  *     @dropcount: total number of sk_receive_queue overflows
  *     @vlan_proto: vlan encapsulation protocol
  *     @vlan_tci: vlan tag control information
+ *     @inner_protocol: Protocol (encapsulation)
  *     @inner_transport_header: Inner transport layer header (encapsulation)
  *     @inner_network_header: Network layer header (encapsulation)
  *     @inner_mac_header: Link layer header (encapsulation)
                __u32           reserved_tailroom;
        };
 
+       __be16                  inner_protocol;
        __u16                   inner_transport_header;
        __u16                   inner_network_header;
        __u16                   inner_mac_header;
 
 source "net/openvswitch/Kconfig"
 source "net/vmw_vsock/Kconfig"
 source "net/netlink/Kconfig"
+source "net/mpls/Kconfig"
 
 config RPS
        boolean
 
 obj-$(CONFIG_NFC)              += nfc/
 obj-$(CONFIG_OPENVSWITCH)      += openvswitch/
 obj-$(CONFIG_VSOCKETS) += vmw_vsock/
+obj-$(CONFIG_NET_MPLS_GSO)     += mpls/
 
         */
        dev->hw_enc_features |= NETIF_F_SG;
 
+       /* Make NETIF_F_SG inheritable to MPLS.
+        */
+       dev->mpls_features |= NETIF_F_SG;
+
        ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
        ret = notifier_to_errno(ret);
        if (ret)
 
        [NETIF_F_FSO_BIT] =              "tx-fcoe-segmentation",
        [NETIF_F_GSO_GRE_BIT] =          "tx-gre-segmentation",
        [NETIF_F_GSO_UDP_TUNNEL_BIT] =   "tx-udp_tnl-segmentation",
+       [NETIF_F_GSO_MPLS_BIT] =         "tx-mpls-segmentation",
 
        [NETIF_F_FCOE_CRC_BIT] =         "tx-checksum-fcoe-crc",
        [NETIF_F_SCTP_CSUM_BIT] =        "tx-checksum-sctp",
 
                       SKB_GSO_GRE |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_MPLS |
                       0)))
                goto out;
 
 
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_MPLS |
                               SKB_GSO_UDP_TUNNEL |
                               0) ||
                             !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
 
 
                if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
-                                     SKB_GSO_GRE) ||
+                                     SKB_GSO_GRE | SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
 
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
                goto out;
 
                if (unlikely(type & ~(SKB_GSO_UDP |
                                      SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
-                                     SKB_GSO_GRE) ||
+                                     SKB_GSO_GRE |
+                                     SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
 
--- /dev/null
+#
+# MPLS configuration
+#
+config NET_MPLS_GSO
+       tristate "MPLS: GSO support"
+       help
+        This is helper module to allow segmentation of non-MPLS GSO packets
+        that have had MPLS stack entries pushed onto them and thus
+        become MPLS GSO packets.
 
--- /dev/null
+#
+# Makefile for MPLS.
+#
+obj-y += mpls_gso.o
 
--- /dev/null
+/*
+ *     MPLS GSO Support
+ *
+ *     Authors: Simon Horman (horms@verge.net.au)
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ *     Based on: GSO portions of net/ipv4/gre.c
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/netdev_features.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
+                                      netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       netdev_features_t mpls_features;
+       __be16 mpls_protocol;
+
+       if (unlikely(skb_shinfo(skb)->gso_type &
+                               ~(SKB_GSO_TCPV4 |
+                                 SKB_GSO_TCPV6 |
+                                 SKB_GSO_UDP |
+                                 SKB_GSO_DODGY |
+                                 SKB_GSO_TCP_ECN |
+                                 SKB_GSO_GRE |
+                                 SKB_GSO_MPLS)))
+               goto out;
+
+       /* Setup inner SKB. */
+       mpls_protocol = skb->protocol;
+       skb->protocol = skb->inner_protocol;
+
+       /* Push back the mac header that skb_mac_gso_segment() has pulled.
+        * It will be re-pulled by the call to skb_mac_gso_segment() below
+        */
+       __skb_push(skb, skb->mac_len);
+
+       /* Segment inner packet. */
+       mpls_features = skb->dev->mpls_features & netif_skb_features(skb);
+       segs = skb_mac_gso_segment(skb, mpls_features);
+
+
+       /* Restore outer protocol. */
+       skb->protocol = mpls_protocol;
+
+       /* Re-pull the mac header that the call to skb_mac_gso_segment()
+        * above pulled.  It will be re-pushed after returning
+        * skb_mac_gso_segment(), an indirect caller of this function.
+        */
+       __skb_push(skb, skb->data - skb_mac_header(skb));
+
+out:
+       return segs;
+}
+
+static int mpls_gso_send_check(struct sk_buff *skb)
+{
+       return 0;
+}
+
+static struct packet_offload mpls_mc_offload = {
+       .type = cpu_to_be16(ETH_P_MPLS_MC),
+       .callbacks = {
+               .gso_send_check =       mpls_gso_send_check,
+               .gso_segment    =       mpls_gso_segment,
+       },
+};
+
+static struct packet_offload mpls_uc_offload = {
+       .type = cpu_to_be16(ETH_P_MPLS_UC),
+       .callbacks = {
+               .gso_send_check =       mpls_gso_send_check,
+               .gso_segment    =       mpls_gso_segment,
+       },
+};
+
+static int __init mpls_gso_init(void)
+{
+       pr_info("MPLS GSO support\n");
+
+       dev_add_offload(&mpls_uc_offload);
+       dev_add_offload(&mpls_mc_offload);
+
+       return 0;
+}
+
+static void __exit mpls_gso_exit(void)
+{
+       dev_remove_offload(&mpls_uc_offload);
+       dev_remove_offload(&mpls_mc_offload);
+}
+
+module_init(mpls_gso_init);
+module_exit(mpls_gso_exit);
+
+MODULE_DESCRIPTION("MPLS GSO support");
+MODULE_AUTHOR("Simon Horman (horms@verge.net.au)");
+MODULE_LICENSE("GPL");