1 /* $OpenBSD: if_sec.c,v 1.10 2024/01/24 00:17:01 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 mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); 319 if (mtag == NULL) { 320 error = ENOBUFS; 321 goto drop; 322 } 323 *(int *)(mtag + 1) = ifp->if_index; 324 m_tag_prepend(m, mtag); 325 326 m->m_pkthdr.ph_family = dst->sa_family; 327 328 error = if_enqueue(ifp, m); 329 if (error != 0) 330 counters_inc(ifp->if_counters, ifc_oerrors); 331 332 return (error); 333 334 drop: 335 m_freem(m); 336 return (error); 337 } 338 339 static int 340 sec_enqueue(struct ifnet *ifp, struct mbuf *m) 341 { 342 struct sec_softc *sc = ifp->if_softc; 343 struct ifqueue *ifq = &ifp->if_snd; 344 int error; 345 346 error = ifq_enqueue(ifq, m); 347 if (error) 348 return (error); 349 350 task_add(systq, &sc->sc_send); 351 352 return (0); 353 } 354 355 static void 356 sec_send(void *arg) 357 { 358 struct sec_softc *sc = arg; 359 struct ifnet *ifp = &sc->sc_if; 360 struct ifqueue *ifq = &ifp->if_snd; 361 struct tdb *tdb; 362 struct mbuf *m; 363 int error; 364 unsigned int flowid; 365 366 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 367 return; 368 369 tdb = sec_tdb_get(sc->sc_unit); 370 if (tdb == NULL) 371 goto purge; 372 373 flowid = sc->sc_unit ^ sec_mix; 374 375 NET_LOCK(); 376 while ((m = ifq_dequeue(ifq)) != NULL) { 377 CLR(m->m_flags, M_BCAST|M_MCAST); 378 379 #if NPF > 0 380 pf_pkt_addr_changed(m); 381 #endif 382 383 #if NBPFILTER > 0 384 if (ifp->if_bpf) 385 bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m, 386 BPF_DIRECTION_OUT); 387 #endif 388 389 m->m_pkthdr.pf.prio = sc->sc_txprio; 390 SET(m->m_pkthdr.csum_flags, M_FLOWID); 391 m->m_pkthdr.ph_flowid = flowid; 392 393 error = ipsp_process_packet(m, tdb, 394 m->m_pkthdr.ph_family, /* already tunnelled? */ 0); 395 if (error != 0) 396 counters_inc(ifp->if_counters, ifc_oerrors); 397 } 398 NET_UNLOCK(); 399 400 tdb_unref(tdb); 401 return; 402 403 purge: 404 counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq)); 405 } 406 407 static void 408 sec_start(struct ifqueue *ifq) 409 { 410 struct ifnet *ifp = ifq->ifq_if; 411 struct sec_softc *sc = ifp->if_softc; 412 413 /* move this back to systq for KERNEL_LOCK */ 414 task_add(systq, &sc->sc_send); 415 } 416 417 /* 418 * ipsec_input handling 419 */ 420 421 struct sec_softc * 422 sec_get(unsigned int unit) 423 { 424 unsigned int idx = stoeplitz_h32(unit) % nitems(sec_map); 425 struct sec_bucket *sb = &sec_map[idx]; 426 struct sec_softc *sc; 427 428 smr_read_enter(); 429 SMR_SLIST_FOREACH(sc, sb, sc_entry) { 430 if (sc->sc_unit == unit) { 431 refcnt_take(&sc->sc_refs); 432 break; 433 } 434 } 435 smr_read_leave(); 436 437 return (sc); 438 } 439 440 void 441 sec_input(struct sec_softc *sc, int af, int proto, struct mbuf *m) 442 { 443 struct ip *iph; 444 int hlen; 445 446 switch (af) { 447 case AF_INET: 448 iph = mtod(m, struct ip *); 449 hlen = iph->ip_hl << 2; 450 break; 451 #ifdef INET6 452 case AF_INET6: 453 hlen = sizeof(struct ip6_hdr); 454 break; 455 #endif 456 default: 457 unhandled_af(af); 458 } 459 460 m_adj(m, hlen); 461 462 switch (proto) { 463 case IPPROTO_IPV4: 464 af = AF_INET; 465 break; 466 case IPPROTO_IPV6: 467 af = AF_INET6; 468 break; 469 case IPPROTO_MPLS: 470 af = AF_MPLS; 471 break; 472 default: 473 af = AF_UNSPEC; 474 break; 475 } 476 477 m->m_pkthdr.ph_family = af; 478 479 if_vinput(&sc->sc_if, m); 480 } 481 482 void 483 sec_put(struct sec_softc *sc) 484 { 485 refcnt_rele_wake(&sc->sc_refs); 486 } 487 488 /* 489 * tdb handling 490 */ 491 492 static int 493 sec_tdb_valid(struct tdb *tdb) 494 { 495 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 496 497 if (!ISSET(tdb->tdb_flags, TDBF_TUNNELING)) 498 return (0); 499 if (ISSET(tdb->tdb_flags, TDBF_INVALID)) 500 return (0); 501 502 if (tdb->tdb_iface_dir != IPSP_DIRECTION_OUT) 503 return (0); 504 505 return (1); 506 } 507 508 /* 509 * these are called from netinet/ip_ipsp.c with tdb_sadb_mtx held, 510 * which we rely on to serialise modifications to the sec_tdbh. 511 */ 512 513 void 514 sec_tdb_insert(struct tdb *tdb) 515 { 516 unsigned int idx; 517 struct tdb **tdbp; 518 struct tdb *ltdb; 519 520 if (!sec_tdb_valid(tdb)) 521 return; 522 523 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 524 tdbp = &sec_tdbh[idx]; 525 526 tdb_ref(tdb); /* take a ref for the SMR pointer */ 527 528 /* wire the tdb into the head of the list */ 529 ltdb = SMR_PTR_GET_LOCKED(tdbp); 530 SMR_PTR_SET_LOCKED(&tdb->tdb_dnext, ltdb); 531 SMR_PTR_SET_LOCKED(tdbp, tdb); 532 } 533 534 void 535 sec_tdb_remove(struct tdb *tdb) 536 { 537 struct tdb **tdbp; 538 struct tdb *ltdb; 539 unsigned int idx; 540 541 if (!sec_tdb_valid(tdb)) 542 return; 543 544 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 545 tdbp = &sec_tdbh[idx]; 546 547 while ((ltdb = SMR_PTR_GET_LOCKED(tdbp)) != NULL) { 548 if (ltdb == tdb) { 549 /* take the tdb out of the list */ 550 ltdb = SMR_PTR_GET_LOCKED(&tdb->tdb_dnext); 551 SMR_PTR_SET_LOCKED(tdbp, ltdb); 552 553 /* move the ref to the gc */ 554 555 mtx_enter(&sec_tdb_gc_mtx); 556 tdb->tdb_dnext = sec_tdb_gc_list; 557 sec_tdb_gc_list = tdb; 558 mtx_leave(&sec_tdb_gc_mtx); 559 task_add(systq, &sec_tdb_gc_task); 560 561 return; 562 } 563 564 tdbp = <db->tdb_dnext; 565 } 566 567 panic("%s: unable to find tdb %p", __func__, tdb); 568 } 569 570 static void 571 sec_tdb_gc(void *null) 572 { 573 struct tdb *tdb, *ntdb; 574 575 mtx_enter(&sec_tdb_gc_mtx); 576 tdb = sec_tdb_gc_list; 577 sec_tdb_gc_list = NULL; 578 mtx_leave(&sec_tdb_gc_mtx); 579 580 if (tdb == NULL) 581 return; 582 583 smr_barrier(); 584 585 NET_LOCK(); 586 do { 587 ntdb = tdb->tdb_dnext; 588 tdb_unref(tdb); 589 tdb = ntdb; 590 } while (tdb != NULL); 591 NET_UNLOCK(); 592 } 593 594 struct tdb * 595 sec_tdb_get(unsigned int unit) 596 { 597 unsigned int idx; 598 struct tdb **tdbp; 599 struct tdb *tdb; 600 601 idx = stoeplitz_h32(unit) % nitems(sec_map); 602 tdbp = &sec_tdbh[idx]; 603 604 smr_read_enter(); 605 while ((tdb = SMR_PTR_GET(tdbp)) != NULL) { 606 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 607 if (!ISSET(tdb->tdb_flags, TDBF_DELETED) && 608 tdb->tdb_iface == unit) { 609 tdb_ref(tdb); 610 break; 611 } 612 613 tdbp = &tdb->tdb_dnext; 614 } 615 smr_read_leave(); 616 617 return (tdb); 618 } 619