xref: /openbmc/linux/net/ipv4/tunnel4.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d2acc347SHerbert Xu /* tunnel4.c: Generic IP tunnel transformer.
3d2acc347SHerbert Xu  *
4d2acc347SHerbert Xu  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
5d2acc347SHerbert Xu  */
6d2acc347SHerbert Xu 
7d2acc347SHerbert Xu #include <linux/init.h>
8d2acc347SHerbert Xu #include <linux/module.h>
9d2acc347SHerbert Xu #include <linux/mutex.h>
108afe97e5SSimon Horman #include <linux/mpls.h>
11d2acc347SHerbert Xu #include <linux/netdevice.h>
12d2acc347SHerbert Xu #include <linux/skbuff.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
1450fba2aaSHerbert Xu #include <net/icmp.h>
1550fba2aaSHerbert Xu #include <net/ip.h>
16d2acc347SHerbert Xu #include <net/protocol.h>
17d2acc347SHerbert Xu #include <net/xfrm.h>
18d2acc347SHerbert Xu 
19b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly;
20b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly;
218afe97e5SSimon Horman static struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly;
22d2acc347SHerbert Xu static DEFINE_MUTEX(tunnel4_mutex);
23d2acc347SHerbert Xu 
fam_handlers(unsigned short family)24b33eab08SEric Dumazet static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
25358352b8SPavel Emelyanov {
268afe97e5SSimon Horman 	return (family == AF_INET) ? &tunnel4_handlers :
278afe97e5SSimon Horman 		(family == AF_INET6) ? &tunnel64_handlers :
288afe97e5SSimon Horman 		&tunnelmpls4_handlers;
29358352b8SPavel Emelyanov }
30358352b8SPavel Emelyanov 
xfrm4_tunnel_register(struct xfrm_tunnel * handler,unsigned short family)31c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
32d2acc347SHerbert Xu {
33b33eab08SEric Dumazet 	struct xfrm_tunnel __rcu **pprev;
34b33eab08SEric Dumazet 	struct xfrm_tunnel *t;
35b33eab08SEric Dumazet 
36d2acc347SHerbert Xu 	int ret = -EEXIST;
37d2acc347SHerbert Xu 	int priority = handler->priority;
38d2acc347SHerbert Xu 
39d2acc347SHerbert Xu 	mutex_lock(&tunnel4_mutex);
40d2acc347SHerbert Xu 
41b33eab08SEric Dumazet 	for (pprev = fam_handlers(family);
42b33eab08SEric Dumazet 	     (t = rcu_dereference_protected(*pprev,
43b33eab08SEric Dumazet 			lockdep_is_held(&tunnel4_mutex))) != NULL;
44b33eab08SEric Dumazet 	     pprev = &t->next) {
45b33eab08SEric Dumazet 		if (t->priority > priority)
46d2acc347SHerbert Xu 			break;
47b33eab08SEric Dumazet 		if (t->priority == priority)
48d2acc347SHerbert Xu 			goto err;
49d2acc347SHerbert Xu 	}
50d2acc347SHerbert Xu 
51d2acc347SHerbert Xu 	handler->next = *pprev;
5249d61e23SEric Dumazet 	rcu_assign_pointer(*pprev, handler);
53d2acc347SHerbert Xu 
54d2acc347SHerbert Xu 	ret = 0;
55d2acc347SHerbert Xu 
56d2acc347SHerbert Xu err:
57d2acc347SHerbert Xu 	mutex_unlock(&tunnel4_mutex);
58d2acc347SHerbert Xu 
59d2acc347SHerbert Xu 	return ret;
60d2acc347SHerbert Xu }
61d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_register);
62d2acc347SHerbert Xu 
xfrm4_tunnel_deregister(struct xfrm_tunnel * handler,unsigned short family)63c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family)
64d2acc347SHerbert Xu {
65b33eab08SEric Dumazet 	struct xfrm_tunnel __rcu **pprev;
66b33eab08SEric Dumazet 	struct xfrm_tunnel *t;
67d2acc347SHerbert Xu 	int ret = -ENOENT;
68d2acc347SHerbert Xu 
69d2acc347SHerbert Xu 	mutex_lock(&tunnel4_mutex);
70d2acc347SHerbert Xu 
71b33eab08SEric Dumazet 	for (pprev = fam_handlers(family);
72b33eab08SEric Dumazet 	     (t = rcu_dereference_protected(*pprev,
73b33eab08SEric Dumazet 			lockdep_is_held(&tunnel4_mutex))) != NULL;
74b33eab08SEric Dumazet 	     pprev = &t->next) {
75b33eab08SEric Dumazet 		if (t == handler) {
76d2acc347SHerbert Xu 			*pprev = handler->next;
77d2acc347SHerbert Xu 			ret = 0;
78d2acc347SHerbert Xu 			break;
79d2acc347SHerbert Xu 		}
80d2acc347SHerbert Xu 	}
81d2acc347SHerbert Xu 
82d2acc347SHerbert Xu 	mutex_unlock(&tunnel4_mutex);
83d2acc347SHerbert Xu 
84d2acc347SHerbert Xu 	synchronize_net();
85d2acc347SHerbert Xu 
86d2acc347SHerbert Xu 	return ret;
87d2acc347SHerbert Xu }
88d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_deregister);
89d2acc347SHerbert Xu 
90875168a9SEric Dumazet #define for_each_tunnel_rcu(head, handler)		\
91875168a9SEric Dumazet 	for (handler = rcu_dereference(head);		\
92875168a9SEric Dumazet 	     handler != NULL;				\
93875168a9SEric Dumazet 	     handler = rcu_dereference(handler->next))	\
94875168a9SEric Dumazet 
tunnel4_rcv(struct sk_buff * skb)95d2acc347SHerbert Xu static int tunnel4_rcv(struct sk_buff *skb)
96d2acc347SHerbert Xu {
97d2acc347SHerbert Xu 	struct xfrm_tunnel *handler;
98d2acc347SHerbert Xu 
9950fba2aaSHerbert Xu 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
10050fba2aaSHerbert Xu 		goto drop;
10150fba2aaSHerbert Xu 
102875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel4_handlers, handler)
103d2acc347SHerbert Xu 		if (!handler->handler(skb))
104d2acc347SHerbert Xu 			return 0;
105d2acc347SHerbert Xu 
10650fba2aaSHerbert Xu 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
10750fba2aaSHerbert Xu 
10850fba2aaSHerbert Xu drop:
109d2acc347SHerbert Xu 	kfree_skb(skb);
110d2acc347SHerbert Xu 	return 0;
111d2acc347SHerbert Xu }
112d2acc347SHerbert Xu 
113*6df2db5dSXin Long #if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
tunnel4_rcv_cb(struct sk_buff * skb,u8 proto,int err)114*6df2db5dSXin Long static int tunnel4_rcv_cb(struct sk_buff *skb, u8 proto, int err)
115*6df2db5dSXin Long {
116*6df2db5dSXin Long 	struct xfrm_tunnel __rcu *head;
117*6df2db5dSXin Long 	struct xfrm_tunnel *handler;
118*6df2db5dSXin Long 	int ret;
119*6df2db5dSXin Long 
120*6df2db5dSXin Long 	head = (proto == IPPROTO_IPIP) ? tunnel4_handlers : tunnel64_handlers;
121*6df2db5dSXin Long 
122*6df2db5dSXin Long 	for_each_tunnel_rcu(head, handler) {
123*6df2db5dSXin Long 		if (handler->cb_handler) {
124*6df2db5dSXin Long 			ret = handler->cb_handler(skb, err);
125*6df2db5dSXin Long 			if (ret <= 0)
126*6df2db5dSXin Long 				return ret;
127*6df2db5dSXin Long 		}
128*6df2db5dSXin Long 	}
129*6df2db5dSXin Long 
130*6df2db5dSXin Long 	return 0;
131*6df2db5dSXin Long }
132*6df2db5dSXin Long 
133*6df2db5dSXin Long static const struct xfrm_input_afinfo tunnel4_input_afinfo = {
134*6df2db5dSXin Long 	.family		=	AF_INET,
135*6df2db5dSXin Long 	.is_ipip	=	true,
136*6df2db5dSXin Long 	.callback	=	tunnel4_rcv_cb,
137*6df2db5dSXin Long };
138*6df2db5dSXin Long #endif
139*6df2db5dSXin Long 
140dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
tunnel64_rcv(struct sk_buff * skb)141c0d56408SKazunori MIYAZAWA static int tunnel64_rcv(struct sk_buff *skb)
142c0d56408SKazunori MIYAZAWA {
143c0d56408SKazunori MIYAZAWA 	struct xfrm_tunnel *handler;
144c0d56408SKazunori MIYAZAWA 
145baa2bfb8SYOSHIFUJI Hideaki 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
146c0d56408SKazunori MIYAZAWA 		goto drop;
147c0d56408SKazunori MIYAZAWA 
148875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel64_handlers, handler)
149c0d56408SKazunori MIYAZAWA 		if (!handler->handler(skb))
150c0d56408SKazunori MIYAZAWA 			return 0;
151c0d56408SKazunori MIYAZAWA 
152c0d56408SKazunori MIYAZAWA 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
153c0d56408SKazunori MIYAZAWA 
154c0d56408SKazunori MIYAZAWA drop:
155c0d56408SKazunori MIYAZAWA 	kfree_skb(skb);
156c0d56408SKazunori MIYAZAWA 	return 0;
157c0d56408SKazunori MIYAZAWA }
158c0d56408SKazunori MIYAZAWA #endif
159c0d56408SKazunori MIYAZAWA 
1608afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
tunnelmpls4_rcv(struct sk_buff * skb)1618afe97e5SSimon Horman static int tunnelmpls4_rcv(struct sk_buff *skb)
1628afe97e5SSimon Horman {
1638afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
1648afe97e5SSimon Horman 
1658afe97e5SSimon Horman 	if (!pskb_may_pull(skb, sizeof(struct mpls_label)))
1668afe97e5SSimon Horman 		goto drop;
1678afe97e5SSimon Horman 
1688afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
1698afe97e5SSimon Horman 		if (!handler->handler(skb))
1708afe97e5SSimon Horman 			return 0;
1718afe97e5SSimon Horman 
1728afe97e5SSimon Horman 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
1738afe97e5SSimon Horman 
1748afe97e5SSimon Horman drop:
1758afe97e5SSimon Horman 	kfree_skb(skb);
1768afe97e5SSimon Horman 	return 0;
1778afe97e5SSimon Horman }
1788afe97e5SSimon Horman #endif
1798afe97e5SSimon Horman 
tunnel4_err(struct sk_buff * skb,u32 info)18032bbd879SStefano Brivio static int tunnel4_err(struct sk_buff *skb, u32 info)
181d2acc347SHerbert Xu {
182d2acc347SHerbert Xu 	struct xfrm_tunnel *handler;
183d2acc347SHerbert Xu 
184875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel4_handlers, handler)
185d2acc347SHerbert Xu 		if (!handler->err_handler(skb, info))
18632bbd879SStefano Brivio 			return 0;
18732bbd879SStefano Brivio 
18832bbd879SStefano Brivio 	return -ENOENT;
189d2acc347SHerbert Xu }
190d2acc347SHerbert Xu 
191dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
tunnel64_err(struct sk_buff * skb,u32 info)19232bbd879SStefano Brivio static int tunnel64_err(struct sk_buff *skb, u32 info)
19399f93326SPavel Emelyanov {
19499f93326SPavel Emelyanov 	struct xfrm_tunnel *handler;
19599f93326SPavel Emelyanov 
196875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel64_handlers, handler)
19799f93326SPavel Emelyanov 		if (!handler->err_handler(skb, info))
19832bbd879SStefano Brivio 			return 0;
19932bbd879SStefano Brivio 
20032bbd879SStefano Brivio 	return -ENOENT;
20199f93326SPavel Emelyanov }
20299f93326SPavel Emelyanov #endif
20399f93326SPavel Emelyanov 
2048afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
tunnelmpls4_err(struct sk_buff * skb,u32 info)20532bbd879SStefano Brivio static int tunnelmpls4_err(struct sk_buff *skb, u32 info)
2068afe97e5SSimon Horman {
2078afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
2088afe97e5SSimon Horman 
2098afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
2108afe97e5SSimon Horman 		if (!handler->err_handler(skb, info))
21132bbd879SStefano Brivio 			return 0;
21232bbd879SStefano Brivio 
21332bbd879SStefano Brivio 	return -ENOENT;
2148afe97e5SSimon Horman }
2158afe97e5SSimon Horman #endif
2168afe97e5SSimon Horman 
21732613090SAlexey Dobriyan static const struct net_protocol tunnel4_protocol = {
218d2acc347SHerbert Xu 	.handler	=	tunnel4_rcv,
219d2acc347SHerbert Xu 	.err_handler	=	tunnel4_err,
220d2acc347SHerbert Xu 	.no_policy	=	1,
221d2acc347SHerbert Xu };
222d2acc347SHerbert Xu 
223dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
22432613090SAlexey Dobriyan static const struct net_protocol tunnel64_protocol = {
225c0d56408SKazunori MIYAZAWA 	.handler	=	tunnel64_rcv,
22699f93326SPavel Emelyanov 	.err_handler	=	tunnel64_err,
227c0d56408SKazunori MIYAZAWA 	.no_policy	=	1,
228c0d56408SKazunori MIYAZAWA };
229c0d56408SKazunori MIYAZAWA #endif
230c0d56408SKazunori MIYAZAWA 
2318afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2328afe97e5SSimon Horman static const struct net_protocol tunnelmpls4_protocol = {
2338afe97e5SSimon Horman 	.handler	=	tunnelmpls4_rcv,
2348afe97e5SSimon Horman 	.err_handler	=	tunnelmpls4_err,
2358afe97e5SSimon Horman 	.no_policy	=	1,
2368afe97e5SSimon Horman };
2378afe97e5SSimon Horman #endif
2388afe97e5SSimon Horman 
tunnel4_init(void)239d2acc347SHerbert Xu static int __init tunnel4_init(void)
240d2acc347SHerbert Xu {
2418afe97e5SSimon Horman 	if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
242aa9667e7SSimon Horman 		goto err;
243dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
244aa9667e7SSimon Horman 	if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) {
245aa9667e7SSimon Horman 		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
246aa9667e7SSimon Horman 		goto err;
247aa9667e7SSimon Horman 	}
2488afe97e5SSimon Horman #endif
2498afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
250aa9667e7SSimon Horman 	if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) {
251aa9667e7SSimon Horman 		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
252aa9667e7SSimon Horman #if IS_ENABLED(CONFIG_IPV6)
253aa9667e7SSimon Horman 		inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6);
254aa9667e7SSimon Horman #endif
255aa9667e7SSimon Horman 		goto err;
256aa9667e7SSimon Horman 	}
257c0d56408SKazunori MIYAZAWA #endif
258*6df2db5dSXin Long #if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
259*6df2db5dSXin Long 	if (xfrm_input_register_afinfo(&tunnel4_input_afinfo)) {
260*6df2db5dSXin Long 		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
261*6df2db5dSXin Long #if IS_ENABLED(CONFIG_IPV6)
262*6df2db5dSXin Long 		inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6);
263*6df2db5dSXin Long #endif
264*6df2db5dSXin Long #if IS_ENABLED(CONFIG_MPLS)
265*6df2db5dSXin Long 		inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS);
266*6df2db5dSXin Long #endif
267*6df2db5dSXin Long 		goto err;
268*6df2db5dSXin Long 	}
269*6df2db5dSXin Long #endif
270d2acc347SHerbert Xu 	return 0;
2718afe97e5SSimon Horman 
272aa9667e7SSimon Horman err:
2738afe97e5SSimon Horman 	pr_err("%s: can't add protocol\n", __func__);
2748afe97e5SSimon Horman 	return -EAGAIN;
275d2acc347SHerbert Xu }
276d2acc347SHerbert Xu 
tunnel4_fini(void)277d2acc347SHerbert Xu static void __exit tunnel4_fini(void)
278d2acc347SHerbert Xu {
279*6df2db5dSXin Long #if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
280*6df2db5dSXin Long 	if (xfrm_input_unregister_afinfo(&tunnel4_input_afinfo))
281*6df2db5dSXin Long 		pr_err("tunnel4 close: can't remove input afinfo\n");
282*6df2db5dSXin Long #endif
2838afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2848afe97e5SSimon Horman 	if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
2858afe97e5SSimon Horman 		pr_err("tunnelmpls4 close: can't remove protocol\n");
2868afe97e5SSimon Horman #endif
287dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
288c0d56408SKazunori MIYAZAWA 	if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
289058bd4d2SJoe Perches 		pr_err("tunnel64 close: can't remove protocol\n");
290c0d56408SKazunori MIYAZAWA #endif
291d2acc347SHerbert Xu 	if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
292058bd4d2SJoe Perches 		pr_err("tunnel4 close: can't remove protocol\n");
293d2acc347SHerbert Xu }
294d2acc347SHerbert Xu 
295d2acc347SHerbert Xu module_init(tunnel4_init);
296d2acc347SHerbert Xu module_exit(tunnel4_fini);
297d2acc347SHerbert Xu MODULE_LICENSE("GPL");
298