![Florian Westphal](/assets/img/avatar_default.png)
skb_gso_segment() has a 'features' argument representing offload features available to the output path. A few handlers, e.g. GRE, instead re-fetch the features of skb->dev and use those instead of the provided ones when handing encapsulation/tunnels. Depending on dev->hw_enc_features of the output device skb_gso_segment() can then return NULL even when the caller has disabled all GSO feature bits, as segmentation of inner header thinks device will take care of segmentation. This e.g. affects the tbf scheduler, which will silently drop GRE-encap GSO skbs that did not fit the remaining token quota as the segmentation does not work when device supports corresponding hw offload capabilities. Cc: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
104 lines
2.4 KiB
C
104 lines
2.4 KiB
C
/*
|
|
* 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_GRE_CSUM |
|
|
SKB_GSO_IPIP |
|
|
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 & features;
|
|
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 struct packet_offload mpls_mc_offload = {
|
|
.type = cpu_to_be16(ETH_P_MPLS_MC),
|
|
.callbacks = {
|
|
.gso_segment = mpls_gso_segment,
|
|
},
|
|
};
|
|
|
|
static struct packet_offload mpls_uc_offload = {
|
|
.type = cpu_to_be16(ETH_P_MPLS_UC),
|
|
.callbacks = {
|
|
.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");
|