Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netipsec/subr_ipsec.c
39475 views
1
/*-
2
* Copyright (c) 2016 Andrey V. Elsukov <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
*
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
*/
26
27
#include "opt_inet.h"
28
#include "opt_inet6.h"
29
#include "opt_ipsec.h"
30
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/kernel.h>
34
#include <sys/lock.h>
35
#include <sys/malloc.h>
36
#include <sys/mbuf.h>
37
#include <sys/module.h>
38
#include <sys/priv.h>
39
#include <sys/socket.h>
40
#include <sys/sockopt.h>
41
#include <sys/syslog.h>
42
#include <sys/proc.h>
43
44
#include <netinet/in.h>
45
#include <netinet/in_pcb.h>
46
#include <netinet/ip.h>
47
#include <netinet/ip6.h>
48
49
#include <netipsec/ipsec_support.h>
50
#include <netipsec/ipsec.h>
51
#include <netipsec/ipsec6.h>
52
#include <netipsec/key.h>
53
#include <netipsec/key_debug.h>
54
#include <netipsec/xform.h>
55
56
#include <machine/atomic.h>
57
/*
58
* This file is build in the kernel only when 'options IPSEC' or
59
* 'options IPSEC_SUPPORT' is enabled.
60
*/
61
62
#ifdef INET
63
void
64
ipsec4_setsockaddrs(const struct mbuf *m, const struct ip *ip1,
65
union sockaddr_union *src, union sockaddr_union *dst)
66
{
67
static const struct sockaddr_in template = {
68
sizeof (struct sockaddr_in),
69
AF_INET,
70
0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
71
};
72
73
src->sin = template;
74
dst->sin = template;
75
76
src->sin.sin_addr = ip1->ip_src;
77
dst->sin.sin_addr = ip1->ip_dst;
78
}
79
#endif
80
#ifdef INET6
81
void
82
ipsec6_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
83
union sockaddr_union *dst)
84
{
85
struct ip6_hdr ip6buf;
86
const struct ip6_hdr *ip6;
87
88
if (m->m_len >= sizeof(*ip6))
89
ip6 = mtod(m, const struct ip6_hdr *);
90
else {
91
m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
92
ip6 = &ip6buf;
93
}
94
95
bzero(&src->sin6, sizeof(struct sockaddr_in6));
96
src->sin6.sin6_family = AF_INET6;
97
src->sin6.sin6_len = sizeof(struct sockaddr_in6);
98
bcopy(&ip6->ip6_src, &src->sin6.sin6_addr, sizeof(ip6->ip6_src));
99
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
100
src->sin6.sin6_addr.s6_addr16[1] = 0;
101
src->sin6.sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
102
}
103
104
bzero(&dst->sin6, sizeof(struct sockaddr_in6));
105
dst->sin6.sin6_family = AF_INET6;
106
dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
107
bcopy(&ip6->ip6_dst, &dst->sin6.sin6_addr, sizeof(ip6->ip6_dst));
108
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
109
dst->sin6.sin6_addr.s6_addr16[1] = 0;
110
dst->sin6.sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
111
}
112
}
113
#endif
114
115
#define IPSEC_MODULE_INCR 2
116
static int
117
ipsec_kmod_enter(volatile u_int *cntr)
118
{
119
u_int old, new;
120
121
do {
122
old = *cntr;
123
if ((old & IPSEC_MODULE_ENABLED) == 0)
124
return (ENXIO);
125
new = old + IPSEC_MODULE_INCR;
126
} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
127
return (0);
128
}
129
130
static void
131
ipsec_kmod_exit(volatile u_int *cntr)
132
{
133
u_int old, new;
134
135
do {
136
old = *cntr;
137
new = old - IPSEC_MODULE_INCR;
138
} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
139
}
140
141
static void
142
ipsec_kmod_drain(volatile u_int *cntr)
143
{
144
u_int old, new;
145
146
do {
147
old = *cntr;
148
new = old & ~IPSEC_MODULE_ENABLED;
149
} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
150
while (atomic_cmpset_int(cntr, 0, 0) == 0)
151
pause("ipsecd", hz/2);
152
}
153
154
static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
155
static struct mtx xforms_lock;
156
MTX_SYSINIT(xfroms_list, &xforms_lock, "IPsec transforms list", MTX_DEF);
157
#define XFORMS_LOCK() mtx_lock(&xforms_lock)
158
#define XFORMS_UNLOCK() mtx_unlock(&xforms_lock)
159
160
void
161
xform_attach(void *data)
162
{
163
struct xformsw *xsp, *entry;
164
165
xsp = (struct xformsw *)data;
166
XFORMS_LOCK();
167
LIST_FOREACH(entry, &xforms, chain) {
168
if (entry->xf_type == xsp->xf_type) {
169
XFORMS_UNLOCK();
170
printf("%s: failed to register %s xform\n",
171
__func__, xsp->xf_name);
172
return;
173
}
174
}
175
LIST_INSERT_HEAD(&xforms, xsp, chain);
176
xsp->xf_cntr = IPSEC_MODULE_ENABLED;
177
XFORMS_UNLOCK();
178
}
179
180
void
181
xform_detach(void *data)
182
{
183
struct xformsw *xsp = (struct xformsw *)data;
184
185
XFORMS_LOCK();
186
LIST_REMOVE(xsp, chain);
187
XFORMS_UNLOCK();
188
189
/* Delete all SAs related to this xform. */
190
key_delete_xform(xsp);
191
if (xsp->xf_cntr & IPSEC_MODULE_ENABLED)
192
ipsec_kmod_drain(&xsp->xf_cntr);
193
}
194
195
/*
196
* Initialize transform support in an sav.
197
*/
198
int
199
xform_init(struct secasvar *sav, u_short xftype)
200
{
201
struct xformsw *entry;
202
int ret;
203
204
IPSEC_ASSERT(sav->tdb_xform == NULL,
205
("tdb_xform is already initialized"));
206
207
XFORMS_LOCK();
208
LIST_FOREACH(entry, &xforms, chain) {
209
if (entry->xf_type == xftype) {
210
ret = ipsec_kmod_enter(&entry->xf_cntr);
211
XFORMS_UNLOCK();
212
if (ret != 0)
213
return (ret);
214
ret = (*entry->xf_init)(sav, entry);
215
ipsec_kmod_exit(&entry->xf_cntr);
216
return (ret);
217
}
218
}
219
XFORMS_UNLOCK();
220
return (EINVAL);
221
}
222
223
#ifdef IPSEC_SUPPORT
224
/*
225
* IPSEC_SUPPORT - loading of ipsec.ko and tcpmd5.ko is supported.
226
* IPSEC + IPSEC_SUPPORT - loading tcpmd5.ko is supported.
227
* IPSEC + TCP_SIGNATURE - all is build in the kernel, do not build
228
* IPSEC_SUPPORT.
229
*/
230
#if !defined(IPSEC) || !defined(TCP_SIGNATURE)
231
#define METHOD_DECL(...) __VA_ARGS__
232
#define METHOD_ARGS(...) __VA_ARGS__
233
#define IPSEC_KMOD_METHOD(type, name, sc, method, decl, args) \
234
type name (decl) \
235
{ \
236
type ret = (type)ipsec_kmod_enter(&sc->enabled); \
237
if (ret == 0) { \
238
ret = (*sc->methods->method)(args); \
239
ipsec_kmod_exit(&sc->enabled); \
240
} \
241
return (ret); \
242
}
243
244
static int
245
ipsec_support_modevent(module_t mod, int type, void *data)
246
{
247
248
switch (type) {
249
case MOD_LOAD:
250
return (0);
251
case MOD_UNLOAD:
252
return (EBUSY);
253
default:
254
return (EOPNOTSUPP);
255
}
256
}
257
258
static moduledata_t ipsec_support_mod = {
259
"ipsec_support",
260
ipsec_support_modevent,
261
0
262
};
263
DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
264
SI_ORDER_ANY);
265
MODULE_VERSION(ipsec_support, 1);
266
#endif /* !IPSEC || !TCP_SIGNATURE */
267
268
#ifndef TCP_SIGNATURE
269
/* Declare TCP-MD5 support as kernel module. */
270
static struct tcpmd5_support tcpmd5_ipsec = {
271
.enabled = 0,
272
.methods = NULL
273
};
274
struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
275
276
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
277
input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
278
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
279
)
280
281
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
282
output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
283
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
284
)
285
286
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
287
pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
288
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
289
)
290
291
void
292
tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
293
{
294
295
KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
296
tcp_ipsec_support->methods = methods;
297
tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
298
}
299
300
void
301
tcpmd5_support_disable(void)
302
{
303
304
if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
305
ipsec_kmod_drain(&tcp_ipsec_support->enabled);
306
tcp_ipsec_support->methods = NULL;
307
}
308
}
309
#endif /* !TCP_SIGNATURE */
310
311
#ifndef IPSEC
312
/*
313
* IPsec support is build as kernel module.
314
*/
315
#ifdef INET
316
static struct ipsec_support ipv4_ipsec = {
317
.enabled = 0,
318
.methods = NULL
319
};
320
struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
321
#endif
322
323
#ifdef INET6
324
static struct ipsec_support ipv6_ipsec = {
325
.enabled = 0,
326
.methods = NULL
327
};
328
struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
329
#endif
330
331
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
332
udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
333
int off, int af), METHOD_ARGS(m, off, af)
334
)
335
336
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
337
udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
338
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
339
)
340
341
IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
342
input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
343
int offset, int proto), METHOD_ARGS(m, offset, proto)
344
)
345
346
IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
347
check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
348
struct inpcb *inp), METHOD_ARGS(m, inp)
349
)
350
351
IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
352
forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
353
(m)
354
)
355
356
IPSEC_KMOD_METHOD(int, ipsec_kmod_ctlinput, sc,
357
ctlinput, METHOD_DECL(struct ipsec_support * const sc,
358
ipsec_ctlinput_param_t param), METHOD_ARGS(param)
359
)
360
361
IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc, output,
362
METHOD_DECL(struct ipsec_support * const sc, struct ifnet *ifp,
363
struct mbuf *m, struct inpcb *inp, u_long mtu),
364
METHOD_ARGS(ifp, m, inp, mtu)
365
)
366
367
IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
368
pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
369
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
370
)
371
372
IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
373
hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
374
(inp)
375
)
376
377
static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
378
capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
379
u_int cap), METHOD_ARGS(m, cap)
380
)
381
382
int
383
ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
384
u_int cap)
385
{
386
387
/*
388
* Since PF_KEY is build in the kernel, we can directly
389
* call key_havesp() without additional synchronizations.
390
*/
391
if (cap == IPSEC_CAP_OPERABLE)
392
return (key_havesp_any());
393
return (ipsec_kmod_caps(sc, m, cap));
394
}
395
396
void
397
ipsec_support_enable(struct ipsec_support * const sc,
398
const struct ipsec_methods * const methods)
399
{
400
401
KASSERT(sc->enabled == 0, ("IPsec already enabled"));
402
sc->methods = methods;
403
sc->enabled |= IPSEC_MODULE_ENABLED;
404
}
405
406
void
407
ipsec_support_disable(struct ipsec_support * const sc)
408
{
409
410
if (sc->enabled & IPSEC_MODULE_ENABLED) {
411
ipsec_kmod_drain(&sc->enabled);
412
sc->methods = NULL;
413
}
414
}
415
#endif /* !IPSEC */
416
#endif /* IPSEC_SUPPORT */
417
418