tunnels: support MPLS over IPv4 tunnels

Extend tunnel support to MPLS over IPv4.  The implementation extends the
existing differentiation between IPIP and IPv6 over IPv4 to also cover MPLS
over IPv4.

Signed-off-by: Simon Horman <simon.horman@netronome.com>
Reviewed-by: Dinan Gunawardena <dinan.gunawardena@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Simon Horman 2016-07-07 07:56:12 +02:00 committed by David S. Miller
parent a65056ecf4
commit 8afe97e5d4

View File

@ -6,6 +6,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/mpls.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -16,11 +17,14 @@
static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly; static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly;
static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly; static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly;
static struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly;
static DEFINE_MUTEX(tunnel4_mutex); static DEFINE_MUTEX(tunnel4_mutex);
static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family) static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
{ {
return (family == AF_INET) ? &tunnel4_handlers : &tunnel64_handlers; return (family == AF_INET) ? &tunnel4_handlers :
(family == AF_INET6) ? &tunnel64_handlers :
&tunnelmpls4_handlers;
} }
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family) int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
@ -125,6 +129,26 @@ drop:
} }
#endif #endif
#if IS_ENABLED(CONFIG_MPLS)
static int tunnelmpls4_rcv(struct sk_buff *skb)
{
struct xfrm_tunnel *handler;
if (!pskb_may_pull(skb, sizeof(struct mpls_label)))
goto drop;
for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
if (!handler->handler(skb))
return 0;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
drop:
kfree_skb(skb);
return 0;
}
#endif
static void tunnel4_err(struct sk_buff *skb, u32 info) static void tunnel4_err(struct sk_buff *skb, u32 info)
{ {
struct xfrm_tunnel *handler; struct xfrm_tunnel *handler;
@ -145,6 +169,17 @@ static void tunnel64_err(struct sk_buff *skb, u32 info)
} }
#endif #endif
#if IS_ENABLED(CONFIG_MPLS)
static void tunnelmpls4_err(struct sk_buff *skb, u32 info)
{
struct xfrm_tunnel *handler;
for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
if (!handler->err_handler(skb, info))
break;
}
#endif
static const struct net_protocol tunnel4_protocol = { static const struct net_protocol tunnel4_protocol = {
.handler = tunnel4_rcv, .handler = tunnel4_rcv,
.err_handler = tunnel4_err, .err_handler = tunnel4_err,
@ -161,24 +196,46 @@ static const struct net_protocol tunnel64_protocol = {
}; };
#endif #endif
#if IS_ENABLED(CONFIG_MPLS)
static const struct net_protocol tunnelmpls4_protocol = {
.handler = tunnelmpls4_rcv,
.err_handler = tunnelmpls4_err,
.no_policy = 1,
.netns_ok = 1,
};
#endif
static int __init tunnel4_init(void) static int __init tunnel4_init(void)
{ {
if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) { if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
pr_err("%s: can't add protocol\n", __func__); goto err_ipip;
return -EAGAIN;
}
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) { if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6))
pr_err("tunnel64 init: can't add protocol\n"); goto err_ipv6;
inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); #endif
return -EAGAIN; #if IS_ENABLED(CONFIG_MPLS)
} if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
goto err_mpls;
#endif #endif
return 0; return 0;
#if IS_ENABLED(CONFIG_IPV6)
err_mpls:
inet_del_protocol(&tunnel4_protocol, IPPROTO_IPV6);
#endif
err_ipv6:
inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
err_ipip:
pr_err("%s: can't add protocol\n", __func__);
return -EAGAIN;
} }
static void __exit tunnel4_fini(void) static void __exit tunnel4_fini(void)
{ {
#if IS_ENABLED(CONFIG_MPLS)
if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
pr_err("tunnelmpls4 close: can't remove protocol\n");
#endif
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6)) if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
pr_err("tunnel64 close: can't remove protocol\n"); pr_err("tunnel64 close: can't remove protocol\n");