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 */ 355