mpls_gso.c 2.25 KB
Newer Older
Simon Horman's avatar
Simon Horman committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 *	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;

	/* 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. */
39
	mpls_features = skb->dev->mpls_features & features;
Simon Horman's avatar
Simon Horman committed
40 41 42 43 44 45 46 47 48 49
	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.
	 */
50
	__skb_pull(skb, skb->data - skb_mac_header(skb));
51

Simon Horman's avatar
Simon Horman committed
52 53 54
	return segs;
}

55
static struct packet_offload mpls_mc_offload __read_mostly = {
Simon Horman's avatar
Simon Horman committed
56
	.type = cpu_to_be16(ETH_P_MPLS_MC),
57
	.priority = 15,
Simon Horman's avatar
Simon Horman committed
58 59 60 61 62
	.callbacks = {
		.gso_segment    =	mpls_gso_segment,
	},
};

63
static struct packet_offload mpls_uc_offload __read_mostly = {
Simon Horman's avatar
Simon Horman committed
64
	.type = cpu_to_be16(ETH_P_MPLS_UC),
65
	.priority = 15,
Simon Horman's avatar
Simon Horman committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
	.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");