1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C)2003,2004 USAGI/WIDE Project |
4 | * |
5 | * Authors Mitsuru KANDA <mk@linux-ipv6.org> |
6 | * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "IPv6: " fmt |
10 | |
11 | #include <linux/icmpv6.h> |
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> |
17 | #include <linux/slab.h> |
18 | #include <net/ipv6.h> |
19 | #include <net/protocol.h> |
20 | #include <net/xfrm.h> |
21 | |
22 | static struct xfrm6_tunnel __rcu *tunnel6_handlers __read_mostly; |
23 | static struct xfrm6_tunnel __rcu *tunnel46_handlers __read_mostly; |
24 | static struct xfrm6_tunnel __rcu *tunnelmpls6_handlers __read_mostly; |
25 | static DEFINE_MUTEX(tunnel6_mutex); |
26 | |
27 | static inline int xfrm6_tunnel_mpls_supported(void) |
28 | { |
29 | return IS_ENABLED(CONFIG_MPLS); |
30 | } |
31 | |
32 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) |
33 | { |
34 | struct xfrm6_tunnel __rcu **pprev; |
35 | struct xfrm6_tunnel *t; |
36 | int ret = -EEXIST; |
37 | int priority = handler->priority; |
38 | |
39 | mutex_lock(&tunnel6_mutex); |
40 | |
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, |
56 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
57 | pprev = &t->next) { |
58 | if (t->priority > priority) |
59 | break; |
60 | if (t->priority == priority) |
61 | goto err; |
62 | } |
63 | |
64 | handler->next = *pprev; |
65 | rcu_assign_pointer(*pprev, handler); |
66 | |
67 | ret = 0; |
68 | |
69 | err: |
70 | mutex_unlock(lock: &tunnel6_mutex); |
71 | |
72 | return ret; |
73 | } |
74 | EXPORT_SYMBOL(xfrm6_tunnel_register); |
75 | |
76 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) |
77 | { |
78 | struct xfrm6_tunnel __rcu **pprev; |
79 | struct xfrm6_tunnel *t; |
80 | int ret = -ENOENT; |
81 | |
82 | mutex_lock(&tunnel6_mutex); |
83 | |
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, |
99 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
100 | pprev = &t->next) { |
101 | if (t == handler) { |
102 | *pprev = handler->next; |
103 | ret = 0; |
104 | break; |
105 | } |
106 | } |
107 | |
108 | err: |
109 | mutex_unlock(lock: &tunnel6_mutex); |
110 | |
111 | synchronize_net(); |
112 | |
113 | return ret; |
114 | } |
115 | EXPORT_SYMBOL(xfrm6_tunnel_deregister); |
116 | |
117 | #define for_each_tunnel_rcu(head, handler) \ |
118 | for (handler = rcu_dereference(head); \ |
119 | handler != NULL; \ |
120 | handler = rcu_dereference(handler->next)) \ |
121 | |
122 | static int tunnelmpls6_rcv(struct sk_buff *skb) |
123 | { |
124 | struct xfrm6_tunnel *handler; |
125 | |
126 | if (!pskb_may_pull(skb, len: 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, info: 0); |
134 | |
135 | drop: |
136 | kfree_skb(skb); |
137 | return 0; |
138 | } |
139 | |
140 | static int tunnel6_rcv(struct sk_buff *skb) |
141 | { |
142 | struct xfrm6_tunnel *handler; |
143 | |
144 | if (!pskb_may_pull(skb, len: sizeof(struct ipv6hdr))) |
145 | goto drop; |
146 | |
147 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
148 | if (!handler->handler(skb)) |
149 | return 0; |
150 | |
151 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, info: 0); |
152 | |
153 | drop: |
154 | kfree_skb(skb); |
155 | return 0; |
156 | } |
157 | |
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 | |
185 | static int tunnel46_rcv(struct sk_buff *skb) |
186 | { |
187 | struct xfrm6_tunnel *handler; |
188 | |
189 | if (!pskb_may_pull(skb, len: sizeof(struct iphdr))) |
190 | goto drop; |
191 | |
192 | for_each_tunnel_rcu(tunnel46_handlers, handler) |
193 | if (!handler->handler(skb)) |
194 | return 0; |
195 | |
196 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, info: 0); |
197 | |
198 | drop: |
199 | kfree_skb(skb); |
200 | return 0; |
201 | } |
202 | |
203 | static int tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
204 | u8 type, u8 code, int offset, __be32 info) |
205 | { |
206 | struct xfrm6_tunnel *handler; |
207 | |
208 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
209 | if (!handler->err_handler(skb, opt, type, code, offset, info)) |
210 | return 0; |
211 | |
212 | return -ENOENT; |
213 | } |
214 | |
215 | static int tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
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)) |
222 | return 0; |
223 | |
224 | return -ENOENT; |
225 | } |
226 | |
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 | |
239 | static const struct inet6_protocol tunnel6_protocol = { |
240 | .handler = tunnel6_rcv, |
241 | .err_handler = tunnel6_err, |
242 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
243 | }; |
244 | |
245 | static const struct inet6_protocol tunnel46_protocol = { |
246 | .handler = tunnel46_rcv, |
247 | .err_handler = tunnel46_err, |
248 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
249 | }; |
250 | |
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 | |
257 | static int __init tunnel6_init(void) |
258 | { |
259 | if (inet6_add_protocol(prot: &tunnel6_protocol, IPPROTO_IPV6)) { |
260 | pr_err("%s: can't add protocol\n" , __func__); |
261 | return -EAGAIN; |
262 | } |
263 | if (inet6_add_protocol(prot: &tunnel46_protocol, IPPROTO_IPIP)) { |
264 | pr_err("%s: can't add protocol\n" , __func__); |
265 | inet6_del_protocol(prot: &tunnel6_protocol, IPPROTO_IPV6); |
266 | return -EAGAIN; |
267 | } |
268 | if (xfrm6_tunnel_mpls_supported() && |
269 | inet6_add_protocol(prot: &tunnelmpls6_protocol, IPPROTO_MPLS)) { |
270 | pr_err("%s: can't add protocol\n" , __func__); |
271 | inet6_del_protocol(prot: &tunnel6_protocol, IPPROTO_IPV6); |
272 | inet6_del_protocol(prot: &tunnel46_protocol, IPPROTO_IPIP); |
273 | return -EAGAIN; |
274 | } |
275 | #if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) |
276 | if (xfrm_input_register_afinfo(afinfo: &tunnel6_input_afinfo)) { |
277 | pr_err("%s: can't add input afinfo\n" , __func__); |
278 | inet6_del_protocol(prot: &tunnel6_protocol, IPPROTO_IPV6); |
279 | inet6_del_protocol(prot: &tunnel46_protocol, IPPROTO_IPIP); |
280 | if (xfrm6_tunnel_mpls_supported()) |
281 | inet6_del_protocol(prot: &tunnelmpls6_protocol, IPPROTO_MPLS); |
282 | return -EAGAIN; |
283 | } |
284 | #endif |
285 | return 0; |
286 | } |
287 | |
288 | static void __exit tunnel6_fini(void) |
289 | { |
290 | #if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) |
291 | if (xfrm_input_unregister_afinfo(afinfo: &tunnel6_input_afinfo)) |
292 | pr_err("%s: can't remove input afinfo\n" , __func__); |
293 | #endif |
294 | if (inet6_del_protocol(prot: &tunnel46_protocol, IPPROTO_IPIP)) |
295 | pr_err("%s: can't remove protocol\n" , __func__); |
296 | if (inet6_del_protocol(prot: &tunnel6_protocol, IPPROTO_IPV6)) |
297 | pr_err("%s: can't remove protocol\n" , __func__); |
298 | if (xfrm6_tunnel_mpls_supported() && |
299 | inet6_del_protocol(prot: &tunnelmpls6_protocol, IPPROTO_MPLS)) |
300 | pr_err("%s: can't remove protocol\n" , __func__); |
301 | } |
302 | |
303 | module_init(tunnel6_init); |
304 | module_exit(tunnel6_fini); |
305 | MODULE_LICENSE("GPL" ); |
306 | |