1 /* $OpenBSD: if_tpmr.c,v 1.24 2021/03/05 06:44:09 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2019 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 #include "bpfilter.h" 27 #include "pf.h" 28 #include "vlan.h" 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/mbuf.h> 34 #include <sys/queue.h> 35 #include <sys/socket.h> 36 #include <sys/sockio.h> 37 #include <sys/systm.h> 38 #include <sys/syslog.h> 39 #include <sys/rwlock.h> 40 #include <sys/percpu.h> 41 #include <sys/smr.h> 42 #include <sys/task.h> 43 44 #include <net/if.h> 45 #include <net/if_dl.h> 46 #include <net/if_types.h> 47 48 #include <netinet/in.h> 49 #include <netinet/if_ether.h> 50 51 #include <net/if_bridge.h> 52 53 #if NBPFILTER > 0 54 #include <net/bpf.h> 55 #endif 56 57 #if NPF > 0 58 #include <net/pfvar.h> 59 #endif 60 61 #if NVLAN > 0 62 #include <net/if_vlan_var.h> 63 #endif 64 65 /* 66 * tpmr interface 67 */ 68 69 #define TPMR_NUM_PORTS 2 70 71 struct tpmr_softc; 72 73 struct tpmr_port { 74 struct ifnet *p_ifp0; 75 76 int (*p_ioctl)(struct ifnet *, u_long, caddr_t); 77 int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *, 78 struct rtentry *); 79 80 struct task p_ltask; 81 struct task p_dtask; 82 83 struct tpmr_softc *p_tpmr; 84 unsigned int p_slot; 85 86 struct ether_brport p_brport; 87 }; 88 89 struct tpmr_softc { 90 struct ifnet sc_if; 91 unsigned int sc_dead; 92 93 struct tpmr_port *sc_ports[TPMR_NUM_PORTS]; 94 unsigned int sc_nports; 95 }; 96 97 #define DPRINTF(_sc, fmt...) do { \ 98 if (ISSET((_sc)->sc_if.if_flags, IFF_DEBUG)) \ 99 printf(fmt); \ 100 } while (0) 101 102 static int tpmr_clone_create(struct if_clone *, int); 103 static int tpmr_clone_destroy(struct ifnet *); 104 105 static int tpmr_ioctl(struct ifnet *, u_long, caddr_t); 106 static int tpmr_enqueue(struct ifnet *, struct mbuf *); 107 static int tpmr_output(struct ifnet *, struct mbuf *, struct sockaddr *, 108 struct rtentry *); 109 static void tpmr_start(struct ifqueue *); 110 111 static int tpmr_up(struct tpmr_softc *); 112 static int tpmr_down(struct tpmr_softc *); 113 static int tpmr_iff(struct tpmr_softc *); 114 115 static void tpmr_p_linkch(void *); 116 static void tpmr_p_detach(void *); 117 static int tpmr_p_ioctl(struct ifnet *, u_long, caddr_t); 118 static int tpmr_p_output(struct ifnet *, struct mbuf *, 119 struct sockaddr *, struct rtentry *); 120 121 static void tpmr_p_dtor(struct tpmr_softc *, struct tpmr_port *, 122 const char *); 123 static int tpmr_add_port(struct tpmr_softc *, 124 const struct ifbreq *); 125 static int tpmr_del_port(struct tpmr_softc *, 126 const struct ifbreq *); 127 static int tpmr_port_list(struct tpmr_softc *, struct ifbifconf *); 128 129 static struct if_clone tpmr_cloner = 130 IF_CLONE_INITIALIZER("tpmr", tpmr_clone_create, tpmr_clone_destroy); 131 132 void 133 tpmrattach(int count) 134 { 135 if_clone_attach(&tpmr_cloner); 136 } 137 138 static int 139 tpmr_clone_create(struct if_clone *ifc, int unit) 140 { 141 struct tpmr_softc *sc; 142 struct ifnet *ifp; 143 144 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO|M_CANFAIL); 145 if (sc == NULL) 146 return (ENOMEM); 147 148 ifp = &sc->sc_if; 149 150 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", 151 ifc->ifc_name, unit); 152 153 ifp->if_softc = sc; 154 ifp->if_type = IFT_BRIDGE; 155 ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; 156 ifp->if_mtu = 0; 157 ifp->if_addrlen = ETHER_ADDR_LEN; 158 ifp->if_hdrlen = ETHER_HDR_LEN; 159 ifp->if_ioctl = tpmr_ioctl; 160 ifp->if_output = tpmr_output; 161 ifp->if_enqueue = tpmr_enqueue; 162 ifp->if_qstart = tpmr_start; 163 ifp->if_flags = IFF_POINTOPOINT; 164 ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; 165 ifp->if_link_state = LINK_STATE_DOWN; 166 167 if_counters_alloc(ifp); 168 if_attach(ifp); 169 if_alloc_sadl(ifp); 170 171 #if NBPFILTER > 0 172 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN); 173 #endif 174 175 ifp->if_llprio = IFQ_MAXPRIO; 176 177 return (0); 178 } 179 180 static int 181 tpmr_clone_destroy(struct ifnet *ifp) 182 { 183 struct tpmr_softc *sc = ifp->if_softc; 184 unsigned int i; 185 186 NET_LOCK(); 187 sc->sc_dead = 1; 188 189 if (ISSET(ifp->if_flags, IFF_RUNNING)) 190 tpmr_down(sc); 191 NET_UNLOCK(); 192 193 if_detach(ifp); 194 195 NET_LOCK(); 196 for (i = 0; i < nitems(sc->sc_ports); i++) { 197 struct tpmr_port *p = SMR_PTR_GET_LOCKED(&sc->sc_ports[i]); 198 if (p == NULL) 199 continue; 200 tpmr_p_dtor(sc, p, "destroy"); 201 } 202 NET_UNLOCK(); 203 204 free(sc, M_DEVBUF, sizeof(*sc)); 205 206 return (0); 207 } 208 209 static int 210 tpmr_vlan_filter(const struct mbuf *m) 211 { 212 const struct ether_header *eh; 213 214 eh = mtod(m, struct ether_header *); 215 switch (ntohs(eh->ether_type)) { 216 case ETHERTYPE_VLAN: 217 case ETHERTYPE_QINQ: 218 return (1); 219 default: 220 break; 221 } 222 223 return (0); 224 } 225 226 static int 227 tpmr_8021q_filter(const struct mbuf *m, uint64_t dst) 228 { 229 if (ETH64_IS_8021_RSVD(dst)) { 230 switch (dst & 0xf) { 231 case 0x01: /* IEEE MAC-specific Control Protocols */ 232 case 0x02: /* IEEE 802.3 Slow Protocols */ 233 case 0x04: /* IEEE MAC-specific Control Protocols */ 234 case 0x0e: /* Individual LAN Scope, Nearest Bridge */ 235 return (1); 236 default: 237 break; 238 } 239 } 240 241 return (0); 242 } 243 244 #if NPF > 0 245 static struct mbuf * 246 tpmr_pf(struct ifnet *ifp0, int dir, struct mbuf *m) 247 { 248 struct ether_header *eh, copy; 249 sa_family_t af = AF_UNSPEC; 250 251 eh = mtod(m, struct ether_header *); 252 switch (ntohs(eh->ether_type)) { 253 case ETHERTYPE_IP: 254 af = AF_INET; 255 break; 256 case ETHERTYPE_IPV6: 257 af = AF_INET6; 258 break; 259 default: 260 return (m); 261 } 262 263 copy = *eh; 264 m_adj(m, sizeof(*eh)); 265 266 if (pf_test(af, dir, ifp0, &m) != PF_PASS) { 267 m_freem(m); 268 return (NULL); 269 } 270 if (m == NULL) 271 return (NULL); 272 273 m = m_prepend(m, sizeof(*eh), M_DONTWAIT); 274 if (m == NULL) 275 return (NULL); 276 277 /* checksum? */ 278 279 eh = mtod(m, struct ether_header *); 280 *eh = copy; 281 282 return (m); 283 } 284 #endif /* NPF > 0 */ 285 286 static struct mbuf * 287 tpmr_input(struct ifnet *ifp0, struct mbuf *m, uint64_t dst, void *brport) 288 { 289 struct tpmr_port *p = brport; 290 struct tpmr_softc *sc = p->p_tpmr; 291 struct ifnet *ifp = &sc->sc_if; 292 struct tpmr_port *pn; 293 int len; 294 #if NBPFILTER > 0 295 caddr_t if_bpf; 296 #endif 297 298 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 299 goto drop; 300 301 #if NVLAN > 0 302 /* 303 * If the underlying interface removed the VLAN header itself, 304 * add it back. 305 */ 306 if (ISSET(m->m_flags, M_VLANTAG)) { 307 m = vlan_inject(m, ETHERTYPE_VLAN, m->m_pkthdr.ether_vtag); 308 if (m == NULL) { 309 counters_inc(ifp->if_counters, ifc_ierrors); 310 goto drop; 311 } 312 } 313 #endif 314 315 if (!ISSET(ifp->if_flags, IFF_LINK2) && 316 tpmr_vlan_filter(m)) 317 goto drop; 318 319 if (!ISSET(ifp->if_flags, IFF_LINK0) && 320 tpmr_8021q_filter(m, dst)) 321 goto drop; 322 323 #if NPF > 0 324 if (!ISSET(ifp->if_flags, IFF_LINK1) && 325 (m = tpmr_pf(ifp0, PF_IN, m)) == NULL) 326 return (NULL); 327 #endif 328 329 len = m->m_pkthdr.len; 330 counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len); 331 332 #if NBPFILTER > 0 333 if_bpf = ifp->if_bpf; 334 if (if_bpf) { 335 if (bpf_mtap(if_bpf, m, 0)) 336 goto drop; 337 } 338 #endif 339 340 smr_read_enter(); 341 pn = SMR_PTR_GET(&sc->sc_ports[!p->p_slot]); 342 if (pn == NULL) 343 m_freem(m); 344 else { 345 struct ifnet *ifpn = pn->p_ifp0; 346 347 #if NPF > 0 348 if (!ISSET(ifp->if_flags, IFF_LINK1) && 349 (m = tpmr_pf(ifpn, PF_OUT, m)) == NULL) 350 ; 351 else 352 #endif 353 if (if_enqueue(ifpn, m)) 354 counters_inc(ifp->if_counters, ifc_oerrors); 355 else { 356 counters_pkt(ifp->if_counters, 357 ifc_opackets, ifc_obytes, len); 358 } 359 } 360 smr_read_leave(); 361 362 return (NULL); 363 364 drop: 365 m_freem(m); 366 return (NULL); 367 } 368 369 static int 370 tpmr_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 371 struct rtentry *rt) 372 { 373 m_freem(m); 374 return (ENODEV); 375 } 376 377 static int 378 tpmr_enqueue(struct ifnet *ifp, struct mbuf *m) 379 { 380 m_freem(m); 381 return (ENODEV); 382 } 383 384 static void 385 tpmr_start(struct ifqueue *ifq) 386 { 387 ifq_purge(ifq); 388 } 389 390 static int 391 tpmr_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 392 { 393 struct tpmr_softc *sc = ifp->if_softc; 394 int error = 0; 395 396 if (sc->sc_dead) 397 return (ENXIO); 398 399 switch (cmd) { 400 case SIOCSIFFLAGS: 401 if (ISSET(ifp->if_flags, IFF_UP)) { 402 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 403 error = tpmr_up(sc); 404 } else { 405 if (ISSET(ifp->if_flags, IFF_RUNNING)) 406 error = tpmr_down(sc); 407 } 408 break; 409 410 case SIOCBRDGADD: 411 error = suser(curproc); 412 if (error != 0) 413 break; 414 415 error = tpmr_add_port(sc, (struct ifbreq *)data); 416 break; 417 case SIOCBRDGDEL: 418 error = suser(curproc); 419 if (error != 0) 420 break; 421 422 error = tpmr_del_port(sc, (struct ifbreq *)data); 423 break; 424 case SIOCBRDGIFS: 425 error = tpmr_port_list(sc, (struct ifbifconf *)data); 426 break; 427 /* stub for ifconfig(8) brconfig.c:bridge_rules() */ 428 case SIOCBRDGGRL: 429 ((struct ifbrlconf *)data)->ifbrl_len = 0; 430 break; 431 432 default: 433 error = ENOTTY; 434 break; 435 } 436 437 if (error == ENETRESET) 438 error = tpmr_iff(sc); 439 440 return (error); 441 } 442 443 static int 444 tpmr_add_port(struct tpmr_softc *sc, const struct ifbreq *req) 445 { 446 struct ifnet *ifp = &sc->sc_if; 447 struct ifnet *ifp0; 448 struct tpmr_port **pp; 449 struct tpmr_port *p; 450 int i; 451 int error; 452 453 NET_ASSERT_LOCKED(); 454 if (sc->sc_nports >= nitems(sc->sc_ports)) 455 return (ENOSPC); 456 457 ifp0 = if_unit(req->ifbr_ifsname); 458 if (ifp0 == NULL) 459 return (EINVAL); 460 461 if (ifp0->if_type != IFT_ETHER) { 462 error = EPROTONOSUPPORT; 463 goto put; 464 } 465 466 error = ether_brport_isset(ifp0); 467 if (error != 0) 468 goto put; 469 470 /* let's try */ 471 472 p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK|M_ZERO|M_CANFAIL); 473 if (p == NULL) { 474 error = ENOMEM; 475 goto put; 476 } 477 478 p->p_ifp0 = ifp0; 479 p->p_tpmr = sc; 480 481 p->p_ioctl = ifp0->if_ioctl; 482 p->p_output = ifp0->if_output; 483 484 error = ifpromisc(ifp0, 1); 485 if (error != 0) 486 goto free; 487 488 /* this might have changed if we slept for malloc or ifpromisc */ 489 error = ether_brport_isset(ifp0); 490 if (error != 0) 491 goto unpromisc; 492 493 task_set(&p->p_ltask, tpmr_p_linkch, p); 494 if_linkstatehook_add(ifp0, &p->p_ltask); 495 496 task_set(&p->p_dtask, tpmr_p_detach, p); 497 if_detachhook_add(ifp0, &p->p_dtask); 498 499 p->p_brport.eb_input = tpmr_input; 500 p->p_brport.eb_port = p; 501 502 /* commit */ 503 DPRINTF(sc, "%s %s trunkport: creating port\n", 504 ifp->if_xname, ifp0->if_xname); 505 506 for (i = 0; i < nitems(sc->sc_ports); i++) { 507 pp = &sc->sc_ports[i]; 508 if (SMR_PTR_GET_LOCKED(pp) == NULL) 509 break; 510 } 511 sc->sc_nports++; 512 513 p->p_slot = i; 514 515 ether_brport_set(ifp0, &p->p_brport); 516 ifp0->if_ioctl = tpmr_p_ioctl; 517 ifp0->if_output = tpmr_p_output; 518 519 SMR_PTR_SET_LOCKED(pp, p); 520 521 tpmr_p_linkch(p); 522 523 return (0); 524 525 unpromisc: 526 ifpromisc(ifp0, 0); 527 free: 528 free(p, M_DEVBUF, sizeof(*p)); 529 put: 530 if_put(ifp0); 531 return (error); 532 } 533 534 static struct tpmr_port * 535 tpmr_trunkport(struct tpmr_softc *sc, const char *name) 536 { 537 unsigned int i; 538 539 for (i = 0; i < nitems(sc->sc_ports); i++) { 540 struct tpmr_port *p = SMR_PTR_GET_LOCKED(&sc->sc_ports[i]); 541 if (p == NULL) 542 continue; 543 544 if (strcmp(p->p_ifp0->if_xname, name) == 0) 545 return (p); 546 } 547 548 return (NULL); 549 } 550 551 static int 552 tpmr_del_port(struct tpmr_softc *sc, const struct ifbreq *req) 553 { 554 struct tpmr_port *p; 555 556 NET_ASSERT_LOCKED(); 557 p = tpmr_trunkport(sc, req->ifbr_ifsname); 558 if (p == NULL) 559 return (EINVAL); 560 561 tpmr_p_dtor(sc, p, "del"); 562 563 return (0); 564 } 565 566 567 static int 568 tpmr_port_list(struct tpmr_softc *sc, struct ifbifconf *bifc) 569 { 570 struct tpmr_port *p; 571 struct ifbreq breq; 572 int i = 0, total = nitems(sc->sc_ports), n = 0, error = 0; 573 574 NET_ASSERT_LOCKED(); 575 576 if (bifc->ifbic_len == 0) { 577 n = total; 578 goto done; 579 } 580 581 for (i = 0; i < total; i++) { 582 memset(&breq, 0, sizeof(breq)); 583 584 if (bifc->ifbic_len < sizeof(breq)) 585 break; 586 587 p = SMR_PTR_GET_LOCKED(&sc->sc_ports[i]); 588 if (p == NULL) 589 continue; 590 strlcpy(breq.ifbr_ifsname, p->p_ifp0->if_xname, IFNAMSIZ); 591 592 /* flag as span port so ifconfig(8)'s brconfig.c:bridge_list() 593 * stays quiet wrt. STP */ 594 breq.ifbr_ifsflags = IFBIF_SPAN; 595 strlcpy(breq.ifbr_name, sc->sc_if.if_xname, IFNAMSIZ); 596 if ((error = copyout(&breq, bifc->ifbic_req + n, 597 sizeof(breq))) != 0) 598 goto done; 599 600 bifc->ifbic_len -= sizeof(breq); 601 n++; 602 } 603 604 done: 605 bifc->ifbic_len = n * sizeof(breq); 606 return (error); 607 } 608 609 static int 610 tpmr_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data) 611 { 612 const struct ether_brport *eb = ether_brport_get_locked(ifp0); 613 struct tpmr_port *p; 614 int error = 0; 615 616 KASSERTMSG(eb != NULL, 617 "%s: %s called without an ether_brport set", 618 ifp0->if_xname, __func__); 619 KASSERTMSG(eb->eb_input == tpmr_input, 620 "%s: %s called, but eb_input seems wrong (%p != tpmr_input())", 621 ifp0->if_xname, __func__, eb->eb_input); 622 623 p = eb->eb_port; 624 625 switch (cmd) { 626 case SIOCSIFADDR: 627 error = EBUSY; 628 break; 629 630 default: 631 error = (*p->p_ioctl)(ifp0, cmd, data); 632 break; 633 } 634 635 return (error); 636 } 637 638 static int 639 tpmr_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, 640 struct rtentry *rt) 641 { 642 int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *, 643 struct rtentry *) = NULL; 644 const struct ether_brport *eb; 645 646 /* restrict transmission to bpf only */ 647 if ((m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) { 648 m_freem(m); 649 return (EBUSY); 650 } 651 652 smr_read_enter(); 653 eb = ether_brport_get(ifp0); 654 if (eb != NULL && eb->eb_input == tpmr_input) { 655 struct tpmr_port *p = eb->eb_port; 656 p_output = p->p_output; /* code doesn't go away */ 657 } 658 smr_read_leave(); 659 660 if (p_output == NULL) { 661 m_freem(m); 662 return (ENXIO); 663 } 664 665 return ((*p_output)(ifp0, m, dst, rt)); 666 } 667 668 static void 669 tpmr_p_dtor(struct tpmr_softc *sc, struct tpmr_port *p, const char *op) 670 { 671 struct ifnet *ifp = &sc->sc_if; 672 struct ifnet *ifp0 = p->p_ifp0; 673 674 DPRINTF(sc, "%s %s: destroying port\n", 675 ifp->if_xname, ifp0->if_xname); 676 677 ifp0->if_ioctl = p->p_ioctl; 678 ifp0->if_output = p->p_output; 679 680 ether_brport_clr(ifp0); 681 682 sc->sc_nports--; 683 SMR_PTR_SET_LOCKED(&sc->sc_ports[p->p_slot], NULL); 684 685 if (ifpromisc(ifp0, 0) != 0) { 686 log(LOG_WARNING, "%s %s: unable to disable promisc\n", 687 ifp->if_xname, ifp0->if_xname); 688 } 689 690 if_detachhook_del(ifp0, &p->p_dtask); 691 if_linkstatehook_del(ifp0, &p->p_ltask); 692 693 smr_barrier(); 694 695 if_put(ifp0); 696 free(p, M_DEVBUF, sizeof(*p)); 697 698 if (ifp->if_link_state != LINK_STATE_DOWN) { 699 ifp->if_link_state = LINK_STATE_DOWN; 700 if_link_state_change(ifp); 701 } 702 } 703 704 static void 705 tpmr_p_detach(void *arg) 706 { 707 struct tpmr_port *p = arg; 708 struct tpmr_softc *sc = p->p_tpmr; 709 710 tpmr_p_dtor(sc, p, "detach"); 711 712 NET_ASSERT_LOCKED(); 713 } 714 715 static int 716 tpmr_p_active(struct tpmr_port *p) 717 { 718 struct ifnet *ifp0 = p->p_ifp0; 719 720 return (ISSET(ifp0->if_flags, IFF_RUNNING) && 721 LINK_STATE_IS_UP(ifp0->if_link_state)); 722 } 723 724 static void 725 tpmr_p_linkch(void *arg) 726 { 727 struct tpmr_port *p = arg; 728 struct tpmr_softc *sc = p->p_tpmr; 729 struct ifnet *ifp = &sc->sc_if; 730 struct tpmr_port *np; 731 u_char link_state = LINK_STATE_FULL_DUPLEX; 732 733 NET_ASSERT_LOCKED(); 734 735 if (!tpmr_p_active(p)) 736 link_state = LINK_STATE_DOWN; 737 738 np = SMR_PTR_GET_LOCKED(&sc->sc_ports[!p->p_slot]); 739 if (np == NULL || !tpmr_p_active(np)) 740 link_state = LINK_STATE_DOWN; 741 742 if (ifp->if_link_state != link_state) { 743 ifp->if_link_state = link_state; 744 if_link_state_change(ifp); 745 } 746 } 747 748 static int 749 tpmr_up(struct tpmr_softc *sc) 750 { 751 struct ifnet *ifp = &sc->sc_if; 752 753 NET_ASSERT_LOCKED(); 754 SET(ifp->if_flags, IFF_RUNNING); 755 756 return (0); 757 } 758 759 static int 760 tpmr_iff(struct tpmr_softc *sc) 761 { 762 return (0); 763 } 764 765 static int 766 tpmr_down(struct tpmr_softc *sc) 767 { 768 struct ifnet *ifp = &sc->sc_if; 769 770 NET_ASSERT_LOCKED(); 771 CLR(ifp->if_flags, IFF_RUNNING); 772 773 return (0); 774 } 775