xref: /freebsd/sys/netipsec/subr_ipsec.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
1 /*-
2  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
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/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/module.h>
41 #include <sys/priv.h>
42 #include <sys/socket.h>
43 #include <sys/sockopt.h>
44 #include <sys/syslog.h>
45 #include <sys/proc.h>
46 
47 #include <netinet/in.h>
48 #include <netinet/in_pcb.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip6.h>
51 
52 #include <netipsec/ipsec_support.h>
53 #include <netipsec/ipsec.h>
54 #include <netipsec/ipsec6.h>
55 #include <netipsec/key.h>
56 #include <netipsec/key_debug.h>
57 #include <netipsec/xform.h>
58 
59 #include <machine/atomic.h>
60 /*
61  * This file is build in the kernel only when 'options IPSEC' or
62  * 'options IPSEC_SUPPORT' is enabled.
63  */
64 
65 #ifdef INET
66 void
67 ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
68     union sockaddr_union *dst)
69 {
70 	static const struct sockaddr_in template = {
71 		sizeof (struct sockaddr_in),
72 		AF_INET,
73 		0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
74 	};
75 
76 	src->sin = template;
77 	dst->sin = template;
78 
79 	if (m->m_len < sizeof (struct ip)) {
80 		m_copydata(m, offsetof(struct ip, ip_src),
81 			   sizeof (struct  in_addr),
82 			   (caddr_t) &src->sin.sin_addr);
83 		m_copydata(m, offsetof(struct ip, ip_dst),
84 			   sizeof (struct  in_addr),
85 			   (caddr_t) &dst->sin.sin_addr);
86 	} else {
87 		const struct ip *ip = mtod(m, const struct ip *);
88 		src->sin.sin_addr = ip->ip_src;
89 		dst->sin.sin_addr = ip->ip_dst;
90 	}
91 }
92 #endif
93 #ifdef INET6
94 void
95 ipsec6_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
96     union sockaddr_union *dst)
97 {
98 	struct ip6_hdr ip6buf;
99 	const struct ip6_hdr *ip6;
100 
101 	if (m->m_len >= sizeof(*ip6))
102 		ip6 = mtod(m, const struct ip6_hdr *);
103 	else {
104 		m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
105 		ip6 = &ip6buf;
106 	}
107 
108 	bzero(&src->sin6, sizeof(struct sockaddr_in6));
109 	src->sin6.sin6_family = AF_INET6;
110 	src->sin6.sin6_len = sizeof(struct sockaddr_in6);
111 	bcopy(&ip6->ip6_src, &src->sin6.sin6_addr, sizeof(ip6->ip6_src));
112 	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
113 		src->sin6.sin6_addr.s6_addr16[1] = 0;
114 		src->sin6.sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
115 	}
116 
117 	bzero(&dst->sin6, sizeof(struct sockaddr_in6));
118 	dst->sin6.sin6_family = AF_INET6;
119 	dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
120 	bcopy(&ip6->ip6_dst, &dst->sin6.sin6_addr, sizeof(ip6->ip6_dst));
121 	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
122 		dst->sin6.sin6_addr.s6_addr16[1] = 0;
123 		dst->sin6.sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
124 	}
125 }
126 #endif
127 
128 #define	IPSEC_MODULE_INCR	2
129 static int
130 ipsec_kmod_enter(volatile u_int *cntr)
131 {
132 	u_int old, new;
133 
134 	do {
135 		old = *cntr;
136 		if ((old & IPSEC_MODULE_ENABLED) == 0)
137 			return (ENXIO);
138 		new = old + IPSEC_MODULE_INCR;
139 	} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
140 	return (0);
141 }
142 
143 static void
144 ipsec_kmod_exit(volatile u_int *cntr)
145 {
146 	u_int old, new;
147 
148 	do {
149 		old = *cntr;
150 		new = old - IPSEC_MODULE_INCR;
151 	} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
152 }
153 
154 static void
155 ipsec_kmod_drain(volatile u_int *cntr)
156 {
157 	u_int old, new;
158 
159 	do {
160 		old = *cntr;
161 		new = old & ~IPSEC_MODULE_ENABLED;
162 	} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
163 	while (atomic_cmpset_int(cntr, 0, 0) == 0)
164 		pause("ipsecd", hz/2);
165 }
166 
167 static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
168 static struct mtx xforms_lock;
169 MTX_SYSINIT(xfroms_list, &xforms_lock, "IPsec transforms list", MTX_DEF);
170 #define	XFORMS_LOCK()		mtx_lock(&xforms_lock)
171 #define	XFORMS_UNLOCK()		mtx_unlock(&xforms_lock)
172 
173 void
174 xform_attach(void *data)
175 {
176 	struct xformsw *xsp, *entry;
177 
178 	xsp = (struct xformsw *)data;
179 	XFORMS_LOCK();
180 	LIST_FOREACH(entry, &xforms, chain) {
181 		if (entry->xf_type == xsp->xf_type) {
182 			XFORMS_UNLOCK();
183 			printf("%s: failed to register %s xform\n",
184 			    __func__, xsp->xf_name);
185 			return;
186 		}
187 	}
188 	LIST_INSERT_HEAD(&xforms, xsp, chain);
189 	xsp->xf_cntr = IPSEC_MODULE_ENABLED;
190 	XFORMS_UNLOCK();
191 }
192 
193 void
194 xform_detach(void *data)
195 {
196 	struct xformsw *xsp = (struct xformsw *)data;
197 
198 	XFORMS_LOCK();
199 	LIST_REMOVE(xsp, chain);
200 	XFORMS_UNLOCK();
201 
202 	/* Delete all SAs related to this xform. */
203 	key_delete_xform(xsp);
204 	if (xsp->xf_cntr & IPSEC_MODULE_ENABLED)
205 		ipsec_kmod_drain(&xsp->xf_cntr);
206 }
207 
208 /*
209  * Initialize transform support in an sav.
210  */
211 int
212 xform_init(struct secasvar *sav, u_short xftype)
213 {
214 	struct xformsw *entry;
215 	int ret;
216 
217 	IPSEC_ASSERT(sav->tdb_xform == NULL,
218 	    ("tdb_xform is already initialized"));
219 
220 	XFORMS_LOCK();
221 	LIST_FOREACH(entry, &xforms, chain) {
222 		if (entry->xf_type == xftype) {
223 			ret = ipsec_kmod_enter(&entry->xf_cntr);
224 			XFORMS_UNLOCK();
225 			if (ret != 0)
226 				return (ret);
227 			ret = (*entry->xf_init)(sav, entry);
228 			ipsec_kmod_exit(&entry->xf_cntr);
229 			return (ret);
230 		}
231 	}
232 	XFORMS_UNLOCK();
233 	return (EINVAL);
234 }
235 
236 #ifdef IPSEC_SUPPORT
237 /*
238  * IPSEC_SUPPORT - loading of ipsec.ko and tcpmd5.ko is supported.
239  * IPSEC + IPSEC_SUPPORT - loading tcpmd5.ko is supported.
240  * IPSEC + TCP_SIGNATURE - all is build in the kernel, do not build
241  *   IPSEC_SUPPORT.
242  */
243 #if !defined(IPSEC) || !defined(TCP_SIGNATURE)
244 #define	METHOD_DECL(...)	__VA_ARGS__
245 #define	METHOD_ARGS(...)	__VA_ARGS__
246 #define	IPSEC_KMOD_METHOD(type, name, sc, method, decl, args)		\
247 type name (decl)							\
248 {									\
249 	type ret = (type)ipsec_kmod_enter(&sc->enabled);		\
250 	if (ret == 0) {							\
251 		ret = (*sc->methods->method)(args);			\
252 		ipsec_kmod_exit(&sc->enabled);				\
253 	}								\
254 	return (ret);							\
255 }
256 
257 static int
258 ipsec_support_modevent(module_t mod, int type, void *data)
259 {
260 
261 	switch (type) {
262 	case MOD_LOAD:
263 		return (0);
264 	case MOD_UNLOAD:
265 		return (EBUSY);
266 	default:
267 		return (EOPNOTSUPP);
268 	}
269 }
270 
271 static moduledata_t ipsec_support_mod = {
272 	"ipsec_support",
273 	ipsec_support_modevent,
274 	0
275 };
276 DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
277     SI_ORDER_ANY);
278 MODULE_VERSION(ipsec_support, 1);
279 #endif /* !IPSEC || !TCP_SIGNATURE */
280 
281 #ifndef TCP_SIGNATURE
282 /* Declare TCP-MD5 support as kernel module. */
283 static struct tcpmd5_support tcpmd5_ipsec = {
284 	.enabled = 0,
285 	.methods = NULL
286 };
287 struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
288 
289 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
290     input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
291 	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
292 )
293 
294 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
295     output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
296 	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
297 )
298 
299 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
300     pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
301 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
302 )
303 
304 void
305 tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
306 {
307 
308 	KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
309 	tcp_ipsec_support->methods = methods;
310 	tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
311 }
312 
313 void
314 tcpmd5_support_disable(void)
315 {
316 
317 	if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
318 		ipsec_kmod_drain(&tcp_ipsec_support->enabled);
319 		tcp_ipsec_support->methods = NULL;
320 	}
321 }
322 #endif /* !TCP_SIGNATURE */
323 
324 #ifndef IPSEC
325 /*
326  * IPsec support is build as kernel module.
327  */
328 #ifdef INET
329 static struct ipsec_support ipv4_ipsec = {
330 	.enabled = 0,
331 	.methods = NULL
332 };
333 struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
334 
335 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
336     udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
337 	int off, int af), METHOD_ARGS(m, off, af)
338 )
339 
340 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
341     udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
342 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
343 )
344 #endif
345 
346 #ifdef INET6
347 static struct ipsec_support ipv6_ipsec = {
348 	.enabled = 0,
349 	.methods = NULL
350 };
351 struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
352 #endif
353 
354 IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
355     input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
356 	int offset, int proto), METHOD_ARGS(m, offset, proto)
357 )
358 
359 IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
360     check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
361 	struct inpcb *inp), METHOD_ARGS(m, inp)
362 )
363 
364 IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
365     forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
366     (m)
367 )
368 
369 IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc,
370     output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
371 	struct inpcb *inp), METHOD_ARGS(m, inp)
372 )
373 
374 IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
375     pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
376 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
377 )
378 
379 IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
380     hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
381     (inp)
382 )
383 
384 static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
385     capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
386 	u_int cap), METHOD_ARGS(m, cap)
387 )
388 
389 int
390 ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
391     u_int cap)
392 {
393 
394 	/*
395 	 * Since PF_KEY is build in the kernel, we can directly
396 	 * call key_havesp() without additional synchronizations.
397 	 */
398 	if (cap == IPSEC_CAP_OPERABLE)
399 		return (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
400 		    key_havesp(IPSEC_DIR_OUTBOUND) != 0);
401 	return (ipsec_kmod_caps(sc, m, cap));
402 }
403 
404 void
405 ipsec_support_enable(struct ipsec_support * const sc,
406     const struct ipsec_methods * const methods)
407 {
408 
409 	KASSERT(sc->enabled == 0, ("IPsec already enabled"));
410 	sc->methods = methods;
411 	sc->enabled |= IPSEC_MODULE_ENABLED;
412 }
413 
414 void
415 ipsec_support_disable(struct ipsec_support * const sc)
416 {
417 
418 	if (sc->enabled & IPSEC_MODULE_ENABLED) {
419 		ipsec_kmod_drain(&sc->enabled);
420 		sc->methods = NULL;
421 	}
422 }
423 #endif /* !IPSEC */
424 #endif /* IPSEC_SUPPORT */
425