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