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
63void
64ipsec4_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
91void
92ipsec6_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
126static int
127ipsec_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
140static void
141ipsec_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
151static void
152ipsec_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
164static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
165static struct mtx xforms_lock;
166MTX_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
170void
171xform_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
190void
191xform_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 */
208int
209xform_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)		\
244type 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
254static int
255ipsec_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
268static moduledata_t ipsec_support_mod = {
269	"ipsec_support",
270	ipsec_support_modevent,
271	0
272};
273DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
274    SI_ORDER_ANY);
275MODULE_VERSION(ipsec_support, 1);
276#endif /* !IPSEC || !TCP_SIGNATURE */
277
278#ifndef TCP_SIGNATURE
279/* Declare TCP-MD5 support as kernel module. */
280static struct tcpmd5_support tcpmd5_ipsec = {
281	.enabled = 0,
282	.methods = NULL
283};
284struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
285
286IPSEC_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
291IPSEC_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
296IPSEC_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
301void
302tcpmd5_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
310void
311tcpmd5_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
326static struct ipsec_support ipv4_ipsec = {
327	.enabled = 0,
328	.methods = NULL
329};
330struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
331#endif
332
333#ifdef INET6
334static struct ipsec_support ipv6_ipsec = {
335	.enabled = 0,
336	.methods = NULL
337};
338struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
339#endif
340
341IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
342    udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
343	int off, int af), METHOD_ARGS(m, off, af)
344)
345
346IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
347    udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
348	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
349)
350
351IPSEC_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
356IPSEC_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
361IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
362    forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
363    (m)
364)
365
366IPSEC_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
371IPSEC_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
376IPSEC_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
381IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
382    hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
383    (inp)
384)
385
386static 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
391int
392ipsec_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
405void
406ipsec_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
415void
416ipsec_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