Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
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
11 : : */
12 : :
13 : : #include <linux/init.h>
14 : : #include <linux/mutex.h>
15 : : #include <linux/skbuff.h>
16 : : #include <linux/icmpv6.h>
17 : : #include <net/ipv6.h>
18 : : #include <net/protocol.h>
19 : : #include <net/xfrm.h>
20 : :
21 : : static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
22 : : static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
23 : : static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
24 : : static DEFINE_MUTEX(xfrm6_protocol_mutex);
25 : :
26 : 18 : static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
27 : : {
28 : 18 : switch (protocol) {
29 : : case IPPROTO_ESP:
30 : : return &esp6_handlers;
31 : 6 : case IPPROTO_AH:
32 : 6 : return &ah6_handlers;
33 : 0 : case IPPROTO_COMP:
34 : 0 : return &ipcomp6_handlers;
35 : : }
36 : :
37 : 0 : return NULL;
38 : : }
39 : :
40 : : #define for_each_protocol_rcu(head, handler) \
41 : : for (handler = rcu_dereference(head); \
42 : : handler != NULL; \
43 : : handler = rcu_dereference(handler->next)) \
44 : :
45 : 0 : static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
46 : : {
47 : 0 : int ret;
48 : 0 : struct xfrm6_protocol *handler;
49 [ # # ]: 0 : struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
50 : :
51 : 0 : if (!head)
52 : : return 0;
53 : :
54 [ # # # # : 0 : for_each_protocol_rcu(*proto_handlers(protocol), handler)
# # ]
55 [ # # ]: 0 : if ((ret = handler->cb_handler(skb, err)) <= 0)
56 : 0 : return ret;
57 : :
58 : : return 0;
59 : : }
60 : :
61 : 0 : static int xfrm6_esp_rcv(struct sk_buff *skb)
62 : : {
63 : 0 : int ret;
64 : 0 : struct xfrm6_protocol *handler;
65 : :
66 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
67 : :
68 [ # # ]: 0 : for_each_protocol_rcu(esp6_handlers, handler)
69 [ # # ]: 0 : if ((ret = handler->handler(skb)) != -EINVAL)
70 : 0 : return ret;
71 : :
72 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
73 : :
74 : 0 : kfree_skb(skb);
75 : 0 : return 0;
76 : : }
77 : :
78 : 0 : static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
79 : : u8 type, u8 code, int offset, __be32 info)
80 : : {
81 : 0 : struct xfrm6_protocol *handler;
82 : :
83 [ # # ]: 0 : for_each_protocol_rcu(esp6_handlers, handler)
84 [ # # ]: 0 : if (!handler->err_handler(skb, opt, type, code, offset, info))
85 : : return 0;
86 : :
87 : : return -ENOENT;
88 : : }
89 : :
90 : 0 : static int xfrm6_ah_rcv(struct sk_buff *skb)
91 : : {
92 : 0 : int ret;
93 : 0 : struct xfrm6_protocol *handler;
94 : :
95 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
96 : :
97 [ # # ]: 0 : for_each_protocol_rcu(ah6_handlers, handler)
98 [ # # ]: 0 : if ((ret = handler->handler(skb)) != -EINVAL)
99 : 0 : return ret;
100 : :
101 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
102 : :
103 : 0 : kfree_skb(skb);
104 : 0 : return 0;
105 : : }
106 : :
107 : 0 : static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
108 : : u8 type, u8 code, int offset, __be32 info)
109 : : {
110 : 0 : struct xfrm6_protocol *handler;
111 : :
112 [ # # ]: 0 : for_each_protocol_rcu(ah6_handlers, handler)
113 [ # # ]: 0 : if (!handler->err_handler(skb, opt, type, code, offset, info))
114 : : return 0;
115 : :
116 : : return -ENOENT;
117 : : }
118 : :
119 : 0 : static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
120 : : {
121 : 0 : int ret;
122 : 0 : struct xfrm6_protocol *handler;
123 : :
124 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
125 : :
126 [ # # ]: 0 : for_each_protocol_rcu(ipcomp6_handlers, handler)
127 [ # # ]: 0 : if ((ret = handler->handler(skb)) != -EINVAL)
128 : 0 : return ret;
129 : :
130 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
131 : :
132 : 0 : kfree_skb(skb);
133 : 0 : return 0;
134 : : }
135 : :
136 : 0 : static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
137 : : u8 type, u8 code, int offset, __be32 info)
138 : : {
139 : 0 : struct xfrm6_protocol *handler;
140 : :
141 [ # # ]: 0 : for_each_protocol_rcu(ipcomp6_handlers, handler)
142 [ # # ]: 0 : if (!handler->err_handler(skb, opt, type, code, offset, info))
143 : : return 0;
144 : :
145 : : return -ENOENT;
146 : : }
147 : :
148 : : static const struct inet6_protocol esp6_protocol = {
149 : : .handler = xfrm6_esp_rcv,
150 : : .err_handler = xfrm6_esp_err,
151 : : .flags = INET6_PROTO_NOPOLICY,
152 : : };
153 : :
154 : : static const struct inet6_protocol ah6_protocol = {
155 : : .handler = xfrm6_ah_rcv,
156 : : .err_handler = xfrm6_ah_err,
157 : : .flags = INET6_PROTO_NOPOLICY,
158 : : };
159 : :
160 : : static const struct inet6_protocol ipcomp6_protocol = {
161 : : .handler = xfrm6_ipcomp_rcv,
162 : : .err_handler = xfrm6_ipcomp_err,
163 : : .flags = INET6_PROTO_NOPOLICY,
164 : : };
165 : :
166 : : static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
167 : : .family = AF_INET6,
168 : : .callback = xfrm6_rcv_cb,
169 : : };
170 : :
171 : 12 : static inline const struct inet6_protocol *netproto(unsigned char protocol)
172 : : {
173 [ - - + - ]: 6 : switch (protocol) {
174 : : case IPPROTO_ESP:
175 : : return &esp6_protocol;
176 : 3 : case IPPROTO_AH:
177 : 3 : return &ah6_protocol;
178 : 0 : case IPPROTO_COMP:
179 : 0 : return &ipcomp6_protocol;
180 : : }
181 : :
182 : 0 : return NULL;
183 : : }
184 : :
185 : 6 : int xfrm6_protocol_register(struct xfrm6_protocol *handler,
186 : : unsigned char protocol)
187 : : {
188 : 6 : struct xfrm6_protocol __rcu **pprev;
189 : 6 : struct xfrm6_protocol *t;
190 : 6 : bool add_netproto = false;
191 : 6 : int ret = -EEXIST;
192 : 6 : int priority = handler->priority;
193 : :
194 [ + - ]: 6 : if (!proto_handlers(protocol) || !netproto(protocol))
195 : : return -EINVAL;
196 : :
197 : 6 : mutex_lock(&xfrm6_protocol_mutex);
198 : :
199 [ + - - + : 9 : if (!rcu_dereference_protected(*proto_handlers(protocol),
+ - ]
200 : : lockdep_is_held(&xfrm6_protocol_mutex)))
201 : 6 : add_netproto = true;
202 : :
203 [ + - + - ]: 6 : for (pprev = proto_handlers(protocol);
204 [ - + ]: 6 : (t = rcu_dereference_protected(*pprev,
205 : : lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
206 : 0 : pprev = &t->next) {
207 [ # # ]: 0 : if (t->priority < priority)
208 : : break;
209 [ # # ]: 0 : if (t->priority == priority)
210 : 0 : goto err;
211 : : }
212 : :
213 : 6 : handler->next = *pprev;
214 : 6 : rcu_assign_pointer(*pprev, handler);
215 : :
216 : 6 : ret = 0;
217 : :
218 : 6 : err:
219 : 6 : mutex_unlock(&xfrm6_protocol_mutex);
220 : :
221 [ + - ]: 6 : if (add_netproto) {
222 [ + - - + : 9 : if (inet6_add_protocol(netproto(protocol), protocol)) {
- + ]
223 : 0 : pr_err("%s: can't add protocol\n", __func__);
224 : 0 : ret = -EAGAIN;
225 : : }
226 : : }
227 : :
228 : : return ret;
229 : : }
230 : : EXPORT_SYMBOL(xfrm6_protocol_register);
231 : :
232 : 0 : int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
233 : : unsigned char protocol)
234 : : {
235 : 0 : struct xfrm6_protocol __rcu **pprev;
236 : 0 : struct xfrm6_protocol *t;
237 : 0 : int ret = -ENOENT;
238 : :
239 [ # # ]: 0 : if (!proto_handlers(protocol) || !netproto(protocol))
240 : : return -EINVAL;
241 : :
242 : 0 : mutex_lock(&xfrm6_protocol_mutex);
243 : :
244 [ # # # # ]: 0 : for (pprev = proto_handlers(protocol);
245 [ # # ]: 0 : (t = rcu_dereference_protected(*pprev,
246 : : lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
247 : 0 : pprev = &t->next) {
248 [ # # ]: 0 : if (t == handler) {
249 : 0 : *pprev = handler->next;
250 : 0 : ret = 0;
251 : 0 : break;
252 : : }
253 : : }
254 : :
255 [ # # # # : 0 : if (!rcu_dereference_protected(*proto_handlers(protocol),
# # ]
256 : : lockdep_is_held(&xfrm6_protocol_mutex))) {
257 [ # # # # : 0 : if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
# # ]
258 : 0 : pr_err("%s: can't remove protocol\n", __func__);
259 : 0 : ret = -EAGAIN;
260 : : }
261 : : }
262 : :
263 : 0 : mutex_unlock(&xfrm6_protocol_mutex);
264 : :
265 : 0 : synchronize_net();
266 : :
267 : 0 : return ret;
268 : : }
269 : : EXPORT_SYMBOL(xfrm6_protocol_deregister);
270 : :
271 : 3 : int __init xfrm6_protocol_init(void)
272 : : {
273 : 3 : return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
274 : : }
275 : :
276 : 0 : void xfrm6_protocol_fini(void)
277 : : {
278 : 0 : xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
279 : 0 : }
|