Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
7e14ea15 SK |
2 | /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. |
3 | * | |
4 | * Copyright (C) 2013 secunet Security Networks AG | |
5 | * | |
6 | * Author: | |
7 | * Steffen Klassert <steffen.klassert@secunet.com> | |
8 | * | |
9 | * Based on: | |
10 | * net/ipv4/xfrm4_protocol.c | |
7e14ea15 SK |
11 | */ |
12 | ||
13 | #include <linux/init.h> | |
14 | #include <linux/mutex.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/icmpv6.h> | |
0146dca7 | 17 | #include <net/ip6_route.h> |
7e14ea15 SK |
18 | #include <net/ipv6.h> |
19 | #include <net/protocol.h> | |
20 | #include <net/xfrm.h> | |
21 | ||
22 | static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; | |
23 | static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; | |
24 | static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; | |
25 | static DEFINE_MUTEX(xfrm6_protocol_mutex); | |
26 | ||
27 | static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) | |
28 | { | |
29 | switch (protocol) { | |
30 | case IPPROTO_ESP: | |
31 | return &esp6_handlers; | |
32 | case IPPROTO_AH: | |
33 | return &ah6_handlers; | |
34 | case IPPROTO_COMP: | |
35 | return &ipcomp6_handlers; | |
36 | } | |
37 | ||
38 | return NULL; | |
39 | } | |
40 | ||
41 | #define for_each_protocol_rcu(head, handler) \ | |
42 | for (handler = rcu_dereference(head); \ | |
43 | handler != NULL; \ | |
44 | handler = rcu_dereference(handler->next)) \ | |
45 | ||
bb9cd077 | 46 | static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) |
7e14ea15 SK |
47 | { |
48 | int ret; | |
49 | struct xfrm6_protocol *handler; | |
edb666f0 SK |
50 | struct xfrm6_protocol __rcu **head = proto_handlers(protocol); |
51 | ||
52 | if (!head) | |
53 | return 0; | |
7e14ea15 SK |
54 | |
55 | for_each_protocol_rcu(*proto_handlers(protocol), handler) | |
56 | if ((ret = handler->cb_handler(skb, err)) <= 0) | |
57 | return ret; | |
58 | ||
59 | return 0; | |
60 | } | |
7e14ea15 | 61 | |
0146dca7 SD |
62 | int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, |
63 | int encap_type) | |
64 | { | |
65 | int ret; | |
66 | struct xfrm6_protocol *handler; | |
67 | struct xfrm6_protocol __rcu **head = proto_handlers(nexthdr); | |
68 | ||
69 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | |
70 | XFRM_SPI_SKB_CB(skb)->family = AF_INET6; | |
71 | XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); | |
72 | ||
73 | if (!head) | |
74 | goto out; | |
75 | ||
76 | if (!skb_dst(skb)) { | |
77 | const struct ipv6hdr *ip6h = ipv6_hdr(skb); | |
78 | int flags = RT6_LOOKUP_F_HAS_SADDR; | |
79 | struct dst_entry *dst; | |
80 | struct flowi6 fl6 = { | |
81 | .flowi6_iif = skb->dev->ifindex, | |
82 | .daddr = ip6h->daddr, | |
83 | .saddr = ip6h->saddr, | |
84 | .flowlabel = ip6_flowinfo(ip6h), | |
85 | .flowi6_mark = skb->mark, | |
86 | .flowi6_proto = ip6h->nexthdr, | |
87 | }; | |
88 | ||
89 | dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, | |
90 | skb, flags); | |
91 | if (dst->error) | |
92 | goto drop; | |
93 | skb_dst_set(skb, dst); | |
94 | } | |
95 | ||
96 | for_each_protocol_rcu(*head, handler) | |
97 | if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) | |
98 | return ret; | |
99 | ||
100 | out: | |
101 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | |
102 | ||
103 | drop: | |
104 | kfree_skb(skb); | |
105 | return 0; | |
106 | } | |
107 | EXPORT_SYMBOL(xfrm6_rcv_encap); | |
108 | ||
7e14ea15 SK |
109 | static int xfrm6_esp_rcv(struct sk_buff *skb) |
110 | { | |
111 | int ret; | |
112 | struct xfrm6_protocol *handler; | |
113 | ||
114 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | |
115 | ||
116 | for_each_protocol_rcu(esp6_handlers, handler) | |
117 | if ((ret = handler->handler(skb)) != -EINVAL) | |
118 | return ret; | |
119 | ||
120 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | |
121 | ||
122 | kfree_skb(skb); | |
123 | return 0; | |
124 | } | |
125 | ||
32bbd879 | 126 | static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
7e14ea15 SK |
127 | u8 type, u8 code, int offset, __be32 info) |
128 | { | |
129 | struct xfrm6_protocol *handler; | |
130 | ||
131 | for_each_protocol_rcu(esp6_handlers, handler) | |
132 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | |
32bbd879 SB |
133 | return 0; |
134 | ||
135 | return -ENOENT; | |
7e14ea15 SK |
136 | } |
137 | ||
138 | static int xfrm6_ah_rcv(struct sk_buff *skb) | |
139 | { | |
140 | int ret; | |
141 | struct xfrm6_protocol *handler; | |
142 | ||
143 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | |
144 | ||
145 | for_each_protocol_rcu(ah6_handlers, handler) | |
146 | if ((ret = handler->handler(skb)) != -EINVAL) | |
147 | return ret; | |
148 | ||
149 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | |
150 | ||
151 | kfree_skb(skb); | |
152 | return 0; | |
153 | } | |
154 | ||
32bbd879 | 155 | static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
7e14ea15 SK |
156 | u8 type, u8 code, int offset, __be32 info) |
157 | { | |
158 | struct xfrm6_protocol *handler; | |
159 | ||
160 | for_each_protocol_rcu(ah6_handlers, handler) | |
161 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | |
32bbd879 SB |
162 | return 0; |
163 | ||
164 | return -ENOENT; | |
7e14ea15 SK |
165 | } |
166 | ||
167 | static int xfrm6_ipcomp_rcv(struct sk_buff *skb) | |
168 | { | |
169 | int ret; | |
170 | struct xfrm6_protocol *handler; | |
171 | ||
172 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | |
173 | ||
174 | for_each_protocol_rcu(ipcomp6_handlers, handler) | |
175 | if ((ret = handler->handler(skb)) != -EINVAL) | |
176 | return ret; | |
177 | ||
178 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | |
179 | ||
180 | kfree_skb(skb); | |
181 | return 0; | |
182 | } | |
183 | ||
32bbd879 | 184 | static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
7e14ea15 SK |
185 | u8 type, u8 code, int offset, __be32 info) |
186 | { | |
187 | struct xfrm6_protocol *handler; | |
188 | ||
189 | for_each_protocol_rcu(ipcomp6_handlers, handler) | |
190 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | |
32bbd879 SB |
191 | return 0; |
192 | ||
193 | return -ENOENT; | |
7e14ea15 SK |
194 | } |
195 | ||
196 | static const struct inet6_protocol esp6_protocol = { | |
197 | .handler = xfrm6_esp_rcv, | |
198 | .err_handler = xfrm6_esp_err, | |
199 | .flags = INET6_PROTO_NOPOLICY, | |
200 | }; | |
201 | ||
202 | static const struct inet6_protocol ah6_protocol = { | |
203 | .handler = xfrm6_ah_rcv, | |
204 | .err_handler = xfrm6_ah_err, | |
205 | .flags = INET6_PROTO_NOPOLICY, | |
206 | }; | |
207 | ||
208 | static const struct inet6_protocol ipcomp6_protocol = { | |
209 | .handler = xfrm6_ipcomp_rcv, | |
210 | .err_handler = xfrm6_ipcomp_err, | |
211 | .flags = INET6_PROTO_NOPOLICY, | |
212 | }; | |
213 | ||
960fdfde | 214 | static const struct xfrm_input_afinfo xfrm6_input_afinfo = { |
7e14ea15 | 215 | .family = AF_INET6, |
7e14ea15 SK |
216 | .callback = xfrm6_rcv_cb, |
217 | }; | |
218 | ||
219 | static inline const struct inet6_protocol *netproto(unsigned char protocol) | |
220 | { | |
221 | switch (protocol) { | |
222 | case IPPROTO_ESP: | |
223 | return &esp6_protocol; | |
224 | case IPPROTO_AH: | |
225 | return &ah6_protocol; | |
226 | case IPPROTO_COMP: | |
227 | return &ipcomp6_protocol; | |
228 | } | |
229 | ||
230 | return NULL; | |
231 | } | |
232 | ||
233 | int xfrm6_protocol_register(struct xfrm6_protocol *handler, | |
234 | unsigned char protocol) | |
235 | { | |
236 | struct xfrm6_protocol __rcu **pprev; | |
237 | struct xfrm6_protocol *t; | |
238 | bool add_netproto = false; | |
7e14ea15 SK |
239 | int ret = -EEXIST; |
240 | int priority = handler->priority; | |
241 | ||
edb666f0 SK |
242 | if (!proto_handlers(protocol) || !netproto(protocol)) |
243 | return -EINVAL; | |
244 | ||
7e14ea15 SK |
245 | mutex_lock(&xfrm6_protocol_mutex); |
246 | ||
247 | if (!rcu_dereference_protected(*proto_handlers(protocol), | |
248 | lockdep_is_held(&xfrm6_protocol_mutex))) | |
249 | add_netproto = true; | |
250 | ||
251 | for (pprev = proto_handlers(protocol); | |
252 | (t = rcu_dereference_protected(*pprev, | |
253 | lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; | |
254 | pprev = &t->next) { | |
255 | if (t->priority < priority) | |
256 | break; | |
257 | if (t->priority == priority) | |
258 | goto err; | |
259 | } | |
260 | ||
261 | handler->next = *pprev; | |
262 | rcu_assign_pointer(*pprev, handler); | |
263 | ||
264 | ret = 0; | |
265 | ||
266 | err: | |
267 | mutex_unlock(&xfrm6_protocol_mutex); | |
268 | ||
269 | if (add_netproto) { | |
270 | if (inet6_add_protocol(netproto(protocol), protocol)) { | |
271 | pr_err("%s: can't add protocol\n", __func__); | |
272 | ret = -EAGAIN; | |
273 | } | |
274 | } | |
275 | ||
276 | return ret; | |
277 | } | |
278 | EXPORT_SYMBOL(xfrm6_protocol_register); | |
279 | ||
280 | int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, | |
281 | unsigned char protocol) | |
282 | { | |
283 | struct xfrm6_protocol __rcu **pprev; | |
284 | struct xfrm6_protocol *t; | |
285 | int ret = -ENOENT; | |
286 | ||
edb666f0 SK |
287 | if (!proto_handlers(protocol) || !netproto(protocol)) |
288 | return -EINVAL; | |
289 | ||
7e14ea15 SK |
290 | mutex_lock(&xfrm6_protocol_mutex); |
291 | ||
292 | for (pprev = proto_handlers(protocol); | |
293 | (t = rcu_dereference_protected(*pprev, | |
294 | lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; | |
295 | pprev = &t->next) { | |
296 | if (t == handler) { | |
297 | *pprev = handler->next; | |
298 | ret = 0; | |
299 | break; | |
300 | } | |
301 | } | |
302 | ||
303 | if (!rcu_dereference_protected(*proto_handlers(protocol), | |
304 | lockdep_is_held(&xfrm6_protocol_mutex))) { | |
305 | if (inet6_del_protocol(netproto(protocol), protocol) < 0) { | |
306 | pr_err("%s: can't remove protocol\n", __func__); | |
307 | ret = -EAGAIN; | |
308 | } | |
309 | } | |
310 | ||
311 | mutex_unlock(&xfrm6_protocol_mutex); | |
312 | ||
313 | synchronize_net(); | |
314 | ||
315 | return ret; | |
316 | } | |
317 | EXPORT_SYMBOL(xfrm6_protocol_deregister); | |
318 | ||
319 | int __init xfrm6_protocol_init(void) | |
320 | { | |
321 | return xfrm_input_register_afinfo(&xfrm6_input_afinfo); | |
322 | } | |
323 | ||
324 | void xfrm6_protocol_fini(void) | |
325 | { | |
326 | xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); | |
327 | } |