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