xref: /openbmc/linux/net/ipv4/tunnel4.c (revision 8afe97e5d4165c9d181d42504af3f96c8427659a)
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>
9*8afe97e5SSimon 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;
20*8afe97e5SSimon 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 {
25*8afe97e5SSimon Horman 	return (family == AF_INET) ? &tunnel4_handlers :
26*8afe97e5SSimon Horman 		(family == AF_INET6) ? &tunnel64_handlers :
27*8afe97e5SSimon 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 
132*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
133*8afe97e5SSimon Horman static int tunnelmpls4_rcv(struct sk_buff *skb)
134*8afe97e5SSimon Horman {
135*8afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
136*8afe97e5SSimon Horman 
137*8afe97e5SSimon Horman 	if (!pskb_may_pull(skb, sizeof(struct mpls_label)))
138*8afe97e5SSimon Horman 		goto drop;
139*8afe97e5SSimon Horman 
140*8afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
141*8afe97e5SSimon Horman 		if (!handler->handler(skb))
142*8afe97e5SSimon Horman 			return 0;
143*8afe97e5SSimon Horman 
144*8afe97e5SSimon Horman 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
145*8afe97e5SSimon Horman 
146*8afe97e5SSimon Horman drop:
147*8afe97e5SSimon Horman 	kfree_skb(skb);
148*8afe97e5SSimon Horman 	return 0;
149*8afe97e5SSimon Horman }
150*8afe97e5SSimon Horman #endif
151*8afe97e5SSimon Horman 
152d2acc347SHerbert Xu static void 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))
158d2acc347SHerbert Xu 			break;
159d2acc347SHerbert Xu }
160d2acc347SHerbert Xu 
161dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
16299f93326SPavel Emelyanov static void tunnel64_err(struct sk_buff *skb, u32 info)
16399f93326SPavel Emelyanov {
16499f93326SPavel Emelyanov 	struct xfrm_tunnel *handler;
16599f93326SPavel Emelyanov 
166875168a9SEric Dumazet 	for_each_tunnel_rcu(tunnel64_handlers, handler)
16799f93326SPavel Emelyanov 		if (!handler->err_handler(skb, info))
16899f93326SPavel Emelyanov 			break;
16999f93326SPavel Emelyanov }
17099f93326SPavel Emelyanov #endif
17199f93326SPavel Emelyanov 
172*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
173*8afe97e5SSimon Horman static void tunnelmpls4_err(struct sk_buff *skb, u32 info)
174*8afe97e5SSimon Horman {
175*8afe97e5SSimon Horman 	struct xfrm_tunnel *handler;
176*8afe97e5SSimon Horman 
177*8afe97e5SSimon Horman 	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
178*8afe97e5SSimon Horman 		if (!handler->err_handler(skb, info))
179*8afe97e5SSimon Horman 			break;
180*8afe97e5SSimon Horman }
181*8afe97e5SSimon Horman #endif
182*8afe97e5SSimon Horman 
18332613090SAlexey Dobriyan static const struct net_protocol tunnel4_protocol = {
184d2acc347SHerbert Xu 	.handler	=	tunnel4_rcv,
185d2acc347SHerbert Xu 	.err_handler	=	tunnel4_err,
186d2acc347SHerbert Xu 	.no_policy	=	1,
1874597a0ceSPavel Emelyanov 	.netns_ok	=	1,
188d2acc347SHerbert Xu };
189d2acc347SHerbert Xu 
190dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
19132613090SAlexey Dobriyan static const struct net_protocol tunnel64_protocol = {
192c0d56408SKazunori MIYAZAWA 	.handler	=	tunnel64_rcv,
19399f93326SPavel Emelyanov 	.err_handler	=	tunnel64_err,
194c0d56408SKazunori MIYAZAWA 	.no_policy	=	1,
195b0970c42SPavel Emelyanov 	.netns_ok	=	1,
196c0d56408SKazunori MIYAZAWA };
197c0d56408SKazunori MIYAZAWA #endif
198c0d56408SKazunori MIYAZAWA 
199*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
200*8afe97e5SSimon Horman static const struct net_protocol tunnelmpls4_protocol = {
201*8afe97e5SSimon Horman 	.handler	=	tunnelmpls4_rcv,
202*8afe97e5SSimon Horman 	.err_handler	=	tunnelmpls4_err,
203*8afe97e5SSimon Horman 	.no_policy	=	1,
204*8afe97e5SSimon Horman 	.netns_ok	=	1,
205*8afe97e5SSimon Horman };
206*8afe97e5SSimon Horman #endif
207*8afe97e5SSimon Horman 
208d2acc347SHerbert Xu static int __init tunnel4_init(void)
209d2acc347SHerbert Xu {
210*8afe97e5SSimon Horman 	if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
211*8afe97e5SSimon Horman 		goto err_ipip;
212dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
213*8afe97e5SSimon Horman 	if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6))
214*8afe97e5SSimon Horman 		goto err_ipv6;
215*8afe97e5SSimon Horman #endif
216*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
217*8afe97e5SSimon Horman 	if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
218*8afe97e5SSimon Horman 		goto err_mpls;
219c0d56408SKazunori MIYAZAWA #endif
220d2acc347SHerbert Xu 	return 0;
221*8afe97e5SSimon Horman 
222*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_IPV6)
223*8afe97e5SSimon Horman err_mpls:
224*8afe97e5SSimon Horman 	inet_del_protocol(&tunnel4_protocol, IPPROTO_IPV6);
225*8afe97e5SSimon Horman #endif
226*8afe97e5SSimon Horman err_ipv6:
227*8afe97e5SSimon Horman 	inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
228*8afe97e5SSimon Horman err_ipip:
229*8afe97e5SSimon Horman 	pr_err("%s: can't add protocol\n", __func__);
230*8afe97e5SSimon Horman 	return -EAGAIN;
231d2acc347SHerbert Xu }
232d2acc347SHerbert Xu 
233d2acc347SHerbert Xu static void __exit tunnel4_fini(void)
234d2acc347SHerbert Xu {
235*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
236*8afe97e5SSimon Horman 	if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
237*8afe97e5SSimon Horman 		pr_err("tunnelmpls4 close: can't remove protocol\n");
238*8afe97e5SSimon Horman #endif
239dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
240c0d56408SKazunori MIYAZAWA 	if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
241058bd4d2SJoe Perches 		pr_err("tunnel64 close: can't remove protocol\n");
242c0d56408SKazunori MIYAZAWA #endif
243d2acc347SHerbert Xu 	if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
244058bd4d2SJoe Perches 		pr_err("tunnel4 close: can't remove protocol\n");
245d2acc347SHerbert Xu }
246d2acc347SHerbert Xu 
247d2acc347SHerbert Xu module_init(tunnel4_init);
248d2acc347SHerbert Xu module_exit(tunnel4_fini);
249d2acc347SHerbert Xu MODULE_LICENSE("GPL");
250