xref: /openbsd-src/sys/net/if_tpmr.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
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