1d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu/* tunnel4.c: Generic IP tunnel transformer.
2d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu *
3d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu * Copyright (C) 2003 David S. Miller (davem@redhat.com)
4d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu */
5d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
6d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <linux/init.h>
7d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <linux/module.h>
8d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <linux/mutex.h>
9d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <linux/netdevice.h>
10d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <linux/skbuff.h>
115a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
1250fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu#include <net/icmp.h>
1350fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu#include <net/ip.h>
14d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <net/protocol.h>
15d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu#include <net/xfrm.h>
16d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
17b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazetstatic struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly;
18b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazetstatic struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly;
19d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xustatic DEFINE_MUTEX(tunnel4_mutex);
20d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
21b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazetstatic inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
22358352b8b83c67ecf75f6d7bc3e2d64bf0cf506aPavel Emelyanov{
23358352b8b83c67ecf75f6d7bc3e2d64bf0cf506aPavel Emelyanov	return (family == AF_INET) ? &tunnel4_handlers : &tunnel64_handlers;
24358352b8b83c67ecf75f6d7bc3e2d64bf0cf506aPavel Emelyanov}
25358352b8b83c67ecf75f6d7bc3e2d64bf0cf506aPavel Emelyanov
26c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWAint xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
27d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
28b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	struct xfrm_tunnel __rcu **pprev;
29b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	struct xfrm_tunnel *t;
30b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet
31d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	int ret = -EEXIST;
32d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	int priority = handler->priority;
33d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
34d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	mutex_lock(&tunnel4_mutex);
35d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
36b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	for (pprev = fam_handlers(family);
37b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	     (t = rcu_dereference_protected(*pprev,
38b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet			lockdep_is_held(&tunnel4_mutex))) != NULL;
39b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	     pprev = &t->next) {
40b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet		if (t->priority > priority)
41d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			break;
42b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet		if (t->priority == priority)
43d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			goto err;
44d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	}
45d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
46d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	handler->next = *pprev;
4749d61e2390c92bd226fc395a6165eb5a65ae4de6Eric Dumazet	rcu_assign_pointer(*pprev, handler);
48d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
49d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	ret = 0;
50d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
51d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xuerr:
52d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	mutex_unlock(&tunnel4_mutex);
53d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
54d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	return ret;
55d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
56d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert XuEXPORT_SYMBOL(xfrm4_tunnel_register);
57d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
58c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWAint xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family)
59d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
60b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	struct xfrm_tunnel __rcu **pprev;
61b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	struct xfrm_tunnel *t;
62d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	int ret = -ENOENT;
63d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
64d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	mutex_lock(&tunnel4_mutex);
65d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
66b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	for (pprev = fam_handlers(family);
67b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	     (t = rcu_dereference_protected(*pprev,
68b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet			lockdep_is_held(&tunnel4_mutex))) != NULL;
69b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet	     pprev = &t->next) {
70b33eab08445d86c3d0dec3111ce10df561328705Eric Dumazet		if (t == handler) {
71d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			*pprev = handler->next;
72d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			ret = 0;
73d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			break;
74d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu		}
75d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	}
76d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
77d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	mutex_unlock(&tunnel4_mutex);
78d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
79d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	synchronize_net();
80d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
81d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	return ret;
82d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
83d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert XuEXPORT_SYMBOL(xfrm4_tunnel_deregister);
84d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
85875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet#define for_each_tunnel_rcu(head, handler)		\
86875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	for (handler = rcu_dereference(head);		\
87875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	     handler != NULL;				\
88875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	     handler = rcu_dereference(handler->next))	\
89875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet
90d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xustatic int tunnel4_rcv(struct sk_buff *skb)
91d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
92d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	struct xfrm_tunnel *handler;
93d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
9450fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
9550fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu		goto drop;
9650fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu
97875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	for_each_tunnel_rcu(tunnel4_handlers, handler)
98d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu		if (!handler->handler(skb))
99d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			return 0;
100d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
10150fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
10250fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xu
10350fba2aa7cefa6b0e1768cb350c9e69042320c03Herbert Xudrop:
104d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	kfree_skb(skb);
105d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	return 0;
106d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
107d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
108dfd56b8b38fff3586f36232db58e1e9f7885a605Eric Dumazet#if IS_ENABLED(CONFIG_IPV6)
109c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWAstatic int tunnel64_rcv(struct sk_buff *skb)
110c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA{
111c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	struct xfrm_tunnel *handler;
112c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
113baa2bfb8aef24bb7fe1875b256918724b3884662YOSHIFUJI Hideaki	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
114c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA		goto drop;
115c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
116875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	for_each_tunnel_rcu(tunnel64_handlers, handler)
117c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA		if (!handler->handler(skb))
118c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA			return 0;
119c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
120c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
121c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
122c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWAdrop:
123c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	kfree_skb(skb);
124c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	return 0;
125c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA}
126c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA#endif
127c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
128d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xustatic void tunnel4_err(struct sk_buff *skb, u32 info)
129d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
130d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	struct xfrm_tunnel *handler;
131d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
132875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	for_each_tunnel_rcu(tunnel4_handlers, handler)
133d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu		if (!handler->err_handler(skb, info))
134d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu			break;
135d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
136d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
137dfd56b8b38fff3586f36232db58e1e9f7885a605Eric Dumazet#if IS_ENABLED(CONFIG_IPV6)
13899f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanovstatic void tunnel64_err(struct sk_buff *skb, u32 info)
13999f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov{
14099f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov	struct xfrm_tunnel *handler;
14199f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov
142875168a9330d3aa6481ce62ce8fa77c7be0c75fbEric Dumazet	for_each_tunnel_rcu(tunnel64_handlers, handler)
14399f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov		if (!handler->err_handler(skb, info))
14499f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov			break;
14599f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov}
14699f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov#endif
14799f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov
14832613090a96dba2ca2cc524c8d4749d3126fdde5Alexey Dobriyanstatic const struct net_protocol tunnel4_protocol = {
149d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	.handler	=	tunnel4_rcv,
150d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	.err_handler	=	tunnel4_err,
151d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	.no_policy	=	1,
1524597a0ce0849eaa62fc9083c21943e0c434e4135Pavel Emelyanov	.netns_ok	=	1,
153d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu};
154d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
155dfd56b8b38fff3586f36232db58e1e9f7885a605Eric Dumazet#if IS_ENABLED(CONFIG_IPV6)
15632613090a96dba2ca2cc524c8d4749d3126fdde5Alexey Dobriyanstatic const struct net_protocol tunnel64_protocol = {
157c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	.handler	=	tunnel64_rcv,
15899f933263ac30eafbb008d01ac1dd0adf40fc343Pavel Emelyanov	.err_handler	=	tunnel64_err,
159c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	.no_policy	=	1,
160b0970c428b33ee6e0aa576f58f87dde74262a441Pavel Emelyanov	.netns_ok	=	1,
161c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA};
162c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA#endif
163c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA
164d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xustatic int __init tunnel4_init(void)
165d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
166d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) {
167058bd4d2a4ff0aaa4a5381c67e776729d840c785Joe Perches		pr_err("%s: can't add protocol\n", __func__);
168d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu		return -EAGAIN;
169d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	}
170dfd56b8b38fff3586f36232db58e1e9f7885a605Eric Dumazet#if IS_ENABLED(CONFIG_IPV6)
171c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) {
172058bd4d2a4ff0aaa4a5381c67e776729d840c785Joe Perches		pr_err("tunnel64 init: can't add protocol\n");
173c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
174c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA		return -EAGAIN;
175c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	}
176c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA#endif
177d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	return 0;
178d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
179d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
180d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xustatic void __exit tunnel4_fini(void)
181d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu{
182dfd56b8b38fff3586f36232db58e1e9f7885a605Eric Dumazet#if IS_ENABLED(CONFIG_IPV6)
183c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA	if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
184058bd4d2a4ff0aaa4a5381c67e776729d840c785Joe Perches		pr_err("tunnel64 close: can't remove protocol\n");
185c0d56408e3ff52d635441e0f08d12164a63728cfKazunori MIYAZAWA#endif
186d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu	if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
187058bd4d2a4ff0aaa4a5381c67e776729d840c785Joe Perches		pr_err("tunnel4 close: can't remove protocol\n");
188d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu}
189d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xu
190d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xumodule_init(tunnel4_init);
191d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert Xumodule_exit(tunnel4_fini);
192d2acc3479cbccd5cfbca6c787be713ef1de12ec6Herbert XuMODULE_LICENSE("GPL");
193