]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netipsec/subr_ipsec.c
MFC r304572 (by bz):
[FreeBSD/FreeBSD.git] / sys / netipsec / subr_ipsec.c
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  * 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
136 static int
137 ipsec_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
150 static void
151 ipsec_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
161 static void
162 ipsec_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)           \
177 type 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
187 static int
188 ipsec_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
201 static moduledata_t ipsec_support_mod = {
202         "ipsec_support",
203         ipsec_support_modevent,
204         0
205 };
206 DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
207     SI_ORDER_ANY);
208 MODULE_VERSION(ipsec_support, 1);
209 #endif /* !IPSEC || !TCP_SIGNATURE */
210
211 #ifndef TCP_SIGNATURE
212 /* Declare TCP-MD5 support as kernel module. */
213 static struct tcpmd5_support tcpmd5_ipsec = {
214         .enabled = 0,
215         .methods = NULL
216 };
217 struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
218
219 IPSEC_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
224 IPSEC_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
229 IPSEC_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
234 void
235 tcpmd5_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
243 void
244 tcpmd5_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
259 static struct ipsec_support ipv4_ipsec = {
260         .enabled = 0,
261         .methods = NULL
262 };
263 struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
264
265 IPSEC_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
270 IPSEC_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
277 static struct ipsec_support ipv6_ipsec = {
278         .enabled = 0,
279         .methods = NULL
280 };
281 struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
282 #endif
283
284 IPSEC_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
289 IPSEC_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
294 IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
295     forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
296     (m)
297 )
298
299 IPSEC_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
304 IPSEC_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
309 IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
310     hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
311     (inp)
312 )
313
314 static 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
319 int
320 ipsec_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
334 void
335 ipsec_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
344 void
345 ipsec_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 */