1 /* $OpenBSD: if_sec.c,v 1.5 2023/08/11 02:34:56 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 76 unsigned int sc_unit; 77 SMR_SLIST_ENTRY(sec_softc) sc_entry; 78 struct refcnt sc_refs; 79 }; 80 81 SMR_SLIST_HEAD(sec_bucket, sec_softc); 82 83 static int sec_output(struct ifnet *, struct mbuf *, struct sockaddr *, 84 struct rtentry *); 85 static int sec_enqueue(struct ifnet *, struct mbuf *); 86 static void sec_send(void *); 87 static void sec_start(struct ifqueue *); 88 89 static int sec_ioctl(struct ifnet *, u_long, caddr_t); 90 static int sec_up(struct sec_softc *); 91 static int sec_down(struct sec_softc *); 92 93 static int sec_clone_create(struct if_clone *, int); 94 static int sec_clone_destroy(struct ifnet *); 95 96 static struct tdb * 97 sec_tdb_get(unsigned int); 98 static void sec_tdb_gc(void *); 99 100 static struct if_clone sec_cloner = 101 IF_CLONE_INITIALIZER("sec", sec_clone_create, sec_clone_destroy); 102 103 static struct sec_bucket sec_map[256] __aligned(CACHELINESIZE); 104 static struct tdb *sec_tdbh[256] __aligned(CACHELINESIZE); 105 106 static struct tdb *sec_tdb_gc_list; 107 static struct task sec_tdb_gc_task = 108 TASK_INITIALIZER(sec_tdb_gc, NULL); 109 static struct mutex sec_tdb_gc_mtx = 110 MUTEX_INITIALIZER(IPL_MPFLOOR); 111 112 void 113 secattach(int n) 114 { 115 if_clone_attach(&sec_cloner); 116 } 117 118 static int 119 sec_clone_create(struct if_clone *ifc, int unit) 120 { 121 struct sec_softc *sc; 122 struct ifnet *ifp; 123 124 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 125 126 sc->sc_unit = unit; 127 128 task_set(&sc->sc_send, sec_send, sc); 129 130 snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d", 131 ifc->ifc_name, unit); 132 133 ifp = &sc->sc_if; 134 ifp->if_softc = sc; 135 ifp->if_type = IFT_TUNNEL; 136 ifp->if_mtu = SEC_MTU; 137 ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 138 ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; 139 ifp->if_bpf_mtap = p2p_bpf_mtap; 140 ifp->if_input = p2p_input; 141 ifp->if_output = sec_output; 142 ifp->if_enqueue = sec_enqueue; 143 ifp->if_qstart = sec_start; 144 ifp->if_ioctl = sec_ioctl; 145 ifp->if_rtrequest = p2p_rtrequest; 146 147 if_counters_alloc(ifp); 148 if_attach(ifp); 149 if_alloc_sadl(ifp); 150 151 #if NBPFILTER > 0 152 bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); 153 #endif 154 155 return (0); 156 } 157 158 static int 159 sec_clone_destroy(struct ifnet *ifp) 160 { 161 struct sec_softc *sc = ifp->if_softc; 162 163 NET_LOCK(); 164 sc->sc_dead = 1; 165 if (ISSET(ifp->if_flags, IFF_RUNNING)) 166 sec_down(sc); 167 NET_UNLOCK(); 168 169 if_detach(ifp); 170 171 free(sc, M_DEVBUF, sizeof(*sc)); 172 173 return (0); 174 } 175 176 static int 177 sec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 178 { 179 struct sec_softc *sc = ifp->if_softc; 180 struct ifreq *ifr = (struct ifreq *)data; 181 int error = 0; 182 183 switch (cmd) { 184 case SIOCSIFADDR: 185 break; 186 187 case SIOCSIFFLAGS: 188 if (ISSET(ifp->if_flags, IFF_UP)) { 189 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 190 error = sec_up(sc); 191 else 192 error = 0; 193 } else { 194 if (ISSET(ifp->if_flags, IFF_RUNNING)) 195 error = sec_down(sc); 196 } 197 break; 198 199 case SIOCADDMULTI: 200 case SIOCDELMULTI: 201 break; 202 203 case SIOCSIFMTU: 204 if (ifr->ifr_mtu < SEC_MTU_MIN || 205 ifr->ifr_mtu > SEC_MTU_MAX) { 206 error = EINVAL; 207 break; 208 } 209 210 ifp->if_mtu = ifr->ifr_mtu; 211 break; 212 213 default: 214 error = ENOTTY; 215 break; 216 } 217 218 return (error); 219 } 220 221 static int 222 sec_up(struct sec_softc *sc) 223 { 224 struct ifnet *ifp = &sc->sc_if; 225 unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map); 226 227 NET_ASSERT_LOCKED(); 228 KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING)); 229 230 if (sc->sc_dead) 231 return (ENXIO); 232 233 /* 234 * coordinate with sec_down(). if sc_up is still up and 235 * we're here then something else is running sec_down. 236 */ 237 if (sc->sc_up) 238 return (EBUSY); 239 240 sc->sc_up = 1; 241 242 refcnt_init(&sc->sc_refs); 243 SET(ifp->if_flags, IFF_RUNNING); 244 SMR_SLIST_INSERT_HEAD_LOCKED(&sec_map[idx], sc, sc_entry); 245 246 return (0); 247 } 248 249 static int 250 sec_down(struct sec_softc *sc) 251 { 252 struct ifnet *ifp = &sc->sc_if; 253 unsigned int idx = stoeplitz_h32(sc->sc_unit) % nitems(sec_map); 254 255 NET_ASSERT_LOCKED(); 256 KASSERT(ISSET(ifp->if_flags, IFF_RUNNING)); 257 258 /* 259 * taking sec down involves waiting for it to stop running 260 * in various contexts. this thread cannot hold netlock 261 * while waiting for a barrier for a task that could be trying 262 * to take netlock itself. so give up netlock, but don't clear 263 * sc_up to prevent sec_up from running. 264 */ 265 266 CLR(ifp->if_flags, IFF_RUNNING); 267 NET_UNLOCK(); 268 269 smr_barrier(); 270 taskq_del_barrier(systq, &sc->sc_send); 271 272 refcnt_finalize(&sc->sc_refs, "secdown"); 273 274 NET_LOCK(); 275 SMR_SLIST_REMOVE_LOCKED(&sec_map[idx], sc, sec_softc, sc_entry); 276 sc->sc_up = 0; 277 278 return (0); 279 } 280 281 static int 282 sec_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 283 struct rtentry *rt) 284 { 285 struct m_tag *mtag; 286 int error = 0; 287 288 if (!ISSET(ifp->if_flags, IFF_RUNNING)) { 289 error = ENETDOWN; 290 goto drop; 291 } 292 293 switch (dst->sa_family) { 294 case AF_INET: 295 #ifdef INET6 296 case AF_INET6: 297 #endif 298 #ifdef MPLS 299 case AF_MPLS: 300 #endif 301 break; 302 default: 303 error = EAFNOSUPPORT; 304 goto drop; 305 } 306 307 mtag = NULL; 308 while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { 309 if (ifp->if_index == *(int *)(mtag + 1)) { 310 error = EIO; 311 goto drop; 312 } 313 } 314 315 m->m_pkthdr.ph_family = dst->sa_family; 316 317 error = if_enqueue(ifp, m); 318 if (error != 0) 319 counters_inc(ifp->if_counters, ifc_oerrors); 320 321 return (error); 322 323 drop: 324 m_freem(m); 325 return (error); 326 } 327 328 static int 329 sec_enqueue(struct ifnet *ifp, struct mbuf *m) 330 { 331 struct sec_softc *sc = ifp->if_softc; 332 struct ifqueue *ifq = &ifp->if_snd; 333 int error; 334 335 error = ifq_enqueue(ifq, m); 336 if (error) 337 return (error); 338 339 task_add(systq, &sc->sc_send); 340 341 return (0); 342 } 343 344 static void 345 sec_send(void *arg) 346 { 347 struct sec_softc *sc = arg; 348 struct ifnet *ifp = &sc->sc_if; 349 struct ifqueue *ifq = &ifp->if_snd; 350 struct tdb *tdb; 351 struct mbuf *m; 352 int error; 353 354 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 355 return; 356 357 tdb = sec_tdb_get(sc->sc_unit); 358 if (tdb == NULL) 359 goto purge; 360 361 NET_LOCK(); 362 while ((m = ifq_dequeue(ifq)) != NULL) { 363 CLR(m->m_flags, M_BCAST|M_MCAST); 364 365 #if NPF > 0 366 pf_pkt_addr_changed(m); 367 #endif 368 369 error = ipsp_process_packet(m, tdb, 370 m->m_pkthdr.ph_family, /* already tunnelled? */ 0); 371 if (error != 0) 372 counters_inc(ifp->if_counters, ifc_oerrors); 373 } 374 NET_UNLOCK(); 375 376 tdb_unref(tdb); 377 return; 378 379 purge: 380 counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq)); 381 } 382 383 static void 384 sec_start(struct ifqueue *ifq) 385 { 386 struct ifnet *ifp = ifq->ifq_if; 387 struct sec_softc *sc = ifp->if_softc; 388 389 /* move this back to systq for KERNEL_LOCK */ 390 task_add(systq, &sc->sc_send); 391 } 392 393 /* 394 * ipsec_input handling 395 */ 396 397 struct sec_softc * 398 sec_get(unsigned int unit) 399 { 400 unsigned int idx = stoeplitz_h32(unit) % nitems(sec_map); 401 struct sec_bucket *sb = &sec_map[idx]; 402 struct sec_softc *sc; 403 404 smr_read_enter(); 405 SMR_SLIST_FOREACH(sc, sb, sc_entry) { 406 if (sc->sc_unit == unit) { 407 refcnt_take(&sc->sc_refs); 408 break; 409 } 410 } 411 smr_read_leave(); 412 413 return (sc); 414 } 415 416 void 417 sec_input(struct sec_softc *sc, int af, int proto, struct mbuf *m) 418 { 419 struct ip *iph; 420 int hlen; 421 422 switch (af) { 423 case AF_INET: 424 iph = mtod(m, struct ip *); 425 hlen = iph->ip_hl << 2; 426 break; 427 #ifdef INET6 428 case AF_INET6: 429 hlen = sizeof(struct ip6_hdr); 430 break; 431 #endif 432 default: 433 unhandled_af(af); 434 } 435 436 m_adj(m, hlen); 437 438 switch (proto) { 439 case IPPROTO_IPV4: 440 af = AF_INET; 441 break; 442 case IPPROTO_IPV6: 443 af = AF_INET6; 444 break; 445 case IPPROTO_MPLS: 446 af = AF_MPLS; 447 break; 448 default: 449 af = AF_UNSPEC; 450 break; 451 } 452 453 m->m_pkthdr.ph_family = af; 454 455 if_vinput(&sc->sc_if, m); 456 } 457 458 void 459 sec_put(struct sec_softc *sc) 460 { 461 refcnt_rele_wake(&sc->sc_refs); 462 } 463 464 /* 465 * tdb handling 466 */ 467 468 static int 469 sec_tdb_valid(struct tdb *tdb) 470 { 471 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 472 473 if (!ISSET(tdb->tdb_flags, TDBF_TUNNELING)) 474 return (0); 475 if (ISSET(tdb->tdb_flags, TDBF_INVALID)) 476 return (0); 477 478 if (tdb->tdb_iface_dir != IPSP_DIRECTION_OUT) 479 return (0); 480 481 return (1); 482 } 483 484 /* 485 * these are called from netinet/ip_ipsp.c with tdb_sadb_mtx held, 486 * which we rely on to serialise modifications to the sec_tdbh. 487 */ 488 489 void 490 sec_tdb_insert(struct tdb *tdb) 491 { 492 unsigned int idx; 493 struct tdb **tdbp; 494 struct tdb *ltdb; 495 496 if (!sec_tdb_valid(tdb)) 497 return; 498 499 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 500 tdbp = &sec_tdbh[idx]; 501 502 tdb_ref(tdb); /* take a ref for the SMR pointer */ 503 504 /* wire the tdb into the head of the list */ 505 ltdb = SMR_PTR_GET_LOCKED(tdbp); 506 SMR_PTR_SET_LOCKED(&tdb->tdb_dnext, ltdb); 507 SMR_PTR_SET_LOCKED(tdbp, tdb); 508 } 509 510 void 511 sec_tdb_remove(struct tdb *tdb) 512 { 513 struct tdb **tdbp; 514 struct tdb *ltdb; 515 unsigned int idx; 516 517 if (!sec_tdb_valid(tdb)) 518 return; 519 520 idx = stoeplitz_h32(tdb->tdb_iface) % nitems(sec_tdbh); 521 tdbp = &sec_tdbh[idx]; 522 523 while ((ltdb = SMR_PTR_GET_LOCKED(tdbp)) != NULL) { 524 if (ltdb == tdb) { 525 /* take the tdb out of the list */ 526 ltdb = SMR_PTR_GET_LOCKED(&tdb->tdb_dnext); 527 SMR_PTR_SET_LOCKED(tdbp, ltdb); 528 529 /* move the ref to the gc */ 530 531 mtx_enter(&sec_tdb_gc_mtx); 532 tdb->tdb_dnext = sec_tdb_gc_list; 533 sec_tdb_gc_list = tdb; 534 mtx_leave(&sec_tdb_gc_mtx); 535 task_add(systq, &sec_tdb_gc_task); 536 537 return; 538 } 539 540 tdbp = <db->tdb_dnext; 541 } 542 543 panic("%s: unable to find tdb %p", __func__, tdb); 544 } 545 546 static void 547 sec_tdb_gc(void *null) 548 { 549 struct tdb *tdb, *ntdb; 550 551 mtx_enter(&sec_tdb_gc_mtx); 552 tdb = sec_tdb_gc_list; 553 sec_tdb_gc_list = NULL; 554 mtx_leave(&sec_tdb_gc_mtx); 555 556 if (tdb == NULL) 557 return; 558 559 smr_barrier(); 560 561 NET_LOCK(); 562 do { 563 ntdb = tdb->tdb_dnext; 564 tdb_unref(tdb); 565 tdb = ntdb; 566 } while (tdb != NULL); 567 NET_UNLOCK(); 568 } 569 570 struct tdb * 571 sec_tdb_get(unsigned int unit) 572 { 573 unsigned int idx; 574 struct tdb **tdbp; 575 struct tdb *tdb; 576 577 idx = stoeplitz_h32(unit) % nitems(sec_map); 578 tdbp = &sec_tdbh[idx]; 579 580 smr_read_enter(); 581 while ((tdb = SMR_PTR_GET(tdbp)) != NULL) { 582 KASSERT(ISSET(tdb->tdb_flags, TDBF_IFACE)); 583 if (!ISSET(tdb->tdb_flags, TDBF_DELETED) && 584 tdb->tdb_iface == unit) { 585 tdb_ref(tdb); 586 break; 587 } 588 589 tdbp = &tdb->tdb_dnext; 590 } 591 smr_read_leave(); 592 593 return (tdb); 594 } 595