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