aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/xfrm6_protocol.c
diff options
context:
space:
mode:
authorSteffen Klassert <steffen.klassert@secunet.com>2014-03-14 02:28:07 -0400
committerSteffen Klassert <steffen.klassert@secunet.com>2014-03-14 02:28:07 -0400
commit7e14ea1521d9249d9de7f0ea39c9af054745eebd (patch)
treeae286e642876c489f2602a4ffffb79796bfebb24 /net/ipv6/xfrm6_protocol.c
parent2f32b51b609faea1e40bb8c5bd305f1351740936 (diff)
xfrm6: Add IPsec protocol multiplexer
This patch adds an IPsec protocol multiplexer for ipv6. With this it is possible to add alternative protocol handlers, as needed for IPsec virtual tunnel interfaces. Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/ipv6/xfrm6_protocol.c')
-rw-r--r--net/ipv6/xfrm6_protocol.c270
1 files changed, 270 insertions, 0 deletions
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
25static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
26static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
27static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
28static DEFINE_MUTEX(xfrm6_protocol_mutex);
29
30static 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
49int 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}
60EXPORT_SYMBOL(xfrm6_rcv_cb);
61
62static 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
79static 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
89static 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
106static 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
116static 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
133static 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
143static const struct inet6_protocol esp6_protocol = {
144 .handler = xfrm6_esp_rcv,
145 .err_handler = xfrm6_esp_err,
146 .flags = INET6_PROTO_NOPOLICY,
147};
148
149static const struct inet6_protocol ah6_protocol = {
150 .handler = xfrm6_ah_rcv,
151 .err_handler = xfrm6_ah_err,
152 .flags = INET6_PROTO_NOPOLICY,
153};
154
155static const struct inet6_protocol ipcomp6_protocol = {
156 .handler = xfrm6_ipcomp_rcv,
157 .err_handler = xfrm6_ipcomp_err,
158 .flags = INET6_PROTO_NOPOLICY,
159};
160
161static struct xfrm_input_afinfo xfrm6_input_afinfo = {
162 .family = AF_INET6,
163 .owner = THIS_MODULE,
164 .callback = xfrm6_rcv_cb,
165};
166
167static 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
181int 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
212err:
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}
224EXPORT_SYMBOL(xfrm6_protocol_register);
225
226int 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}
260EXPORT_SYMBOL(xfrm6_protocol_deregister);
261
262int __init xfrm6_protocol_init(void)
263{
264 return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
265}
266
267void xfrm6_protocol_fini(void)
268{
269 xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
270}