10d89d203SSimon Horman /* 20d89d203SSimon Horman * MPLS GSO Support 30d89d203SSimon Horman * 40d89d203SSimon Horman * Authors: Simon Horman (horms@verge.net.au) 50d89d203SSimon Horman * 60d89d203SSimon Horman * This program is free software; you can redistribute it and/or 70d89d203SSimon Horman * modify it under the terms of the GNU General Public License 80d89d203SSimon Horman * as published by the Free Software Foundation; either version 90d89d203SSimon Horman * 2 of the License, or (at your option) any later version. 100d89d203SSimon Horman * 110d89d203SSimon Horman * Based on: GSO portions of net/ipv4/gre.c 120d89d203SSimon Horman */ 130d89d203SSimon Horman 140d89d203SSimon Horman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 150d89d203SSimon Horman 160d89d203SSimon Horman #include <linux/err.h> 170d89d203SSimon Horman #include <linux/module.h> 180d89d203SSimon Horman #include <linux/netdev_features.h> 190d89d203SSimon Horman #include <linux/netdevice.h> 200d89d203SSimon Horman #include <linux/skbuff.h> 210d89d203SSimon Horman 220d89d203SSimon Horman static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, 230d89d203SSimon Horman netdev_features_t features) 240d89d203SSimon Horman { 250d89d203SSimon Horman struct sk_buff *segs = ERR_PTR(-EINVAL); 260d89d203SSimon Horman netdev_features_t mpls_features; 270d89d203SSimon Horman __be16 mpls_protocol; 280d89d203SSimon Horman 290d89d203SSimon Horman if (unlikely(skb_shinfo(skb)->gso_type & 300d89d203SSimon Horman ~(SKB_GSO_TCPV4 | 310d89d203SSimon Horman SKB_GSO_TCPV6 | 320d89d203SSimon Horman SKB_GSO_UDP | 330d89d203SSimon Horman SKB_GSO_DODGY | 340d89d203SSimon Horman SKB_GSO_TCP_ECN | 350d89d203SSimon Horman SKB_GSO_GRE | 364749c09cSTom Herbert SKB_GSO_GRE_CSUM | 37cb32f511SEric Dumazet SKB_GSO_IPIP | 380d89d203SSimon Horman SKB_GSO_MPLS))) 390d89d203SSimon Horman goto out; 400d89d203SSimon Horman 410d89d203SSimon Horman /* Setup inner SKB. */ 420d89d203SSimon Horman mpls_protocol = skb->protocol; 430d89d203SSimon Horman skb->protocol = skb->inner_protocol; 440d89d203SSimon Horman 450d89d203SSimon Horman /* Push back the mac header that skb_mac_gso_segment() has pulled. 460d89d203SSimon Horman * It will be re-pulled by the call to skb_mac_gso_segment() below 470d89d203SSimon Horman */ 480d89d203SSimon Horman __skb_push(skb, skb->mac_len); 490d89d203SSimon Horman 500d89d203SSimon Horman /* Segment inner packet. */ 51*1e16aa3dSFlorian Westphal mpls_features = skb->dev->mpls_features & features; 520d89d203SSimon Horman segs = skb_mac_gso_segment(skb, mpls_features); 530d89d203SSimon Horman 540d89d203SSimon Horman 550d89d203SSimon Horman /* Restore outer protocol. */ 560d89d203SSimon Horman skb->protocol = mpls_protocol; 570d89d203SSimon Horman 580d89d203SSimon Horman /* Re-pull the mac header that the call to skb_mac_gso_segment() 590d89d203SSimon Horman * above pulled. It will be re-pushed after returning 600d89d203SSimon Horman * skb_mac_gso_segment(), an indirect caller of this function. 610d89d203SSimon Horman */ 620d89d203SSimon Horman __skb_push(skb, skb->data - skb_mac_header(skb)); 630d89d203SSimon Horman 640d89d203SSimon Horman out: 650d89d203SSimon Horman return segs; 660d89d203SSimon Horman } 670d89d203SSimon Horman 680d89d203SSimon Horman static struct packet_offload mpls_mc_offload = { 690d89d203SSimon Horman .type = cpu_to_be16(ETH_P_MPLS_MC), 700d89d203SSimon Horman .callbacks = { 710d89d203SSimon Horman .gso_segment = mpls_gso_segment, 720d89d203SSimon Horman }, 730d89d203SSimon Horman }; 740d89d203SSimon Horman 750d89d203SSimon Horman static struct packet_offload mpls_uc_offload = { 760d89d203SSimon Horman .type = cpu_to_be16(ETH_P_MPLS_UC), 770d89d203SSimon Horman .callbacks = { 780d89d203SSimon Horman .gso_segment = mpls_gso_segment, 790d89d203SSimon Horman }, 800d89d203SSimon Horman }; 810d89d203SSimon Horman 820d89d203SSimon Horman static int __init mpls_gso_init(void) 830d89d203SSimon Horman { 840d89d203SSimon Horman pr_info("MPLS GSO support\n"); 850d89d203SSimon Horman 860d89d203SSimon Horman dev_add_offload(&mpls_uc_offload); 870d89d203SSimon Horman dev_add_offload(&mpls_mc_offload); 880d89d203SSimon Horman 890d89d203SSimon Horman return 0; 900d89d203SSimon Horman } 910d89d203SSimon Horman 920d89d203SSimon Horman static void __exit mpls_gso_exit(void) 930d89d203SSimon Horman { 940d89d203SSimon Horman dev_remove_offload(&mpls_uc_offload); 950d89d203SSimon Horman dev_remove_offload(&mpls_mc_offload); 960d89d203SSimon Horman } 970d89d203SSimon Horman 980d89d203SSimon Horman module_init(mpls_gso_init); 990d89d203SSimon Horman module_exit(mpls_gso_exit); 1000d89d203SSimon Horman 1010d89d203SSimon Horman MODULE_DESCRIPTION("MPLS GSO support"); 1020d89d203SSimon Horman MODULE_AUTHOR("Simon Horman (horms@verge.net.au)"); 1030d89d203SSimon Horman MODULE_LICENSE("GPL"); 104