subr_ipsec.c revision 315514
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: stable/11/sys/netipsec/subr_ipsec.c 315514 2017-03-18 22:04:20Z ae $");
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
58#include <machine/atomic.h>
59/*
60 * This file is build in the kernel only when 'options IPSEC' or
61 * 'options IPSEC_SUPPORT' is enabled.
62 */
63
64#ifdef INET
65void
66ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
67    union sockaddr_union *dst)
68{
69	static const struct sockaddr_in template = {
70		sizeof (struct sockaddr_in),
71		AF_INET,
72		0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
73	};
74
75	src->sin = template;
76	dst->sin = template;
77
78	if (m->m_len < sizeof (struct ip)) {
79		m_copydata(m, offsetof(struct ip, ip_src),
80			   sizeof (struct  in_addr),
81			   (caddr_t) &src->sin.sin_addr);
82		m_copydata(m, offsetof(struct ip, ip_dst),
83			   sizeof (struct  in_addr),
84			   (caddr_t) &dst->sin.sin_addr);
85	} else {
86		const struct ip *ip = mtod(m, const struct ip *);
87		src->sin.sin_addr = ip->ip_src;
88		dst->sin.sin_addr = ip->ip_dst;
89	}
90}
91#endif
92#ifdef INET6
93void
94ipsec6_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
95    union sockaddr_union *dst)
96{
97	struct ip6_hdr ip6buf;
98	const struct ip6_hdr *ip6;
99
100	if (m->m_len >= sizeof(*ip6))
101		ip6 = mtod(m, const struct ip6_hdr *);
102	else {
103		m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
104		ip6 = &ip6buf;
105	}
106
107	bzero(&src->sin6, sizeof(struct sockaddr_in6));
108	src->sin6.sin6_family = AF_INET6;
109	src->sin6.sin6_len = sizeof(struct sockaddr_in6);
110	bcopy(&ip6->ip6_src, &src->sin6.sin6_addr, sizeof(ip6->ip6_src));
111	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
112		src->sin6.sin6_addr.s6_addr16[1] = 0;
113		src->sin6.sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
114	}
115
116	bzero(&dst->sin6, sizeof(struct sockaddr_in6));
117	dst->sin6.sin6_family = AF_INET6;
118	dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
119	bcopy(&ip6->ip6_dst, &dst->sin6.sin6_addr, sizeof(ip6->ip6_dst));
120	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
121		dst->sin6.sin6_addr.s6_addr16[1] = 0;
122		dst->sin6.sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
123	}
124}
125#endif
126
127#ifdef IPSEC_SUPPORT
128/*
129 * IPSEC_SUPPORT - loading of ipsec.ko and tcpmd5.ko is supported.
130 * IPSEC + IPSEC_SUPPORT - loading tcpmd5.ko is supported.
131 * IPSEC + TCP_SIGNATURE - all is build in the kernel, do not build
132 *   IPSEC_SUPPORT.
133 */
134#if !defined(IPSEC) || !defined(TCP_SIGNATURE)
135#define	IPSEC_MODULE_INCR	2
136static int
137ipsec_kmod_enter(volatile u_int *cntr)
138{
139	u_int old, new;
140
141	do {
142		old = *cntr;
143		if ((old & IPSEC_MODULE_ENABLED) == 0)
144			return (ENXIO);
145		new = old + IPSEC_MODULE_INCR;
146	} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
147	return (0);
148}
149
150static void
151ipsec_kmod_exit(volatile u_int *cntr)
152{
153	u_int old, new;
154
155	do {
156		old = *cntr;
157		new = old - IPSEC_MODULE_INCR;
158	} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
159}
160
161static void
162ipsec_kmod_drain(volatile u_int *cntr)
163{
164	u_int old, new;
165
166	do {
167		old = *cntr;
168		new = old & ~IPSEC_MODULE_ENABLED;
169	} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
170	while (atomic_cmpset_int(cntr, 0, 0) == 0)
171		pause("ipsecd", hz/2);
172}
173
174#define	METHOD_DECL(...)	__VA_ARGS__
175#define	METHOD_ARGS(...)	__VA_ARGS__
176#define	IPSEC_KMOD_METHOD(type, name, sc, method, decl, args)		\
177type name (decl)							\
178{									\
179	type ret = (type)ipsec_kmod_enter(&sc->enabled);		\
180	if (ret == 0) {							\
181		ret = (*sc->methods->method)(args);			\
182		ipsec_kmod_exit(&sc->enabled);				\
183	}								\
184	return (ret);							\
185}
186
187static int
188ipsec_support_modevent(module_t mod, int type, void *data)
189{
190
191	switch (type) {
192	case MOD_LOAD:
193		return (0);
194	case MOD_UNLOAD:
195		return (EBUSY);
196	default:
197		return (EOPNOTSUPP);
198	}
199}
200
201static moduledata_t ipsec_support_mod = {
202	"ipsec_support",
203	ipsec_support_modevent,
204	0
205};
206DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
207    SI_ORDER_ANY);
208MODULE_VERSION(ipsec_support, 1);
209#endif /* !IPSEC || !TCP_SIGNATURE */
210
211#ifndef TCP_SIGNATURE
212/* Declare TCP-MD5 support as kernel module. */
213static struct tcpmd5_support tcpmd5_ipsec = {
214	.enabled = 0,
215	.methods = NULL
216};
217struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
218
219IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
220    input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
221	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
222)
223
224IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
225    output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
226	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
227)
228
229IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
230    pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
231	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
232)
233
234void
235tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
236{
237
238	KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
239	tcp_ipsec_support->methods = methods;
240	tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
241}
242
243void
244tcpmd5_support_disable(void)
245{
246
247	if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
248		ipsec_kmod_drain(&tcp_ipsec_support->enabled);
249		tcp_ipsec_support->methods = NULL;
250	}
251}
252#endif /* !TCP_SIGNATURE */
253
254#ifndef IPSEC
255/*
256 * IPsec support is build as kernel module.
257 */
258#ifdef INET
259static struct ipsec_support ipv4_ipsec = {
260	.enabled = 0,
261	.methods = NULL
262};
263struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
264
265IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
266    udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
267	int off, int af), METHOD_ARGS(m, off, af)
268)
269
270IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
271    udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
272	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
273)
274#endif
275
276#ifdef INET6
277static struct ipsec_support ipv6_ipsec = {
278	.enabled = 0,
279	.methods = NULL
280};
281struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
282#endif
283
284IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
285    input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
286	int offset, int proto), METHOD_ARGS(m, offset, proto)
287)
288
289IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
290    check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
291	struct inpcb *inp), METHOD_ARGS(m, inp)
292)
293
294IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
295    forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
296    (m)
297)
298
299IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc,
300    output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
301	struct inpcb *inp), METHOD_ARGS(m, inp)
302)
303
304IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
305    pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
306	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
307)
308
309IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
310    hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
311    (inp)
312)
313
314static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
315    capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
316	u_int cap), METHOD_ARGS(m, cap)
317)
318
319int
320ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
321    u_int cap)
322{
323
324	/*
325	 * Since PF_KEY is build in the kernel, we can directly
326	 * call key_havesp() without additional synchronizations.
327	 */
328	if (cap == IPSEC_CAP_OPERABLE)
329		return (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
330		    key_havesp(IPSEC_DIR_OUTBOUND) != 0);
331	return (ipsec_kmod_caps(sc, m, cap));
332}
333
334void
335ipsec_support_enable(struct ipsec_support * const sc,
336    const struct ipsec_methods * const methods)
337{
338
339	KASSERT(sc->enabled == 0, ("IPsec already enabled"));
340	sc->methods = methods;
341	sc->enabled |= IPSEC_MODULE_ENABLED;
342}
343
344void
345ipsec_support_disable(struct ipsec_support * const sc)
346{
347
348	if (sc->enabled & IPSEC_MODULE_ENABLED) {
349		ipsec_kmod_drain(&sc->enabled);
350		sc->methods = NULL;
351	}
352}
353#endif /* !IPSEC */
354#endif /* IPSEC_SUPPORT */
355