diff options
-rw-r--r-- | include/net/xfrm.h | 15 | ||||
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/xfrm6_policy.c | 7 | ||||
-rw-r--r-- | net/ipv6/xfrm6_protocol.c | 270 |
4 files changed, 293 insertions, 1 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index af13599b60a0..6304ec394c4a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -1374,6 +1374,16 @@ struct xfrm4_protocol { | |||
1374 | int priority; | 1374 | int priority; |
1375 | }; | 1375 | }; |
1376 | 1376 | ||
1377 | struct xfrm6_protocol { | ||
1378 | int (*handler)(struct sk_buff *skb); | ||
1379 | int (*cb_handler)(struct sk_buff *skb, int err); | ||
1380 | int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
1381 | u8 type, u8 code, int offset, __be32 info); | ||
1382 | |||
1383 | struct xfrm6_protocol __rcu *next; | ||
1384 | int priority; | ||
1385 | }; | ||
1386 | |||
1377 | /* XFRM tunnel handlers. */ | 1387 | /* XFRM tunnel handlers. */ |
1378 | struct xfrm_tunnel { | 1388 | struct xfrm_tunnel { |
1379 | int (*handler)(struct sk_buff *skb); | 1389 | int (*handler)(struct sk_buff *skb); |
@@ -1408,6 +1418,8 @@ int xfrm6_init(void); | |||
1408 | void xfrm6_fini(void); | 1418 | void xfrm6_fini(void); |
1409 | int xfrm6_state_init(void); | 1419 | int xfrm6_state_init(void); |
1410 | void xfrm6_state_fini(void); | 1420 | void xfrm6_state_fini(void); |
1421 | int xfrm6_protocol_init(void); | ||
1422 | void xfrm6_protocol_fini(void); | ||
1411 | #else | 1423 | #else |
1412 | static inline int xfrm6_init(void) | 1424 | static inline int xfrm6_init(void) |
1413 | { | 1425 | { |
@@ -1552,6 +1564,9 @@ int xfrm6_rcv(struct sk_buff *skb); | |||
1552 | int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, | 1564 | int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, |
1553 | xfrm_address_t *saddr, u8 proto); | 1565 | xfrm_address_t *saddr, u8 proto); |
1554 | void xfrm6_local_error(struct sk_buff *skb, u32 mtu); | 1566 | void xfrm6_local_error(struct sk_buff *skb, u32 mtu); |
1567 | int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err); | ||
1568 | int xfrm6_protocol_register(struct xfrm6_protocol *handler, unsigned char protocol); | ||
1569 | int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, unsigned char protocol); | ||
1555 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); | 1570 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); |
1556 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); | 1571 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); |
1557 | __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); | 1572 | __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); |
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 17bb830872db..2fe68364bb20 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -16,7 +16,7 @@ ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o | |||
16 | ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o | 16 | ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o |
17 | 17 | ||
18 | ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ | 18 | ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ |
19 | xfrm6_output.o | 19 | xfrm6_output.o xfrm6_protocol.o |
20 | ipv6-$(CONFIG_NETFILTER) += netfilter.o | 20 | ipv6-$(CONFIG_NETFILTER) += netfilter.o |
21 | ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o | 21 | ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o |
22 | ipv6-$(CONFIG_PROC_FS) += proc.o | 22 | ipv6-$(CONFIG_PROC_FS) += proc.o |
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 5f8e128c512d..2a0bbda2c76a 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c | |||
@@ -389,11 +389,17 @@ int __init xfrm6_init(void) | |||
389 | if (ret) | 389 | if (ret) |
390 | goto out_policy; | 390 | goto out_policy; |
391 | 391 | ||
392 | ret = xfrm6_protocol_init(); | ||
393 | if (ret) | ||
394 | goto out_state; | ||
395 | |||
392 | #ifdef CONFIG_SYSCTL | 396 | #ifdef CONFIG_SYSCTL |
393 | register_pernet_subsys(&xfrm6_net_ops); | 397 | register_pernet_subsys(&xfrm6_net_ops); |
394 | #endif | 398 | #endif |
395 | out: | 399 | out: |
396 | return ret; | 400 | return ret; |
401 | out_state: | ||
402 | xfrm6_state_fini(); | ||
397 | out_policy: | 403 | out_policy: |
398 | xfrm6_policy_fini(); | 404 | xfrm6_policy_fini(); |
399 | goto out; | 405 | goto out; |
@@ -404,6 +410,7 @@ void xfrm6_fini(void) | |||
404 | #ifdef CONFIG_SYSCTL | 410 | #ifdef CONFIG_SYSCTL |
405 | unregister_pernet_subsys(&xfrm6_net_ops); | 411 | unregister_pernet_subsys(&xfrm6_net_ops); |
406 | #endif | 412 | #endif |
413 | xfrm6_protocol_fini(); | ||
407 | xfrm6_policy_fini(); | 414 | xfrm6_policy_fini(); |
408 | xfrm6_state_fini(); | 415 | xfrm6_state_fini(); |
409 | dst_entries_destroy(&xfrm6_dst_ops); | 416 | dst_entries_destroy(&xfrm6_dst_ops); |
diff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c new file mode 100644 index 000000000000..6ab989c486f7 --- /dev/null +++ b/net/ipv6/xfrm6_protocol.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. | ||
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/xfrm4_protocol.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 <linux/icmpv6.h> | ||
21 | #include <net/ipv6.h> | ||
22 | #include <net/protocol.h> | ||
23 | #include <net/xfrm.h> | ||
24 | |||
25 | static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; | ||
26 | static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; | ||
27 | static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; | ||
28 | static DEFINE_MUTEX(xfrm6_protocol_mutex); | ||
29 | |||
30 | static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) | ||
31 | { | ||
32 | switch (protocol) { | ||
33 | case IPPROTO_ESP: | ||
34 | return &esp6_handlers; | ||
35 | case IPPROTO_AH: | ||
36 | return &ah6_handlers; | ||
37 | case IPPROTO_COMP: | ||
38 | return &ipcomp6_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 xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) | ||
50 | { | ||
51 | int ret; | ||
52 | struct xfrm6_protocol *handler; | ||
53 | |||
54 | for_each_protocol_rcu(*proto_handlers(protocol), handler) | ||
55 | if ((ret = handler->cb_handler(skb, err)) <= 0) | ||
56 | return ret; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | EXPORT_SYMBOL(xfrm6_rcv_cb); | ||
61 | |||
62 | static int xfrm6_esp_rcv(struct sk_buff *skb) | ||
63 | { | ||
64 | int ret; | ||
65 | struct xfrm6_protocol *handler; | ||
66 | |||
67 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | ||
68 | |||
69 | for_each_protocol_rcu(esp6_handlers, handler) | ||
70 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
71 | return ret; | ||
72 | |||
73 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | ||
74 | |||
75 | kfree_skb(skb); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
80 | u8 type, u8 code, int offset, __be32 info) | ||
81 | { | ||
82 | struct xfrm6_protocol *handler; | ||
83 | |||
84 | for_each_protocol_rcu(esp6_handlers, handler) | ||
85 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | static int xfrm6_ah_rcv(struct sk_buff *skb) | ||
90 | { | ||
91 | int ret; | ||
92 | struct xfrm6_protocol *handler; | ||
93 | |||
94 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | ||
95 | |||
96 | for_each_protocol_rcu(ah6_handlers, handler) | ||
97 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
98 | return ret; | ||
99 | |||
100 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | ||
101 | |||
102 | kfree_skb(skb); | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
107 | u8 type, u8 code, int offset, __be32 info) | ||
108 | { | ||
109 | struct xfrm6_protocol *handler; | ||
110 | |||
111 | for_each_protocol_rcu(ah6_handlers, handler) | ||
112 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | static int xfrm6_ipcomp_rcv(struct sk_buff *skb) | ||
117 | { | ||
118 | int ret; | ||
119 | struct xfrm6_protocol *handler; | ||
120 | |||
121 | XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; | ||
122 | |||
123 | for_each_protocol_rcu(ipcomp6_handlers, handler) | ||
124 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
125 | return ret; | ||
126 | |||
127 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); | ||
128 | |||
129 | kfree_skb(skb); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
134 | u8 type, u8 code, int offset, __be32 info) | ||
135 | { | ||
136 | struct xfrm6_protocol *handler; | ||
137 | |||
138 | for_each_protocol_rcu(ipcomp6_handlers, handler) | ||
139 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | ||
140 | break; | ||
141 | } | ||
142 | |||
143 | static const struct inet6_protocol esp6_protocol = { | ||
144 | .handler = xfrm6_esp_rcv, | ||
145 | .err_handler = xfrm6_esp_err, | ||
146 | .flags = INET6_PROTO_NOPOLICY, | ||
147 | }; | ||
148 | |||
149 | static const struct inet6_protocol ah6_protocol = { | ||
150 | .handler = xfrm6_ah_rcv, | ||
151 | .err_handler = xfrm6_ah_err, | ||
152 | .flags = INET6_PROTO_NOPOLICY, | ||
153 | }; | ||
154 | |||
155 | static const struct inet6_protocol ipcomp6_protocol = { | ||
156 | .handler = xfrm6_ipcomp_rcv, | ||
157 | .err_handler = xfrm6_ipcomp_err, | ||
158 | .flags = INET6_PROTO_NOPOLICY, | ||
159 | }; | ||
160 | |||
161 | static struct xfrm_input_afinfo xfrm6_input_afinfo = { | ||
162 | .family = AF_INET6, | ||
163 | .owner = THIS_MODULE, | ||
164 | .callback = xfrm6_rcv_cb, | ||
165 | }; | ||
166 | |||
167 | static inline const struct inet6_protocol *netproto(unsigned char protocol) | ||
168 | { | ||
169 | switch (protocol) { | ||
170 | case IPPROTO_ESP: | ||
171 | return &esp6_protocol; | ||
172 | case IPPROTO_AH: | ||
173 | return &ah6_protocol; | ||
174 | case IPPROTO_COMP: | ||
175 | return &ipcomp6_protocol; | ||
176 | } | ||
177 | |||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | int xfrm6_protocol_register(struct xfrm6_protocol *handler, | ||
182 | unsigned char protocol) | ||
183 | { | ||
184 | struct xfrm6_protocol __rcu **pprev; | ||
185 | struct xfrm6_protocol *t; | ||
186 | bool add_netproto = false; | ||
187 | |||
188 | int ret = -EEXIST; | ||
189 | int priority = handler->priority; | ||
190 | |||
191 | mutex_lock(&xfrm6_protocol_mutex); | ||
192 | |||
193 | if (!rcu_dereference_protected(*proto_handlers(protocol), | ||
194 | lockdep_is_held(&xfrm6_protocol_mutex))) | ||
195 | add_netproto = true; | ||
196 | |||
197 | for (pprev = proto_handlers(protocol); | ||
198 | (t = rcu_dereference_protected(*pprev, | ||
199 | lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; | ||
200 | pprev = &t->next) { | ||
201 | if (t->priority < priority) | ||
202 | break; | ||
203 | if (t->priority == priority) | ||
204 | goto err; | ||
205 | } | ||
206 | |||
207 | handler->next = *pprev; | ||
208 | rcu_assign_pointer(*pprev, handler); | ||
209 | |||
210 | ret = 0; | ||
211 | |||
212 | err: | ||
213 | mutex_unlock(&xfrm6_protocol_mutex); | ||
214 | |||
215 | if (add_netproto) { | ||
216 | if (inet6_add_protocol(netproto(protocol), protocol)) { | ||
217 | pr_err("%s: can't add protocol\n", __func__); | ||
218 | ret = -EAGAIN; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | return ret; | ||
223 | } | ||
224 | EXPORT_SYMBOL(xfrm6_protocol_register); | ||
225 | |||
226 | int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, | ||
227 | unsigned char protocol) | ||
228 | { | ||
229 | struct xfrm6_protocol __rcu **pprev; | ||
230 | struct xfrm6_protocol *t; | ||
231 | int ret = -ENOENT; | ||
232 | |||
233 | mutex_lock(&xfrm6_protocol_mutex); | ||
234 | |||
235 | for (pprev = proto_handlers(protocol); | ||
236 | (t = rcu_dereference_protected(*pprev, | ||
237 | lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; | ||
238 | pprev = &t->next) { | ||
239 | if (t == handler) { | ||
240 | *pprev = handler->next; | ||
241 | ret = 0; | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | if (!rcu_dereference_protected(*proto_handlers(protocol), | ||
247 | lockdep_is_held(&xfrm6_protocol_mutex))) { | ||
248 | if (inet6_del_protocol(netproto(protocol), protocol) < 0) { | ||
249 | pr_err("%s: can't remove protocol\n", __func__); | ||
250 | ret = -EAGAIN; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | mutex_unlock(&xfrm6_protocol_mutex); | ||
255 | |||
256 | synchronize_net(); | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | EXPORT_SYMBOL(xfrm6_protocol_deregister); | ||
261 | |||
262 | int __init xfrm6_protocol_init(void) | ||
263 | { | ||
264 | return xfrm_input_register_afinfo(&xfrm6_input_afinfo); | ||
265 | } | ||
266 | |||
267 | void xfrm6_protocol_fini(void) | ||
268 | { | ||
269 | xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); | ||
270 | } | ||