xref: /openbmc/linux/net/ipv4/tunnel4.c (revision 32bbd8793f24b0d5beb1cdb33c45c75ad1140e4b)
1d2acc347SHerbert Xu /* tunnel4.c: Generic IP tunnel transformer.
2d2acc347SHerbert Xu  *
3d2acc347SHerbert Xu  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
4d2acc347SHerbert Xu  */
5d2acc347SHerbert Xu 
6d2acc347SHerbert Xu #include <linux/init.h>
7d2acc347SHerbert Xu #include <linux/module.h>
8d2acc347SHerbert Xu #include <linux/mutex.h>
98afe97e5SSimon Horman #include <linux/mpls.h>
10d2acc347SHerbert Xu #include <linux/netdevice.h>
11d2acc347SHerbert Xu #include <linux/skbuff.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
1350fba2aaSHerbert Xu #include <net/icmp.h>
1450fba2aaSHerbert Xu #include <net/ip.h>
15d2acc347SHerbert Xu #include <net/protocol.h>
16d2acc347SHerbert Xu #include <net/xfrm.h>
17d2acc347SHerbert Xu 
18b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly;
19b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly;
208afe97e5SSimon Horman static struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly;
21d2acc347SHerbert Xu static DEFINE_MUTEX(tunnel4_mutex);
22d2acc347SHerbert Xu 
23b33eab08SEric Dumazet static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
24358352b8SPavel Emelyanov {
258afe97e5SSimon Horman 	return (family == AF_INET) ? &tunnel4_handlers :
268afe97e5SSimon Horman 		(family == AF_INET6) ? &tunnel64_handlers :
278afe97e5SSimon Horman 		&tunnelmpls4_handlers;
28358352b8SPavel Emelyanov }
29358352b8SPavel Emelyanov 
30c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
31d2acc347SHerbert Xu {
32b33eab08SEric Dumazet 	struct xfrm_tunnel __rcu **pprev;
33b33eab08SEric Dumazet 	struct xfrm_tunnel *t;
34b33eab08SEric Dumazet 
35d2acc347SHerbert Xu 	int ret = -EEXIST;
36d2acc347SHerbert Xu 	int priority = handler->priority;
37d2acc347SHerbert Xu 
38d2acc347SHerbert Xu 	mutex_lock(&tunnel4_mutex);
39d2acc347SHerbert Xu 
40b33eab08SEric Dumazet 	for (pprev = fam_handlers(family);
41b33eab08SEric Dumazet 	     (t = rcu_dereference_protected(*pprev,
42b33eab08SEric Dumazet 			lockdep_is_held(&tunnel4_mutex))) != NULL;
43b33eab08SEric Dumazet 	     pprev = &t->next) {
44b33eab08SEric Dumazet 		if (t->priority > priority)
45d2acc347SHerbert Xu 			break;
46b33eab08SEric Dumazet 		if (t->priority == priority)
47d2acc347SHerbert Xu 			goto err;
48d2acc347SHerbert Xu 	}
49d2acc347SHerbert Xu 
50d2acc347SHerbert Xu 	handler->next = *pprev;
5149d61e23SEric Dumazet 	rcu_assign_pointer(*pprev, handler);
52d2acc347SHerbert Xu 
53d2acc347SHerbert Xu 	ret = 0;
54d2acc347SHerbert Xu 
55d2acc347SHerbert Xu err:
56d2acc347SHerbert Xu 	mutex_unlock(&tunnel4_mutex);
57d2acc347SHerbert Xu 
58d2acc347SHerbert Xu 	return ret;
59d2acc347SHerbert Xu }
60d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_register);
61d2acc347SHerbert Xu 
62c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family)
63d2acc347SHerbert Xu {
64b33eab08SEric Dumazet 	struct xfrm_tunnel __rcu **pprev;
65b33eab08SEric Dumazet 	struct xfrm_tunnel *t;
66d2acc347SHerbert Xu 	int ret = -ENOENT;
67d2acc347SHerbert Xu 
68d2acc347SHerbert Xu 	mutex_lock(&tunnel4_mutex);
69d2acc347SHerbert Xu 
70b33eab08SEric Dumazet 	for (pprev = fam_handlers(family);
71b33eab08SEric Dumazet 	     (t = rcu_dereference_protected(*pprev,
72b33eab08SEric Dumazet 			lockdep_is_held(&tunnel4_mutex))) != NULL;
73b33eab08SEric Dumazet 	     pprev = &t->next) {
74b33eab08SEric Dumazet 		if (t == handler) {
75d2acc347SHerbert Xu 			*pprev = handler->next;
76d2acc347SHerbert Xu 			ret = 0;
77d2acc347SHerbert Xu 			break;
78d2acc347SHerbert Xu 		}
79d2acc347SHerbert Xu 	}
80d2acc347SHerbert Xu 
81d2acc347SHerbert Xu 	mutex_unlock(&tunnel4_mutex);
82d2acc347SHerbert Xu 
83d2acc347SHerbert Xu 	synchronize_net();
84d2acc347SHerbert Xu 
85d2acc347SHerbert Xu 	return ret;
86d2acc347SHerbert Xu }
87d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_deregister);
88d2acc347SHerbert Xu 
89875168a9SEric Dumazet #define for_each_tunnel_rcu(head, handler)		\
90875168a9SEric Dumazet 	for (handler = rcu_dereference(head);		\
91875168a9SEric Dumazet 	     handler != NULL;				\
92875168a9SEric Dumazet 	     handler = rcu_dereference(handler->next))	\
93875168a9SEric Dumazet 
94d2acc347SHerbert Xu static int tunnel4_rcv(struct sk_buff *skb)
95d2acc347SHerbert Xu {
96d2acc347SHerbert Xu 	struct xfrm_tunnel *handler;
97d2acc347SHerbert Xu 
9850fba2aaSHerbert Xu 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
9950fba2aaSHerbert Xu 		goto drop;
10050fba2aaSHerbert Xu 
101875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel4_handlers, handler)
102d2acc347SHerbert Xu 		if (!handler->handler(skb))
103d2acc347SHerbert Xu 			return 0;
104d2acc347SHerbert Xu 
10550fba2aaSHerbert Xu 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
10650fba2aaSHerbert Xu 
10750fba2aaSHerbert Xu drop:
108d2acc347SHerbert Xu 	kfree_skb(skb);
109d2acc347SHerbert Xu 	return 0;
110d2acc347SHerbert Xu }
111d2acc347SHerbert Xu 
112dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
113c0d56408SKazunori MIYAZAWA static int tunnel64_rcv(struct sk_buff *skb)
114c0d56408SKazunori MIYAZAWA {
115c0d56408SKazunori MIYAZAWA 	struct xfrm_tunnel *handler;
116c0d56408SKazunori MIYAZAWA 
117baa2bfb8SYOSHIFUJI Hideaki 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
118c0d56408SKazunori MIYAZAWA 		goto drop;
119c0d56408SKazunori MIYAZAWA 
120875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel64_handlers, handler)
121c0d56408SKazunori MIYAZAWA 		if (!handler->handler(skb))
122c0d56408SKazunori MIYAZAWA 			return 0;
123c0d56408SKazunori MIYAZAWA 
124c0d56408SKazunori MIYAZAWA 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
125c0d56408SKazunori MIYAZAWA 
126c0d56408SKazunori MIYAZAWA drop:
127c0d56408SKazunori MIYAZAWA 	kfree_skb(skb);
128c0d56408SKazunori MIYAZAWA 	return 0;
129c0d56408SKazunori MIYAZAWA }
130c0d56408SKazunori MIYAZAWA #endif
131c0d56408SKazunori MIYAZAWA 
1328afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
1338afe97e5SSimon Horman static int tunnelmpls4_rcv(struct sk_buff *skb)
1348afe97e5SSimon Horman {
1358afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
1368afe97e5SSimon Horman 
1378afe97e5SSimon Horman 	if (!pskb_may_pull(skb, sizeof(struct mpls_label)))
1388afe97e5SSimon Horman 		goto drop;
1398afe97e5SSimon Horman 
1408afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
1418afe97e5SSimon Horman 		if (!handler->handler(skb))
1428afe97e5SSimon Horman 			return 0;
1438afe97e5SSimon Horman 
1448afe97e5SSimon Horman 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
1458afe97e5SSimon Horman 
1468afe97e5SSimon Horman drop:
1478afe97e5SSimon Horman 	kfree_skb(skb);
1488afe97e5SSimon Horman 	return 0;
1498afe97e5SSimon Horman }
1508afe97e5SSimon Horman #endif
1518afe97e5SSimon Horman 
152*32bbd879SStefano Brivio static int tunnel4_err(struct sk_buff *skb, u32 info)
153d2acc347SHerbert Xu {
154d2acc347SHerbert Xu 	struct xfrm_tunnel *handler;
155d2acc347SHerbert Xu 
156875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel4_handlers, handler)
157d2acc347SHerbert Xu 		if (!handler->err_handler(skb, info))
158*32bbd879SStefano Brivio 			return 0;
159*32bbd879SStefano Brivio 
160*32bbd879SStefano Brivio 	return -ENOENT;
161d2acc347SHerbert Xu }
162d2acc347SHerbert Xu 
163dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
164*32bbd879SStefano Brivio static int tunnel64_err(struct sk_buff *skb, u32 info)
16599f93326SPavel Emelyanov {
16699f93326SPavel Emelyanov 	struct xfrm_tunnel *handler;
16799f93326SPavel Emelyanov 
168875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel64_handlers, handler)
16999f93326SPavel Emelyanov 		if (!handler->err_handler(skb, info))
170*32bbd879SStefano Brivio 			return 0;
171*32bbd879SStefano Brivio 
172*32bbd879SStefano Brivio 	return -ENOENT;
17399f93326SPavel Emelyanov }
17499f93326SPavel Emelyanov #endif
17599f93326SPavel Emelyanov 
1768afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
177*32bbd879SStefano Brivio static int tunnelmpls4_err(struct sk_buff *skb, u32 info)
1788afe97e5SSimon Horman {
1798afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
1808afe97e5SSimon Horman 
1818afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
1828afe97e5SSimon Horman 		if (!handler->err_handler(skb, info))
183*32bbd879SStefano Brivio 			return 0;
184*32bbd879SStefano Brivio 
185*32bbd879SStefano Brivio 	return -ENOENT;
1868afe97e5SSimon Horman }
1878afe97e5SSimon Horman #endif
1888afe97e5SSimon Horman 
18932613090SAlexey Dobriyan static const struct net_protocol tunnel4_protocol = {
190d2acc347SHerbert Xu 	.handler	=	tunnel4_rcv,
191d2acc347SHerbert Xu 	.err_handler	=	tunnel4_err,
192d2acc347SHerbert Xu 	.no_policy	=	1,
1934597a0ceSPavel Emelyanov 	.netns_ok	=	1,
194d2acc347SHerbert Xu };
195d2acc347SHerbert Xu 
196dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
19732613090SAlexey Dobriyan static const struct net_protocol tunnel64_protocol = {
198c0d56408SKazunori MIYAZAWA 	.handler	=	tunnel64_rcv,
19999f93326SPavel Emelyanov 	.err_handler	=	tunnel64_err,
200c0d56408SKazunori MIYAZAWA 	.no_policy	=	1,
201b0970c42SPavel Emelyanov 	.netns_ok	=	1,
202c0d56408SKazunori MIYAZAWA };
203c0d56408SKazunori MIYAZAWA #endif
204c0d56408SKazunori MIYAZAWA 
2058afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2068afe97e5SSimon Horman static const struct net_protocol tunnelmpls4_protocol = {
2078afe97e5SSimon Horman 	.handler	=	tunnelmpls4_rcv,
2088afe97e5SSimon Horman 	.err_handler	=	tunnelmpls4_err,
2098afe97e5SSimon Horman 	.no_policy	=	1,
2108afe97e5SSimon Horman 	.netns_ok	=	1,
2118afe97e5SSimon Horman };
2128afe97e5SSimon Horman #endif
2138afe97e5SSimon Horman 
214d2acc347SHerbert Xu static int __init tunnel4_init(void)
215d2acc347SHerbert Xu {
2168afe97e5SSimon Horman 	if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
217aa9667e7SSimon Horman 		goto err;
218dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
219aa9667e7SSimon Horman 	if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) {
220aa9667e7SSimon Horman 		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
221aa9667e7SSimon Horman 		goto err;
222aa9667e7SSimon Horman 	}
2238afe97e5SSimon Horman #endif
2248afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
225aa9667e7SSimon Horman 	if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) {
226aa9667e7SSimon Horman 		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
227aa9667e7SSimon Horman #if IS_ENABLED(CONFIG_IPV6)
228aa9667e7SSimon Horman 		inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6);
229aa9667e7SSimon Horman #endif
230aa9667e7SSimon Horman 		goto err;
231aa9667e7SSimon Horman 	}
232c0d56408SKazunori MIYAZAWA #endif
233d2acc347SHerbert Xu 	return 0;
2348afe97e5SSimon Horman 
235aa9667e7SSimon Horman err:
2368afe97e5SSimon Horman 	pr_err("%s: can't add protocol\n", __func__);
2378afe97e5SSimon Horman 	return -EAGAIN;
238d2acc347SHerbert Xu }
239d2acc347SHerbert Xu 
240d2acc347SHerbert Xu static void __exit tunnel4_fini(void)
241d2acc347SHerbert Xu {
2428afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2438afe97e5SSimon Horman 	if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
2448afe97e5SSimon Horman 		pr_err("tunnelmpls4 close: can't remove protocol\n");
2458afe97e5SSimon Horman #endif
246dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
247c0d56408SKazunori MIYAZAWA 	if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
248058bd4d2SJoe Perches 		pr_err("tunnel64 close: can't remove protocol\n");
249c0d56408SKazunori MIYAZAWA #endif
250d2acc347SHerbert Xu 	if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
251058bd4d2SJoe Perches 		pr_err("tunnel4 close: can't remove protocol\n");
252d2acc347SHerbert Xu }
253d2acc347SHerbert Xu 
254d2acc347SHerbert Xu module_init(tunnel4_init);
255d2acc347SHerbert Xu module_exit(tunnel4_fini);
256d2acc347SHerbert Xu MODULE_LICENSE("GPL");
257