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 * Declare IPSEC_SUPPORT as module even if IPSEC is defined. 130 * tcpmd5.ko module depends from IPSEC_SUPPORT. 131 */ 132 #define IPSEC_MODULE_INCR 2 133 static int 134 ipsec_kmod_enter(volatile u_int *cntr) 135 { 136 u_int old, new; 137 138 do { 139 old = *cntr; 140 if ((old & IPSEC_MODULE_ENABLED) == 0) 141 return (ENXIO); 142 new = old + IPSEC_MODULE_INCR; 143 } while(atomic_cmpset_acq_int(cntr, old, new) == 0); 144 return (0); 145 } 146 147 static void 148 ipsec_kmod_exit(volatile u_int *cntr) 149 { 150 u_int old, new; 151 152 do { 153 old = *cntr; 154 new = old - IPSEC_MODULE_INCR; 155 } while (atomic_cmpset_rel_int(cntr, old, new) == 0); 156 } 157 158 static void 159 ipsec_kmod_drain(volatile u_int *cntr) 160 { 161 u_int old, new; 162 163 do { 164 old = *cntr; 165 new = old & ~IPSEC_MODULE_ENABLED; 166 } while (atomic_cmpset_acq_int(cntr, old, new) == 0); 167 while (atomic_cmpset_int(cntr, 0, 0) == 0) 168 pause("ipsecd", hz/2); 169 } 170 171 #define METHOD_DECL(...) __VA_ARGS__ 172 #define METHOD_ARGS(...) __VA_ARGS__ 173 #define IPSEC_KMOD_METHOD(type, name, sc, method, decl, args) \ 174 type name (decl) \ 175 { \ 176 type ret = (type)ipsec_kmod_enter(&sc->enabled); \ 177 if (ret == 0) { \ 178 ret = (*sc->methods->method)(args); \ 179 ipsec_kmod_exit(&sc->enabled); \ 180 } \ 181 return (ret); \ 182 } 183 184 #ifndef TCP_SIGNATURE 185 /* Declare TCP-MD5 support as kernel module. */ 186 static struct tcpmd5_support tcpmd5_ipsec = { 187 .enabled = 0, 188 .methods = NULL 189 }; 190 struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec; 191 192 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc, 193 input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m, 194 struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf) 195 ) 196 197 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc, 198 output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m, 199 struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf) 200 ) 201 202 IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc, 203 pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp, 204 struct sockopt *sopt), METHOD_ARGS(inp, sopt) 205 ) 206 207 void 208 tcpmd5_support_enable(const struct tcpmd5_methods * const methods) 209 { 210 211 KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled")); 212 tcp_ipsec_support->methods = methods; 213 tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED; 214 } 215 216 void 217 tcpmd5_support_disable(void) 218 { 219 220 if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) { 221 ipsec_kmod_drain(&tcp_ipsec_support->enabled); 222 tcp_ipsec_support->methods = NULL; 223 } 224 } 225 #endif 226 227 static int 228 ipsec_support_modevent(module_t mod, int type, void *data) 229 { 230 231 switch (type) { 232 case MOD_LOAD: 233 return (0); 234 case MOD_UNLOAD: 235 return (EBUSY); 236 default: 237 return (EOPNOTSUPP); 238 } 239 } 240 241 static moduledata_t ipsec_support_mod = { 242 "ipsec_support", 243 ipsec_support_modevent, 244 0 245 }; 246 DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN, 247 SI_ORDER_ANY); 248 MODULE_VERSION(ipsec_support, 1); 249 250 #ifndef IPSEC 251 /* 252 * IPsec support is build as kernel module. 253 */ 254 #ifdef INET 255 static struct ipsec_support ipv4_ipsec = { 256 .enabled = 0, 257 .methods = NULL 258 }; 259 struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec; 260 261 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc, 262 udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 263 int off, int af), METHOD_ARGS(m, off, af) 264 ) 265 266 IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc, 267 udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp, 268 struct sockopt *sopt), METHOD_ARGS(inp, sopt) 269 ) 270 #endif 271 272 #ifdef INET6 273 static struct ipsec_support ipv6_ipsec = { 274 .enabled = 0, 275 .methods = NULL 276 }; 277 struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec; 278 #endif 279 280 IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc, 281 input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 282 int offset, int proto), METHOD_ARGS(m, offset, proto) 283 ) 284 285 IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc, 286 check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 287 struct inpcb *inp), METHOD_ARGS(m, inp) 288 ) 289 290 IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc, 291 forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m), 292 (m) 293 ) 294 295 IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc, 296 output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 297 struct inpcb *inp), METHOD_ARGS(m, inp) 298 ) 299 300 IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc, 301 pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp, 302 struct sockopt *sopt), METHOD_ARGS(inp, sopt) 303 ) 304 305 IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc, 306 hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp), 307 (inp) 308 ) 309 310 static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc, 311 capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m, 312 u_int cap), METHOD_ARGS(m, cap) 313 ) 314 315 int 316 ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m, 317 u_int cap) 318 { 319 320 /* 321 * Since PF_KEY is build in the kernel, we can directly 322 * call key_havesp() without additional synchronizations. 323 */ 324 if (cap == IPSEC_CAP_OPERABLE) 325 return (key_havesp(IPSEC_DIR_INBOUND) != 0 || 326 key_havesp(IPSEC_DIR_OUTBOUND) != 0); 327 return (ipsec_kmod_caps(sc, m, cap)); 328 } 329 330 void 331 ipsec_support_enable(struct ipsec_support * const sc, 332 const struct ipsec_methods * const methods) 333 { 334 335 KASSERT(sc->enabled == 0, ("IPsec already enabled")); 336 sc->methods = methods; 337 sc->enabled |= IPSEC_MODULE_ENABLED; 338 } 339 340 void 341 ipsec_support_disable(struct ipsec_support * const sc) 342 { 343 344 if (sc->enabled & IPSEC_MODULE_ENABLED) { 345 ipsec_kmod_drain(&sc->enabled); 346 sc->methods = NULL; 347 } 348 } 349 #endif /* !IPSEC */ 350 #endif /* IPSEC_SUPPORT */ 351