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