]>
Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d2acc347 HX |
2 | /* |
3 | * Copyright (C)2003,2004 USAGI/WIDE Project | |
4 | * | |
d2acc347 | 5 | * Authors Mitsuru KANDA <[email protected]> |
67ba4152 | 6 | * YOSHIFUJI Hideaki <[email protected]> |
d2acc347 HX |
7 | */ |
8 | ||
f3213831 JP |
9 | #define pr_fmt(fmt) "IPv6: " fmt |
10 | ||
50fba2aa | 11 | #include <linux/icmpv6.h> |
d2acc347 HX |
12 | #include <linux/init.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/mutex.h> | |
15 | #include <linux/netdevice.h> | |
16 | #include <linux/skbuff.h> | |
5a0e3ad6 | 17 | #include <linux/slab.h> |
50fba2aa | 18 | #include <net/ipv6.h> |
d2acc347 HX |
19 | #include <net/protocol.h> |
20 | #include <net/xfrm.h> | |
21 | ||
6f0bcf15 ED |
22 | static struct xfrm6_tunnel __rcu *tunnel6_handlers __read_mostly; |
23 | static struct xfrm6_tunnel __rcu *tunnel46_handlers __read_mostly; | |
f234efac | 24 | static struct xfrm6_tunnel __rcu *tunnelmpls6_handlers __read_mostly; |
d2acc347 HX |
25 | static DEFINE_MUTEX(tunnel6_mutex); |
26 | ||
f234efac VF |
27 | static inline int xfrm6_tunnel_mpls_supported(void) |
28 | { | |
29 | return IS_ENABLED(CONFIG_MPLS); | |
30 | } | |
31 | ||
73d605d1 | 32 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) |
d2acc347 | 33 | { |
6f0bcf15 ED |
34 | struct xfrm6_tunnel __rcu **pprev; |
35 | struct xfrm6_tunnel *t; | |
d2acc347 HX |
36 | int ret = -EEXIST; |
37 | int priority = handler->priority; | |
38 | ||
39 | mutex_lock(&tunnel6_mutex); | |
40 | ||
f234efac VF |
41 | switch (family) { |
42 | case AF_INET6: | |
43 | pprev = &tunnel6_handlers; | |
44 | break; | |
45 | case AF_INET: | |
46 | pprev = &tunnel46_handlers; | |
47 | break; | |
48 | case AF_MPLS: | |
49 | pprev = &tunnelmpls6_handlers; | |
50 | break; | |
51 | default: | |
52 | goto err; | |
53 | } | |
54 | ||
55 | for (; (t = rcu_dereference_protected(*pprev, | |
6f0bcf15 ED |
56 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
57 | pprev = &t->next) { | |
58 | if (t->priority > priority) | |
d2acc347 | 59 | break; |
6f0bcf15 | 60 | if (t->priority == priority) |
d2acc347 HX |
61 | goto err; |
62 | } | |
63 | ||
64 | handler->next = *pprev; | |
49d61e23 | 65 | rcu_assign_pointer(*pprev, handler); |
d2acc347 HX |
66 | |
67 | ret = 0; | |
68 | ||
69 | err: | |
70 | mutex_unlock(&tunnel6_mutex); | |
71 | ||
72 | return ret; | |
73 | } | |
d2acc347 HX |
74 | EXPORT_SYMBOL(xfrm6_tunnel_register); |
75 | ||
73d605d1 | 76 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) |
d2acc347 | 77 | { |
6f0bcf15 ED |
78 | struct xfrm6_tunnel __rcu **pprev; |
79 | struct xfrm6_tunnel *t; | |
d2acc347 HX |
80 | int ret = -ENOENT; |
81 | ||
82 | mutex_lock(&tunnel6_mutex); | |
83 | ||
f234efac VF |
84 | switch (family) { |
85 | case AF_INET6: | |
86 | pprev = &tunnel6_handlers; | |
87 | break; | |
88 | case AF_INET: | |
89 | pprev = &tunnel46_handlers; | |
90 | break; | |
91 | case AF_MPLS: | |
92 | pprev = &tunnelmpls6_handlers; | |
93 | break; | |
94 | default: | |
95 | goto err; | |
96 | } | |
97 | ||
98 | for (; (t = rcu_dereference_protected(*pprev, | |
6f0bcf15 ED |
99 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
100 | pprev = &t->next) { | |
101 | if (t == handler) { | |
d2acc347 HX |
102 | *pprev = handler->next; |
103 | ret = 0; | |
104 | break; | |
105 | } | |
106 | } | |
107 | ||
f234efac | 108 | err: |
d2acc347 HX |
109 | mutex_unlock(&tunnel6_mutex); |
110 | ||
111 | synchronize_net(); | |
112 | ||
113 | return ret; | |
114 | } | |
d2acc347 HX |
115 | EXPORT_SYMBOL(xfrm6_tunnel_deregister); |
116 | ||
875168a9 ED |
117 | #define for_each_tunnel_rcu(head, handler) \ |
118 | for (handler = rcu_dereference(head); \ | |
119 | handler != NULL; \ | |
120 | handler = rcu_dereference(handler->next)) \ | |
121 | ||
f234efac VF |
122 | static int tunnelmpls6_rcv(struct sk_buff *skb) |
123 | { | |
124 | struct xfrm6_tunnel *handler; | |
125 | ||
126 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | |
127 | goto drop; | |
128 | ||
129 | for_each_tunnel_rcu(tunnelmpls6_handlers, handler) | |
130 | if (!handler->handler(skb)) | |
131 | return 0; | |
132 | ||
133 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | |
134 | ||
135 | drop: | |
136 | kfree_skb(skb); | |
137 | return 0; | |
138 | } | |
139 | ||
e5bbef20 | 140 | static int tunnel6_rcv(struct sk_buff *skb) |
d2acc347 | 141 | { |
d2acc347 HX |
142 | struct xfrm6_tunnel *handler; |
143 | ||
50fba2aa HX |
144 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) |
145 | goto drop; | |
146 | ||
875168a9 | 147 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
d2acc347 HX |
148 | if (!handler->handler(skb)) |
149 | return 0; | |
150 | ||
3ffe533c | 151 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); |
50fba2aa HX |
152 | |
153 | drop: | |
d2acc347 HX |
154 | kfree_skb(skb); |
155 | return 0; | |
156 | } | |
157 | ||
86afc703 XL |
158 | #if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) |
159 | static int tunnel6_rcv_cb(struct sk_buff *skb, u8 proto, int err) | |
160 | { | |
161 | struct xfrm6_tunnel __rcu *head; | |
162 | struct xfrm6_tunnel *handler; | |
163 | int ret; | |
164 | ||
165 | head = (proto == IPPROTO_IPV6) ? tunnel6_handlers : tunnel46_handlers; | |
166 | ||
167 | for_each_tunnel_rcu(head, handler) { | |
168 | if (handler->cb_handler) { | |
169 | ret = handler->cb_handler(skb, err); | |
170 | if (ret <= 0) | |
171 | return ret; | |
172 | } | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | static const struct xfrm_input_afinfo tunnel6_input_afinfo = { | |
179 | .family = AF_INET6, | |
180 | .is_ipip = true, | |
181 | .callback = tunnel6_rcv_cb, | |
182 | }; | |
183 | #endif | |
184 | ||
e5bbef20 | 185 | static int tunnel46_rcv(struct sk_buff *skb) |
73d605d1 | 186 | { |
73d605d1 KM |
187 | struct xfrm6_tunnel *handler; |
188 | ||
82836372 | 189 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) |
73d605d1 KM |
190 | goto drop; |
191 | ||
875168a9 | 192 | for_each_tunnel_rcu(tunnel46_handlers, handler) |
73d605d1 KM |
193 | if (!handler->handler(skb)) |
194 | return 0; | |
195 | ||
3ffe533c | 196 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); |
73d605d1 KM |
197 | |
198 | drop: | |
199 | kfree_skb(skb); | |
200 | return 0; | |
201 | } | |
202 | ||
32bbd879 | 203 | static int tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
d5fdd6ba | 204 | u8 type, u8 code, int offset, __be32 info) |
d2acc347 HX |
205 | { |
206 | struct xfrm6_tunnel *handler; | |
207 | ||
875168a9 | 208 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
d2acc347 | 209 | if (!handler->err_handler(skb, opt, type, code, offset, info)) |
32bbd879 SB |
210 | return 0; |
211 | ||
212 | return -ENOENT; | |
d2acc347 HX |
213 | } |
214 | ||
32bbd879 | 215 | static int tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
ebac62fe MK |
216 | u8 type, u8 code, int offset, __be32 info) |
217 | { | |
218 | struct xfrm6_tunnel *handler; | |
219 | ||
220 | for_each_tunnel_rcu(tunnel46_handlers, handler) | |
221 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | |
32bbd879 SB |
222 | return 0; |
223 | ||
224 | return -ENOENT; | |
ebac62fe MK |
225 | } |
226 | ||
f234efac VF |
227 | static int tunnelmpls6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
228 | u8 type, u8 code, int offset, __be32 info) | |
229 | { | |
230 | struct xfrm6_tunnel *handler; | |
231 | ||
232 | for_each_tunnel_rcu(tunnelmpls6_handlers, handler) | |
233 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | |
234 | return 0; | |
235 | ||
236 | return -ENOENT; | |
237 | } | |
238 | ||
41135cc8 | 239 | static const struct inet6_protocol tunnel6_protocol = { |
d2acc347 HX |
240 | .handler = tunnel6_rcv, |
241 | .err_handler = tunnel6_err, | |
242 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, | |
243 | }; | |
244 | ||
41135cc8 | 245 | static const struct inet6_protocol tunnel46_protocol = { |
73d605d1 | 246 | .handler = tunnel46_rcv, |
ebac62fe | 247 | .err_handler = tunnel46_err, |
73d605d1 KM |
248 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
249 | }; | |
250 | ||
f234efac VF |
251 | static const struct inet6_protocol tunnelmpls6_protocol = { |
252 | .handler = tunnelmpls6_rcv, | |
253 | .err_handler = tunnelmpls6_err, | |
254 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, | |
255 | }; | |
256 | ||
d2acc347 HX |
257 | static int __init tunnel6_init(void) |
258 | { | |
259 | if (inet6_add_protocol(&tunnel6_protocol, IPPROTO_IPV6)) { | |
f3213831 | 260 | pr_err("%s: can't add protocol\n", __func__); |
d2acc347 HX |
261 | return -EAGAIN; |
262 | } | |
73d605d1 | 263 | if (inet6_add_protocol(&tunnel46_protocol, IPPROTO_IPIP)) { |
f3213831 | 264 | pr_err("%s: can't add protocol\n", __func__); |
73d605d1 KM |
265 | inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); |
266 | return -EAGAIN; | |
267 | } | |
f234efac VF |
268 | if (xfrm6_tunnel_mpls_supported() && |
269 | inet6_add_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS)) { | |
270 | pr_err("%s: can't add protocol\n", __func__); | |
271 | inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); | |
272 | inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP); | |
273 | return -EAGAIN; | |
274 | } | |
86afc703 XL |
275 | #if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) |
276 | if (xfrm_input_register_afinfo(&tunnel6_input_afinfo)) { | |
277 | pr_err("%s: can't add input afinfo\n", __func__); | |
278 | inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); | |
279 | inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP); | |
280 | if (xfrm6_tunnel_mpls_supported()) | |
281 | inet6_del_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS); | |
282 | return -EAGAIN; | |
283 | } | |
284 | #endif | |
d2acc347 HX |
285 | return 0; |
286 | } | |
287 | ||
288 | static void __exit tunnel6_fini(void) | |
289 | { | |
86afc703 XL |
290 | #if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) |
291 | if (xfrm_input_unregister_afinfo(&tunnel6_input_afinfo)) | |
292 | pr_err("%s: can't remove input afinfo\n", __func__); | |
293 | #endif | |
73d605d1 | 294 | if (inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP)) |
f3213831 | 295 | pr_err("%s: can't remove protocol\n", __func__); |
d2acc347 | 296 | if (inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6)) |
f3213831 | 297 | pr_err("%s: can't remove protocol\n", __func__); |
f234efac VF |
298 | if (xfrm6_tunnel_mpls_supported() && |
299 | inet6_del_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS)) | |
300 | pr_err("%s: can't remove protocol\n", __func__); | |
d2acc347 HX |
301 | } |
302 | ||
303 | module_init(tunnel6_init); | |
304 | module_exit(tunnel6_fini); | |
305 | MODULE_LICENSE("GPL"); |