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