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
66void
67ipsec4_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
94void
95ipsec6_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
129static int
130ipsec_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
143static void
144ipsec_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
154static void
155ipsec_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
167static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
168static struct mtx xforms_lock;
169MTX_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
173void
174xform_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
193void
194xform_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 */
211int
212xform_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)		\
247type 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
257static int
258ipsec_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
271static moduledata_t ipsec_support_mod = {
272	"ipsec_support",
273	ipsec_support_modevent,
274	0
275};
276DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
277    SI_ORDER_ANY);
278MODULE_VERSION(ipsec_support, 1);
279#endif /* !IPSEC || !TCP_SIGNATURE */
280
281#ifndef TCP_SIGNATURE
282/* Declare TCP-MD5 support as kernel module. */
283static struct tcpmd5_support tcpmd5_ipsec = {
284	.enabled = 0,
285	.methods = NULL
286};
287struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
288
289IPSEC_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
294IPSEC_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
299IPSEC_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
304void
305tcpmd5_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
313void
314tcpmd5_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
329static struct ipsec_support ipv4_ipsec = {
330	.enabled = 0,
331	.methods = NULL
332};
333struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
334
335IPSEC_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
340IPSEC_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
347static struct ipsec_support ipv6_ipsec = {
348	.enabled = 0,
349	.methods = NULL
350};
351struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
352#endif
353
354IPSEC_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
359IPSEC_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
364IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
365    forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
366    (m)
367)
368
369IPSEC_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
374IPSEC_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
379IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
380    hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
381    (inp)
382)
383
384static 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
389int
390ipsec_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
404void
405ipsec_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
414void
415ipsec_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