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