xref: /freebsd/sys/netipsec/subr_ipsec.c (revision 9fc7a59f2a9baa13d62a86a10d97652ca06caa5f)
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 
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
65 void
66 ipsec4_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
93 void
94 ipsec6_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  * Declare IPSEC_SUPPORT as module even if IPSEC is defined.
130  * tcpmd5.ko module depends from IPSEC_SUPPORT.
131  */
132 #define	IPSEC_MODULE_INCR	2
133 static int
134 ipsec_kmod_enter(volatile u_int *cntr)
135 {
136 	u_int old, new;
137 
138 	do {
139 		old = *cntr;
140 		if ((old & IPSEC_MODULE_ENABLED) == 0)
141 			return (ENXIO);
142 		new = old + IPSEC_MODULE_INCR;
143 	} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
144 	return (0);
145 }
146 
147 static void
148 ipsec_kmod_exit(volatile u_int *cntr)
149 {
150 	u_int old, new;
151 
152 	do {
153 		old = *cntr;
154 		new = old - IPSEC_MODULE_INCR;
155 	} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
156 }
157 
158 static void
159 ipsec_kmod_drain(volatile u_int *cntr)
160 {
161 	u_int old, new;
162 
163 	do {
164 		old = *cntr;
165 		new = old & ~IPSEC_MODULE_ENABLED;
166 	} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
167 	while (atomic_cmpset_int(cntr, 0, 0) == 0)
168 		pause("ipsecd", hz/2);
169 }
170 
171 #define	METHOD_DECL(...)	__VA_ARGS__
172 #define	METHOD_ARGS(...)	__VA_ARGS__
173 #define	IPSEC_KMOD_METHOD(type, name, sc, method, decl, args)		\
174 type name (decl)							\
175 {									\
176 	type ret = (type)ipsec_kmod_enter(&sc->enabled);		\
177 	if (ret == 0) {							\
178 		ret = (*sc->methods->method)(args);			\
179 		ipsec_kmod_exit(&sc->enabled);				\
180 	}								\
181 	return (ret);							\
182 }
183 
184 #ifndef TCP_SIGNATURE
185 /* Declare TCP-MD5 support as kernel module. */
186 static struct tcpmd5_support tcpmd5_ipsec = {
187 	.enabled = 0,
188 	.methods = NULL
189 };
190 struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
191 
192 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
193     input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
194 	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
195 )
196 
197 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
198     output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
199 	struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
200 )
201 
202 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
203     pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
204 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
205 )
206 
207 void
208 tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
209 {
210 
211 	KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
212 	tcp_ipsec_support->methods = methods;
213 	tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
214 }
215 
216 void
217 tcpmd5_support_disable(void)
218 {
219 
220 	if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
221 		ipsec_kmod_drain(&tcp_ipsec_support->enabled);
222 		tcp_ipsec_support->methods = NULL;
223 	}
224 }
225 #endif
226 
227 static int
228 ipsec_support_modevent(module_t mod, int type, void *data)
229 {
230 
231 	switch (type) {
232 	case MOD_LOAD:
233 		return (0);
234 	case MOD_UNLOAD:
235 		return (EBUSY);
236 	default:
237 		return (EOPNOTSUPP);
238 	}
239 }
240 
241 static moduledata_t ipsec_support_mod = {
242 	"ipsec_support",
243 	ipsec_support_modevent,
244 	0
245 };
246 DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
247     SI_ORDER_ANY);
248 MODULE_VERSION(ipsec_support, 1);
249 
250 #ifndef IPSEC
251 /*
252  * IPsec support is build as kernel module.
253  */
254 #ifdef INET
255 static struct ipsec_support ipv4_ipsec = {
256 	.enabled = 0,
257 	.methods = NULL
258 };
259 struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
260 
261 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
262     udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
263 	int off, int af), METHOD_ARGS(m, off, af)
264 )
265 
266 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
267     udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
268 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
269 )
270 #endif
271 
272 #ifdef INET6
273 static struct ipsec_support ipv6_ipsec = {
274 	.enabled = 0,
275 	.methods = NULL
276 };
277 struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
278 #endif
279 
280 IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
281     input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
282 	int offset, int proto), METHOD_ARGS(m, offset, proto)
283 )
284 
285 IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
286     check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
287 	struct inpcb *inp), METHOD_ARGS(m, inp)
288 )
289 
290 IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
291     forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
292     (m)
293 )
294 
295 IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc,
296     output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
297 	struct inpcb *inp), METHOD_ARGS(m, inp)
298 )
299 
300 IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
301     pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
302 	struct sockopt *sopt), METHOD_ARGS(inp, sopt)
303 )
304 
305 IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
306     hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
307     (inp)
308 )
309 
310 static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
311     capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
312 	u_int cap), METHOD_ARGS(m, cap)
313 )
314 
315 int
316 ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
317     u_int cap)
318 {
319 
320 	/*
321 	 * Since PF_KEY is build in the kernel, we can directly
322 	 * call key_havesp() without additional synchronizations.
323 	 */
324 	if (cap == IPSEC_CAP_OPERABLE)
325 		return (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
326 		    key_havesp(IPSEC_DIR_OUTBOUND) != 0);
327 	return (ipsec_kmod_caps(sc, m, cap));
328 }
329 
330 void
331 ipsec_support_enable(struct ipsec_support * const sc,
332     const struct ipsec_methods * const methods)
333 {
334 
335 	KASSERT(sc->enabled == 0, ("IPsec already enabled"));
336 	sc->methods = methods;
337 	sc->enabled |= IPSEC_MODULE_ENABLED;
338 }
339 
340 void
341 ipsec_support_disable(struct ipsec_support * const sc)
342 {
343 
344 	if (sc->enabled & IPSEC_MODULE_ENABLED) {
345 		ipsec_kmod_drain(&sc->enabled);
346 		sc->methods = NULL;
347 	}
348 }
349 #endif /* !IPSEC */
350 #endif /* IPSEC_SUPPORT */
351