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 63 void 64 ipsec4_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 91 void 92 ipsec6_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 126 static int 127 ipsec_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 140 static void 141 ipsec_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 151 static void 152 ipsec_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 164 static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER(); 165 static struct mtx xforms_lock; 166 MTX_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 170 void 171 xform_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 190 void 191 xform_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 */ 208 int 209 xform_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) \ 244 type 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 254 static int 255 ipsec_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 268 static moduledata_t ipsec_support_mod = { 269 "ipsec_support", 270 ipsec_support_modevent, 271 0 272 }; 273 DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN, 274 SI_ORDER_ANY); 275 MODULE_VERSION(ipsec_support, 1); 276 #endif /* !IPSEC || !TCP_SIGNATURE */ 277 278 #ifndef TCP_SIGNATURE 279 /* Declare TCP-MD5 support as kernel module. */ 280 static struct tcpmd5_support tcpmd5_ipsec = { 281 .enabled = 0, 282 .methods = NULL 283 }; 284 struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec; 285 286 IPSEC_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 291 IPSEC_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 296 IPSEC_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 301 void 302 tcpmd5_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 310 void 311 tcpmd5_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 326 static struct ipsec_support ipv4_ipsec = { 327 .enabled = 0, 328 .methods = NULL 329 }; 330 struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec; 331 #endif 332 333 #ifdef INET6 334 static struct ipsec_support ipv6_ipsec = { 335 .enabled = 0, 336 .methods = NULL 337 }; 338 struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec; 339 #endif 340 341 IPSEC_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 346 IPSEC_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 351 IPSEC_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 356 IPSEC_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 361 IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc, 362 forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m), 363 (m) 364 ) 365 366 IPSEC_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 371 IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc, output, 372 METHOD_DECL(struct ipsec_support * const sc, struct ifnet *ifp, 373 struct mbuf *m, struct inpcb *inp, u_long mtu), 374 METHOD_ARGS(ifp, m, inp, mtu) 375 ) 376 377 IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc, 378 pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp, 379 struct sockopt *sopt), METHOD_ARGS(inp, sopt) 380 ) 381 382 IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc, 383 hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp), 384 (inp) 385 ) 386 387 static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc, 388 capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 389 u_int cap), METHOD_ARGS(m, cap) 390 ) 391 392 int 393 ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m, 394 u_int cap) 395 { 396 397 /* 398 * Since PF_KEY is build in the kernel, we can directly 399 * call key_havesp() without additional synchronizations. 400 */ 401 if (cap == IPSEC_CAP_OPERABLE) 402 return (key_havesp_any()); 403 return (ipsec_kmod_caps(sc, m, cap)); 404 } 405 406 void 407 ipsec_support_enable(struct ipsec_support * const sc, 408 const struct ipsec_methods * const methods) 409 { 410 411 KASSERT(sc->enabled == 0, ("IPsec already enabled")); 412 sc->methods = methods; 413 sc->enabled |= IPSEC_MODULE_ENABLED; 414 } 415 416 void 417 ipsec_support_disable(struct ipsec_support * const sc) 418 { 419 420 if (sc->enabled & IPSEC_MODULE_ENABLED) { 421 ipsec_kmod_drain(&sc->enabled); 422 sc->methods = NULL; 423 } 424 } 425 #endif /* !IPSEC */ 426 #endif /* IPSEC_SUPPORT */ 427