1 | /* xfrm4_protocol.c - Generic xfrm protocol multiplexer. |
2 | * |
3 | * Copyright (C) 2013 secunet Security Networks AG |
4 | * |
5 | * Author: |
6 | * Steffen Klassert <steffen.klassert@secunet.com> |
7 | * |
8 | * Based on: |
9 | * net/ipv4/tunnel4.c |
10 | * |
11 | * This program is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU General Public License |
13 | * as published by the Free Software Foundation; either version |
14 | * 2 of the License, or (at your option) any later version. |
15 | */ |
16 | |
17 | #include <linux/init.h> |
18 | #include <linux/mutex.h> |
19 | #include <linux/skbuff.h> |
20 | #include <net/icmp.h> |
21 | #include <net/ip.h> |
22 | #include <net/protocol.h> |
23 | #include <net/xfrm.h> |
24 | |
25 | static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly; |
26 | static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly; |
27 | static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly; |
28 | static DEFINE_MUTEX(xfrm4_protocol_mutex); |
29 | |
30 | static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol) |
31 | { |
32 | switch (protocol) { |
33 | case IPPROTO_ESP: |
34 | return &esp4_handlers; |
35 | case IPPROTO_AH: |
36 | return &ah4_handlers; |
37 | case IPPROTO_COMP: |
38 | return &ipcomp4_handlers; |
39 | } |
40 | |
41 | return NULL; |
42 | } |
43 | |
44 | #define for_each_protocol_rcu(head, handler) \ |
45 | for (handler = rcu_dereference(head); \ |
46 | handler != NULL; \ |
47 | handler = rcu_dereference(handler->next)) \ |
48 | |
49 | int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err) |
50 | { |
51 | int ret; |
52 | struct xfrm4_protocol *handler; |
53 | struct xfrm4_protocol __rcu **head = proto_handlers(protocol); |
54 | |
55 | if (!head) |
56 | return 0; |
57 | |
58 | for_each_protocol_rcu(*head, handler) |
59 | if ((ret = handler->cb_handler(skb, err)) <= 0) |
60 | return ret; |
61 | |
62 | return 0; |
63 | } |
64 | EXPORT_SYMBOL(xfrm4_rcv_cb); |
65 | |
66 | int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, |
67 | int encap_type) |
68 | { |
69 | int ret; |
70 | struct xfrm4_protocol *handler; |
71 | struct xfrm4_protocol __rcu **head = proto_handlers(nexthdr); |
72 | |
73 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; |
74 | XFRM_SPI_SKB_CB(skb)->family = AF_INET; |
75 | XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); |
76 | |
77 | if (!head) |
78 | goto out; |
79 | |
80 | for_each_protocol_rcu(*head, handler) |
81 | if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) |
82 | return ret; |
83 | |
84 | out: |
85 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
86 | |
87 | kfree_skb(skb); |
88 | return 0; |
89 | } |
90 | EXPORT_SYMBOL(xfrm4_rcv_encap); |
91 | |
92 | static int xfrm4_esp_rcv(struct sk_buff *skb) |
93 | { |
94 | int ret; |
95 | struct xfrm4_protocol *handler; |
96 | |
97 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; |
98 | |
99 | for_each_protocol_rcu(esp4_handlers, handler) |
100 | if ((ret = handler->handler(skb)) != -EINVAL) |
101 | return ret; |
102 | |
103 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
104 | |
105 | kfree_skb(skb); |
106 | return 0; |
107 | } |
108 | |
109 | static int xfrm4_esp_err(struct sk_buff *skb, u32 info) |
110 | { |
111 | struct xfrm4_protocol *handler; |
112 | |
113 | for_each_protocol_rcu(esp4_handlers, handler) |
114 | if (!handler->err_handler(skb, info)) |
115 | return 0; |
116 | |
117 | return -ENOENT; |
118 | } |
119 | |
120 | static int xfrm4_ah_rcv(struct sk_buff *skb) |
121 | { |
122 | int ret; |
123 | struct xfrm4_protocol *handler; |
124 | |
125 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; |
126 | |
127 | for_each_protocol_rcu(ah4_handlers, handler) |
128 | if ((ret = handler->handler(skb)) != -EINVAL) |
129 | return ret; |
130 | |
131 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
132 | |
133 | kfree_skb(skb); |
134 | return 0; |
135 | } |
136 | |
137 | static int xfrm4_ah_err(struct sk_buff *skb, u32 info) |
138 | { |
139 | struct xfrm4_protocol *handler; |
140 | |
141 | for_each_protocol_rcu(ah4_handlers, handler) |
142 | if (!handler->err_handler(skb, info)) |
143 | return 0; |
144 | |
145 | return -ENOENT; |
146 | } |
147 | |
148 | static int xfrm4_ipcomp_rcv(struct sk_buff *skb) |
149 | { |
150 | int ret; |
151 | struct xfrm4_protocol *handler; |
152 | |
153 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; |
154 | |
155 | for_each_protocol_rcu(ipcomp4_handlers, handler) |
156 | if ((ret = handler->handler(skb)) != -EINVAL) |
157 | return ret; |
158 | |
159 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
160 | |
161 | kfree_skb(skb); |
162 | return 0; |
163 | } |
164 | |
165 | static int xfrm4_ipcomp_err(struct sk_buff *skb, u32 info) |
166 | { |
167 | struct xfrm4_protocol *handler; |
168 | |
169 | for_each_protocol_rcu(ipcomp4_handlers, handler) |
170 | if (!handler->err_handler(skb, info)) |
171 | return 0; |
172 | |
173 | return -ENOENT; |
174 | } |
175 | |
176 | static const struct net_protocol esp4_protocol = { |
177 | .handler = xfrm4_esp_rcv, |
178 | .err_handler = xfrm4_esp_err, |
179 | .no_policy = 1, |
180 | .netns_ok = 1, |
181 | }; |
182 | |
183 | static const struct net_protocol ah4_protocol = { |
184 | .handler = xfrm4_ah_rcv, |
185 | .err_handler = xfrm4_ah_err, |
186 | .no_policy = 1, |
187 | .netns_ok = 1, |
188 | }; |
189 | |
190 | static const struct net_protocol ipcomp4_protocol = { |
191 | .handler = xfrm4_ipcomp_rcv, |
192 | .err_handler = xfrm4_ipcomp_err, |
193 | .no_policy = 1, |
194 | .netns_ok = 1, |
195 | }; |
196 | |
197 | static const struct xfrm_input_afinfo xfrm4_input_afinfo = { |
198 | .family = AF_INET, |
199 | .callback = xfrm4_rcv_cb, |
200 | }; |
201 | |
202 | static inline const struct net_protocol *netproto(unsigned char protocol) |
203 | { |
204 | switch (protocol) { |
205 | case IPPROTO_ESP: |
206 | return &esp4_protocol; |
207 | case IPPROTO_AH: |
208 | return &ah4_protocol; |
209 | case IPPROTO_COMP: |
210 | return &ipcomp4_protocol; |
211 | } |
212 | |
213 | return NULL; |
214 | } |
215 | |
216 | int xfrm4_protocol_register(struct xfrm4_protocol *handler, |
217 | unsigned char protocol) |
218 | { |
219 | struct xfrm4_protocol __rcu **pprev; |
220 | struct xfrm4_protocol *t; |
221 | bool add_netproto = false; |
222 | int ret = -EEXIST; |
223 | int priority = handler->priority; |
224 | |
225 | if (!proto_handlers(protocol) || !netproto(protocol)) |
226 | return -EINVAL; |
227 | |
228 | mutex_lock(&xfrm4_protocol_mutex); |
229 | |
230 | if (!rcu_dereference_protected(*proto_handlers(protocol), |
231 | lockdep_is_held(&xfrm4_protocol_mutex))) |
232 | add_netproto = true; |
233 | |
234 | for (pprev = proto_handlers(protocol); |
235 | (t = rcu_dereference_protected(*pprev, |
236 | lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; |
237 | pprev = &t->next) { |
238 | if (t->priority < priority) |
239 | break; |
240 | if (t->priority == priority) |
241 | goto err; |
242 | } |
243 | |
244 | handler->next = *pprev; |
245 | rcu_assign_pointer(*pprev, handler); |
246 | |
247 | ret = 0; |
248 | |
249 | err: |
250 | mutex_unlock(&xfrm4_protocol_mutex); |
251 | |
252 | if (add_netproto) { |
253 | if (inet_add_protocol(netproto(protocol), protocol)) { |
254 | pr_err("%s: can't add protocol\n" , __func__); |
255 | ret = -EAGAIN; |
256 | } |
257 | } |
258 | |
259 | return ret; |
260 | } |
261 | EXPORT_SYMBOL(xfrm4_protocol_register); |
262 | |
263 | int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, |
264 | unsigned char protocol) |
265 | { |
266 | struct xfrm4_protocol __rcu **pprev; |
267 | struct xfrm4_protocol *t; |
268 | int ret = -ENOENT; |
269 | |
270 | if (!proto_handlers(protocol) || !netproto(protocol)) |
271 | return -EINVAL; |
272 | |
273 | mutex_lock(&xfrm4_protocol_mutex); |
274 | |
275 | for (pprev = proto_handlers(protocol); |
276 | (t = rcu_dereference_protected(*pprev, |
277 | lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; |
278 | pprev = &t->next) { |
279 | if (t == handler) { |
280 | *pprev = handler->next; |
281 | ret = 0; |
282 | break; |
283 | } |
284 | } |
285 | |
286 | if (!rcu_dereference_protected(*proto_handlers(protocol), |
287 | lockdep_is_held(&xfrm4_protocol_mutex))) { |
288 | if (inet_del_protocol(netproto(protocol), protocol) < 0) { |
289 | pr_err("%s: can't remove protocol\n" , __func__); |
290 | ret = -EAGAIN; |
291 | } |
292 | } |
293 | |
294 | mutex_unlock(&xfrm4_protocol_mutex); |
295 | |
296 | synchronize_net(); |
297 | |
298 | return ret; |
299 | } |
300 | EXPORT_SYMBOL(xfrm4_protocol_deregister); |
301 | |
302 | void __init xfrm4_protocol_init(void) |
303 | { |
304 | xfrm_input_register_afinfo(&xfrm4_input_afinfo); |
305 | } |
306 | EXPORT_SYMBOL(xfrm4_protocol_init); |
307 | |