1 /* $OpenBSD: if_sec.c,v 1.7 2023/08/15 09:46:30 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2022 The University of Queensland 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * This code was written by David Gwynne <dlg@uq.edu.au> as part 21 * of the Information Technology Infrastructure Group (ITIG) in the 22 * Faculty of Engineering, Architecture and Information Technology 23 * (EAIT). 24 */ 25 26 #ifndef IPSEC 27 #error sec enabled without IPSEC defined 28 #endif 29 30 #include "bpfilter.h" 31 #include "pf.h" 32 33 #include <sys/param.h> 34 #include <sys/mbuf.h> 35 #include <sys/socket.h> 36 #include <sys/sockio.h> 37 #include <sys/systm.h> 38 #include <sys/errno.h> 39 #include <sys/smr.h> 40 #include <sys/refcnt.h> 41 #include <sys/task.h> 42 #include <sys/mutex.h> 43 44 #include <net/if.h> 45 #include <net/if_var.h> 46 #include <net/if_types.h> 47 #include <net/toeplitz.h> 48 49 #include <netinet/in.h> 50 #include <netinet/ip.h> 51 #include <netinet/ip_ipsp.h> 52 53 #ifdef INET6 54 #include <netinet/ip6.h> 55 #endif 56 57 #if NBPFILTER > 0 58 #include <net/bpf.h> 59 #endif 60 61 #if NPF > 0 62 #include <net/pfvar.h> 63 #endif 64 65 #define SEC_MTU 1280 66 #define SEC_MTU_MIN 1280 67 #define SEC_MTU_MAX 32768 /* could get closer to 64k... */ 68 69 struct sec_softc { 70 struct ifnet sc_if; 71 unsigned int sc_dead; 72 unsigned int sc_up; 73 74 struct task sc_send; 75 int sc_txprio; 76 77 unsigned int sc_unit; 78 SMR_SLIST_ENTRY(sec_softc) sc_entry; 79 struct refcnt sc_refs; 80 }; 81 82 SMR_SLIST_HEAD(sec_bucket, sec_softc); 83 84 static int sec_output(struct ifnet *, struct mbuf *, struct sockaddr *, 85 struct rtentry *); 86 static int sec_enqueue(struct ifnet *, struct mbuf *); 87 static void sec_send(void *); 88 static void sec_start(struct ifqueue *); 89 90 static int sec_ioctl(struct ifnet *, u_long, caddr_t); 91 static int sec_up(struct sec_softc *); 92 static int sec_down(struct sec_softc *); 93 94 static int sec_clone_create(struct if_clone *, int); 95 static int sec_clone_destroy(struct ifnet *); 96 97 static struct tdb * 98 sec_tdb_get(unsigned int); 99 static void sec_tdb_gc(void *); 100 101 static struct if_clone sec_cloner = 102 IF_CLONE_INITIALIZER("sec", sec_clone_create, sec_clone_destroy); 103 104 static unsigned int sec_mix; 105 static struct sec_bucket sec_map[256] __aligned(CACHELINESIZE); 106 static struct tdb *sec_tdbh[256] __aligned(CACHELINESIZE); 107 108 static struct tdb *sec_tdb_gc_list; 109 static struct task sec_tdb_gc_task = 110 TASK_INITIALIZER(sec_tdb_gc, NULL); 111 static struct mutex sec_tdb_gc_mtx = 112 MUTEX_INITIALIZER(IPL_MPFLOOR); 113 114 void 115 secattach(int n) 116 { 117 sec_mix = arc4random(); 118 if_clone_attach(&sec_cloner); 119 } 120 121 static int 122 sec_clone_create(struct if_clone *ifc, int unit) 123 { 124 struct sec_softc *sc; 125 struct ifnet *ifp; 126 127 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 128 129 sc->sc_unit = unit; 130 131 task_set(&sc->sc_send, sec_send, sc); 132 133 snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d", 134 ifc->ifc_name, unit); 135 136 ifp = &sc->sc_if; 137 ifp->if_softc = sc; 138 ifp->if_type = IFT_TUNNEL; 139 ifp->if_mtu = SEC_MTU; 140 ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 141 ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; 142 ifp->if_bpf_mtap = p2p_bpf_mtap; 143 ifp->if_input = p2p_input; 144 ifp->if_output = sec_output; 145 ifp->if_enqueue = sec_enqueue; 146 ifp->if_qstart = sec_start; 147 ifp->if_ioctl = sec_ioctl; 148 ifp->if_rtrequest = p2p_rtrequest; 149 150 if_counters_alloc(ifp); 151 if_attach(ifp); 152 if_alloc_sadl(ifp); 153 154 #if NBPFILTER > 0 155 bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); 156 #endif 157 158 return (0); 159 } 160 161 static int 162 sec_clone_destroy(struct ifnet *ifp) 163 { 164 struct sec_softc *sc = ifp->if_softc; 165 166 NET_LOCK(); 167 sc->sc_dead = 1; 168 if (ISSET(ifp->if_flags, IFF_RUNNING)) 169 sec_down(sc); 170 NET_UNLOCK(); 171 172 if_detach(ifp); 173 174 free(sc, M_DEVBUF, sizeof(*sc)); 175 176 return (0); 177 } 178 179 static int 180 sec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 181 { 182 struct sec_softc *sc = ifp->if_softc; 183 struct ifreq *ifr = (struct ifreq *)data; 184 int error = 0; 185 186 switch (cmd) { 187 case SIOCSIFADDR: 188 break; 189 190 case SIOCSIFFLAGS: 191 if (ISSET(ifp->if_flags, IFF_UP)) { 192 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 193 error = sec_up(sc); 194 else 195 error = 0; 196 } else { 197 if (ISSET(ifp->if_flags, IFF_RUNNING)) 198 error = sec_down(sc); 199 } 200 break; 201 202 case SIOCADDMULTI: 203 case SIOCDELMULTI: 204 break; 205 206 case SIOCSIFMTU: 207 if (ifr->ifr_mtu < SEC_MTU_MIN || 208 ifr->ifr_mtu > SEC_MTU_MAX) { 209 error = EINVAL; 210 break; 211 } 212 213 ifp->if_mtu = ifr->ifr_mtu; 214 break; 215 216 default: 217 error = ENOTTY; 218 break; 219 } 220 221 return (error); 222 } 223 224 static int 225 sec_up(struct sec_softc *sc) 226 { 227 struct ifnet *ifp = &sc->sc_if; 228 unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map); 229 230 NET_ASSERT_LOCKED(); 231 KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING)); 232 233 if (sc->sc_dead) 234 return (ENXIO); 235 236 /* 237 * coordinate with sec_down(). if sc_up is still up and 238 * we're here then something else is running sec_down. 239 */ 240 if (sc->sc_up) 241 return (EBUSY); 242 243 sc->sc_up = 1; 244 245 refcnt_init(&sc->sc_refs); 246 SET(ifp->if_flags, IFF_RUNNING); 247 SMR_SLIST_INSERT_HEAD_LOCKED(&sec_map[idx], sc, sc_entry); 248 249 return (0); 250 } 251 252 static int 253 sec_down(struct sec_softc *sc) 254 { 255 struct ifnet *ifp = &sc->sc_if; 256 unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map); 257 258 NET_ASSERT_LOCKED(); 259 KASSERT(ISSET(ifp->if_flags, IFF_RUNNING)); 260 261 /* 262 * taking sec down involves waiting for it to stop running 263 * in various contexts. this thread cannot hold netlock 264 * while waiting for a barrier for a task that could be trying 265 * to take netlock itself. so give up netlock, but don't clear 266 * sc_up to prevent sec_up from running. 267 */ 268 269 CLR(ifp->if_flags, IFF_RUNNING); 270 NET_UNLOCK(); 271 272 smr_barrier(); 273 taskq_del_barrier(systq, &sc->sc_send); 274 275 refcnt_finalize(&sc->sc_refs, "secdown"); 276 277 NET_LOCK(); 278 SMR_SLIST_REMOVE_LOCKED(&sec_map[idx], sc, sec_softc, sc_entry); 279 sc->sc_up = 0; 280 281 return (0); 282 } 283 284 static int 285 sec_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 286 struct rtentry *rt) 287 { 288 struct m_tag *mtag; 289 int error = 0; 290 291 if (!ISSET(ifp->if_flags, IFF_RUNNING)) { 292 error = ENETDOWN; 293 goto drop; 294 } 295 296 switch (dst->sa_family) { 297 case AF_INET: 298 #ifdef INET6 299 case AF_INET6: 300 #endif 301 #ifdef MPLS 302 case AF_MPLS: 303 #endif 304 break; 305 default: 306 error = EAFNOSUPPORT; 307 goto drop; 308 } 309 310 mtag = NULL; 311 while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { 312 if (ifp->if_index == *(int *)(mtag + 1)) { 313 error = EIO; 314 goto drop; 315 } 316 } 317 318 m->m_pkthdr.ph_family = dst->sa_family; 319 320 error = if_enqueue(ifp, m); 321 if (error != 0) 322 counters_inc(ifp->if_counters, ifc_oerrors); 323 324 return (error); 325 326 drop: 327 m_freem(m); 328 return (error); 329 } 330 331 static int 332 sec_enqueue(struct ifnet *ifp, struct mbuf *m) 333 { 334 struct sec_softc *sc = ifp->if_softc; 335 struct ifqueue *ifq = &ifp->if_snd; 336 int error; 337 338 error = ifq_enqueue(ifq, m); 339 if (error) 340 return (error); 341 342 task_add(systq, &sc->sc_send); 343 344 return (0); 345 } 346 347 static void 348 sec_send(void *arg) 349 { 350 struct sec_softc *sc = arg; 351 struct ifnet *ifp = &sc->sc_if; 352 struct ifqueue *ifq = &ifp->if_snd; 353 struct tdb *tdb; 354 struct mbuf *m; 355 int error; 356 unsigned int flowid; 357 358 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 359 return; 360 361 tdb = sec_tdb_get(sc->sc_unit); 362 if (tdb == NULL) 363 goto purge; 364 365 flowid = sc->sc_unit ^ sec_mix; 366 367 NET_LOCK(); 368 while ((m = ifq_dequeue(ifq)) != NULL) { 369 CLR(m->m_flags, M_BCAST|M_MCAST); 370 371 #if NPF > 0 372 pf_pkt_addr_changed(m); 373 #endif 374 375 #if NBPFILTER > 0 376 if (ifp->if_bpf) 377 bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m, 378 BPF_DIRECTION_OUT); 379 #endif 380 381 m->m_pkthdr.pf.prio = sc->sc_txprio; 382 SET(m->m_pkthdr.csum_flags, M_FLOWID); 383 m->m_pkthdr.ph_flowid = flowid; 384 385 error = ipsp_process_packet(m, tdb, 386 m->m_pkthdr.ph_family, /* already tunnelled? */ 0); 387 if (error != 0) 388 counters_inc(ifp->if_counters, ifc_oerrors); 389 } 390 NET_UNLOCK(); 391 392 tdb_unref(tdb); 393 return; 394 395 purge: 396 counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq)); 397 } 398 399 static void 400 sec_start(struct ifqueue *ifq) 401 { 402 struct ifnet *ifp = ifq->ifq_if; 403 struct sec_softc *sc = ifp->if_softc; 404 405 /* move this back to systq for KERNEL_LOCK */ 406 task_add(systq, &sc->sc_send); 407 } 408 409 /* 410 * ipsec_input handling 411 */ 412 413 struct sec_softc * 414 sec_get(unsigned int unit) 415 { 416 unsigned int idx = stoeplitz_h32(unit) % nitems(sec_map); 417 struct sec_bucket *sb = &sec_map[idx]; 418 struct sec_softc *sc; 419 420 smr_read_enter(); 421 SMR_SLIST_FOREACH(sc, sb, sc_entry) { 422 if (sc->sc_unit == unit) { 423 refcnt_take(&sc->sc_refs); 424 break; 425 } 426 } 427 smr_read_leave(); 428 429 return (sc); 430 } 431 432 void 433 sec_input(struct sec_softc *sc, int af, int proto, struct mbuf *m) 434 { 435 struct ip *iph; 436 int hlen; 437 438 switch (af) { 439 case AF_INET: 440 iph = mtod(m, struct ip *); 441 hlen = iph->ip_hl << 2; 442 break; 443 #ifdef INET6 444 case AF_INET6: 445 hlen = sizeof(struct ip6_hdr); 446 break; 447 #endif 448 default: 449 unhandled_af(af); 450 } 451 452 m_adj(m, hlen); 453 454 switch (proto) { 455 case IPPROTO_IPV4: 456 af = AF_INET; 457 break; 458 case IPPROTO_IPV6: 459 af = AF_INET6; 460 break; 461 case IPPROTO_MPLS: 462 af = AF_MPLS; 463 break; 464 default: 465 af = AF_UNSPEC; 466 break; 467 } 468 469 m->m_pkthdr.ph_family = af; 470 471 if_vinput(&sc->sc_if, m); 472 } 473 474 void 475 sec_put(struct sec_softc *sc) 476 { 477 refcnt_rele_wake(&sc->sc_refs); 478 } 479 480 /* 481 * tdb handling 482 */ 483 484 static int 485 sec_tdb_valid(struct tdb *tdb) 486 { 487 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 488 489 if (!ISSET(tdb->tdb_flags, TDBF_TUNNELING)) 490 return (0); 491 if (ISSET(tdb->tdb_flags, TDBF_INVALID)) 492 return (0); 493 494 if (tdb->tdb_iface_dir != IPSP_DIRECTION_OUT) 495 return (0); 496 497 return (1); 498 } 499 500 /* 501 * these are called from netinet/ip_ipsp.c with tdb_sadb_mtx held, 502 * which we rely on to serialise modifications to the sec_tdbh. 503 */ 504 505 void 506 sec_tdb_insert(struct tdb *tdb) 507 { 508 unsigned int idx; 509 struct tdb **tdbp; 510 struct tdb *ltdb; 511 512 if (!sec_tdb_valid(tdb)) 513 return; 514 515 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 516 tdbp = &sec_tdbh[idx]; 517 518 tdb_ref(tdb); /* take a ref for the SMR pointer */ 519 520 /* wire the tdb into the head of the list */ 521 ltdb = SMR_PTR_GET_LOCKED(tdbp); 522 SMR_PTR_SET_LOCKED(&tdb->tdb_dnext, ltdb); 523 SMR_PTR_SET_LOCKED(tdbp, tdb); 524 } 525 526 void 527 sec_tdb_remove(struct tdb *tdb) 528 { 529 struct tdb **tdbp; 530 struct tdb *ltdb; 531 unsigned int idx; 532 533 if (!sec_tdb_valid(tdb)) 534 return; 535 536 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 537 tdbp = &sec_tdbh[idx]; 538 539 while ((ltdb = SMR_PTR_GET_LOCKED(tdbp)) != NULL) { 540 if (ltdb == tdb) { 541 /* take the tdb out of the list */ 542 ltdb = SMR_PTR_GET_LOCKED(&tdb->tdb_dnext); 543 SMR_PTR_SET_LOCKED(tdbp, ltdb); 544 545 /* move the ref to the gc */ 546 547 mtx_enter(&sec_tdb_gc_mtx); 548 tdb->tdb_dnext = sec_tdb_gc_list; 549 sec_tdb_gc_list = tdb; 550 mtx_leave(&sec_tdb_gc_mtx); 551 task_add(systq, &sec_tdb_gc_task); 552 553 return; 554 } 555 556 tdbp = <db->tdb_dnext; 557 } 558 559 panic("%s: unable to find tdb %p", __func__, tdb); 560 } 561 562 static void 563 sec_tdb_gc(void *null) 564 { 565 struct tdb *tdb, *ntdb; 566 567 mtx_enter(&sec_tdb_gc_mtx); 568 tdb = sec_tdb_gc_list; 569 sec_tdb_gc_list = NULL; 570 mtx_leave(&sec_tdb_gc_mtx); 571 572 if (tdb == NULL) 573 return; 574 575 smr_barrier(); 576 577 NET_LOCK(); 578 do { 579 ntdb = tdb->tdb_dnext; 580 tdb_unref(tdb); 581 tdb = ntdb; 582 } while (tdb != NULL); 583 NET_UNLOCK(); 584 } 585 586 struct tdb * 587 sec_tdb_get(unsigned int unit) 588 { 589 unsigned int idx; 590 struct tdb **tdbp; 591 struct tdb *tdb; 592 593 idx = stoeplitz_h32(unit) % nitems(sec_map); 594 tdbp = &sec_tdbh[idx]; 595 596 smr_read_enter(); 597 while ((tdb = SMR_PTR_GET(tdbp)) != NULL) { 598 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 599 if (!ISSET(tdb->tdb_flags, TDBF_DELETED) && 600 tdb->tdb_iface == unit) { 601 tdb_ref(tdb); 602 break; 603 } 604 605 tdbp = &tdb->tdb_dnext; 606 } 607 smr_read_leave(); 608 609 return (tdb); 610 } 611