Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MPLS: Add limited GSO support

In the case where a non-MPLS packet is received and an MPLS stack is
added it may well be the case that the original skb is GSO but the
NIC used for transmit does not support GSO of MPLS packets.

The aim of this code is to provide GSO in software for MPLS packets
whose skbs are GSO.

SKB Usage:

When an implementation adds an MPLS stack to a non-MPLS packet it should do
the following to skb metadata:

* Set skb->inner_protocol to the old non-MPLS ethertype of the packet.
skb->inner_protocol is added by this patch.

* Set skb->protocol to the new MPLS ethertype of the packet.

* Set skb->network_header to correspond to the
end of the L3 header, including the MPLS label stack.

I have posted a patch, "[PATCH v3.29] datapath: Add basic MPLS support to
kernel" which adds MPLS support to the kernel datapath of Open vSwtich.
That patch sets the above requirements in datapath/actions.c:push_mpls()
and was used to exercise this code. The datapath patch is against the Open
vSwtich tree but it is intended that it be added to the Open vSwtich code
present in the mainline Linux kernel at some point.

Features:

I believe that the approach that I have taken is at least partially
consistent with the handling of other protocols. Jesse, I understand that
you have some ideas here. I am more than happy to change my implementation.

This patch adds dev->mpls_features which may be used by devices
to advertise features supported for MPLS packets.

A new NETIF_F_MPLS_GSO feature is added for devices which support
hardware MPLS GSO offload. Currently no devices support this
and MPLS GSO always falls back to software.

Alternate Implementation:

One possible alternate implementation is to teach netif_skb_features()
and skb_network_protocol() about MPLS, in a similar way to their
understanding of VLANs. I believe this would avoid the need
for net/mpls/mpls_gso.c and in particular the calls to
__skb_push() and __skb_push() in mpls_gso_segment().

I have decided on the implementation in this patch as it should
not introduce any overhead in the case where mpls_gso is not compiled
into the kernel or inserted as a module.

MPLS GSO suggested by Jesse Gross.
Based in part on "v4 GRE: Add TCP segmentation offload for GRE"
by Pravin B Shelar.

Cc: Jesse Gross <jesse@nicira.com>
Cc: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Simon Horman and committed by
David S. Miller
0d89d203 1a37e412

+143 -3
+3 -1
include/linux/netdev_features.h
··· 43 43 NETIF_F_FSO_BIT, /* ... FCoE segmentation */ 44 44 NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */ 45 45 NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ 46 + NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */ 46 47 /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ 47 - NETIF_F_GSO_UDP_TUNNEL_BIT, 48 + NETIF_F_GSO_MPLS_BIT, 48 49 49 50 NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ 50 51 NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ ··· 108 107 #define NETIF_F_RXALL __NETIF_F(RXALL) 109 108 #define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE) 110 109 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) 110 + #define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS) 111 111 #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) 112 112 #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) 113 113 #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
+2
include/linux/netdevice.h
··· 1088 1088 * need to set them appropriately. 1089 1089 */ 1090 1090 netdev_features_t hw_enc_features; 1091 + /* mask of fetures inheritable by MPLS */ 1092 + netdev_features_t mpls_features; 1091 1093 1092 1094 /* Interface index. Unique device identifier */ 1093 1095 int ifindex;
+4
include/linux/skbuff.h
··· 319 319 SKB_GSO_GRE = 1 << 6, 320 320 321 321 SKB_GSO_UDP_TUNNEL = 1 << 7, 322 + 323 + SKB_GSO_MPLS = 1 << 8, 322 324 }; 323 325 324 326 #if BITS_PER_LONG > 32 ··· 391 389 * @dropcount: total number of sk_receive_queue overflows 392 390 * @vlan_proto: vlan encapsulation protocol 393 391 * @vlan_tci: vlan tag control information 392 + * @inner_protocol: Protocol (encapsulation) 394 393 * @inner_transport_header: Inner transport layer header (encapsulation) 395 394 * @inner_network_header: Network layer header (encapsulation) 396 395 * @inner_mac_header: Link layer header (encapsulation) ··· 512 509 __u32 reserved_tailroom; 513 510 }; 514 511 512 + __be16 inner_protocol; 515 513 __u16 inner_transport_header; 516 514 __u16 inner_network_header; 517 515 __u16 inner_mac_header;
+1
net/Kconfig
··· 218 218 source "net/openvswitch/Kconfig" 219 219 source "net/vmw_vsock/Kconfig" 220 220 source "net/netlink/Kconfig" 221 + source "net/mpls/Kconfig" 221 222 222 223 config RPS 223 224 boolean
+1
net/Makefile
··· 70 70 obj-$(CONFIG_NFC) += nfc/ 71 71 obj-$(CONFIG_OPENVSWITCH) += openvswitch/ 72 72 obj-$(CONFIG_VSOCKETS) += vmw_vsock/ 73 + obj-$(CONFIG_NET_MPLS_GSO) += mpls/
+4
net/core/dev.c
··· 5277 5277 */ 5278 5278 dev->hw_enc_features |= NETIF_F_SG; 5279 5279 5280 + /* Make NETIF_F_SG inheritable to MPLS. 5281 + */ 5282 + dev->mpls_features |= NETIF_F_SG; 5283 + 5280 5284 ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); 5281 5285 ret = notifier_to_errno(ret); 5282 5286 if (ret)
+1
net/core/ethtool.c
··· 82 82 [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", 83 83 [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", 84 84 [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", 85 + [NETIF_F_GSO_MPLS_BIT] = "tx-mpls-segmentation", 85 86 86 87 [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", 87 88 [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",
+1
net/ipv4/af_inet.c
··· 1295 1295 SKB_GSO_GRE | 1296 1296 SKB_GSO_TCPV6 | 1297 1297 SKB_GSO_UDP_TUNNEL | 1298 + SKB_GSO_MPLS | 1298 1299 0))) 1299 1300 goto out; 1300 1301
+1
net/ipv4/tcp.c
··· 2917 2917 SKB_GSO_TCP_ECN | 2918 2918 SKB_GSO_TCPV6 | 2919 2919 SKB_GSO_GRE | 2920 + SKB_GSO_MPLS | 2920 2921 SKB_GSO_UDP_TUNNEL | 2921 2922 0) || 2922 2923 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
+1 -1
net/ipv4/udp.c
··· 2381 2381 2382 2382 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | 2383 2383 SKB_GSO_UDP_TUNNEL | 2384 - SKB_GSO_GRE) || 2384 + SKB_GSO_GRE | SKB_GSO_MPLS) || 2385 2385 !(type & (SKB_GSO_UDP)))) 2386 2386 goto out; 2387 2387
+1
net/ipv6/ip6_offload.c
··· 98 98 SKB_GSO_TCP_ECN | 99 99 SKB_GSO_GRE | 100 100 SKB_GSO_UDP_TUNNEL | 101 + SKB_GSO_MPLS | 101 102 SKB_GSO_TCPV6 | 102 103 0))) 103 104 goto out;
+2 -1
net/ipv6/udp_offload.c
··· 63 63 if (unlikely(type & ~(SKB_GSO_UDP | 64 64 SKB_GSO_DODGY | 65 65 SKB_GSO_UDP_TUNNEL | 66 - SKB_GSO_GRE) || 66 + SKB_GSO_GRE | 67 + SKB_GSO_MPLS) || 67 68 !(type & (SKB_GSO_UDP)))) 68 69 goto out; 69 70
+9
net/mpls/Kconfig
··· 1 + # 2 + # MPLS configuration 3 + # 4 + config NET_MPLS_GSO 5 + tristate "MPLS: GSO support" 6 + help 7 + This is helper module to allow segmentation of non-MPLS GSO packets 8 + that have had MPLS stack entries pushed onto them and thus 9 + become MPLS GSO packets.
+4
net/mpls/Makefile
··· 1 + # 2 + # Makefile for MPLS. 3 + # 4 + obj-y += mpls_gso.o
+108
net/mpls/mpls_gso.c
··· 1 + /* 2 + * MPLS GSO Support 3 + * 4 + * Authors: Simon Horman (horms@verge.net.au) 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + * 11 + * Based on: GSO portions of net/ipv4/gre.c 12 + */ 13 + 14 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 + 16 + #include <linux/err.h> 17 + #include <linux/module.h> 18 + #include <linux/netdev_features.h> 19 + #include <linux/netdevice.h> 20 + #include <linux/skbuff.h> 21 + 22 + static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, 23 + netdev_features_t features) 24 + { 25 + struct sk_buff *segs = ERR_PTR(-EINVAL); 26 + netdev_features_t mpls_features; 27 + __be16 mpls_protocol; 28 + 29 + if (unlikely(skb_shinfo(skb)->gso_type & 30 + ~(SKB_GSO_TCPV4 | 31 + SKB_GSO_TCPV6 | 32 + SKB_GSO_UDP | 33 + SKB_GSO_DODGY | 34 + SKB_GSO_TCP_ECN | 35 + SKB_GSO_GRE | 36 + SKB_GSO_MPLS))) 37 + goto out; 38 + 39 + /* Setup inner SKB. */ 40 + mpls_protocol = skb->protocol; 41 + skb->protocol = skb->inner_protocol; 42 + 43 + /* Push back the mac header that skb_mac_gso_segment() has pulled. 44 + * It will be re-pulled by the call to skb_mac_gso_segment() below 45 + */ 46 + __skb_push(skb, skb->mac_len); 47 + 48 + /* Segment inner packet. */ 49 + mpls_features = skb->dev->mpls_features & netif_skb_features(skb); 50 + segs = skb_mac_gso_segment(skb, mpls_features); 51 + 52 + 53 + /* Restore outer protocol. */ 54 + skb->protocol = mpls_protocol; 55 + 56 + /* Re-pull the mac header that the call to skb_mac_gso_segment() 57 + * above pulled. It will be re-pushed after returning 58 + * skb_mac_gso_segment(), an indirect caller of this function. 59 + */ 60 + __skb_push(skb, skb->data - skb_mac_header(skb)); 61 + 62 + out: 63 + return segs; 64 + } 65 + 66 + static int mpls_gso_send_check(struct sk_buff *skb) 67 + { 68 + return 0; 69 + } 70 + 71 + static struct packet_offload mpls_mc_offload = { 72 + .type = cpu_to_be16(ETH_P_MPLS_MC), 73 + .callbacks = { 74 + .gso_send_check = mpls_gso_send_check, 75 + .gso_segment = mpls_gso_segment, 76 + }, 77 + }; 78 + 79 + static struct packet_offload mpls_uc_offload = { 80 + .type = cpu_to_be16(ETH_P_MPLS_UC), 81 + .callbacks = { 82 + .gso_send_check = mpls_gso_send_check, 83 + .gso_segment = mpls_gso_segment, 84 + }, 85 + }; 86 + 87 + static int __init mpls_gso_init(void) 88 + { 89 + pr_info("MPLS GSO support\n"); 90 + 91 + dev_add_offload(&mpls_uc_offload); 92 + dev_add_offload(&mpls_mc_offload); 93 + 94 + return 0; 95 + } 96 + 97 + static void __exit mpls_gso_exit(void) 98 + { 99 + dev_remove_offload(&mpls_uc_offload); 100 + dev_remove_offload(&mpls_mc_offload); 101 + } 102 + 103 + module_init(mpls_gso_init); 104 + module_exit(mpls_gso_exit); 105 + 106 + MODULE_DESCRIPTION("MPLS GSO support"); 107 + MODULE_AUTHOR("Simon Horman (horms@verge.net.au)"); 108 + MODULE_LICENSE("GPL");