if_agr.c revision 1.49 1 /* $NetBSD: if_agr.c,v 1.49 2019/04/26 11:51:56 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c)2005 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: if_agr.c,v 1.49 2019/04/26 11:51:56 pgoyette Exp $");
31
32 #ifdef _KERNEL_OPT
33 #include "opt_inet.h"
34 #endif
35
36 #include <sys/param.h>
37 #include <sys/callout.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
42 #include <sys/queue.h>
43 #include <sys/sockio.h>
44 #include <sys/proc.h> /* XXX for curproc */
45 #include <sys/kauth.h>
46 #include <sys/xcall.h>
47 #include <sys/device.h>
48 #include <sys/module.h>
49 #include <sys/atomic.h>
50
51 #include <net/bpf.h>
52 #include <net/if.h>
53 #include <net/if_dl.h>
54 #include <net/if_types.h>
55 #include <net/if_ether.h>
56 #include <net/if_vlanvar.h>
57
58 #if defined(INET)
59 #include <netinet/in.h>
60 #include <netinet/if_inarp.h>
61 #endif
62
63 #include <net/agr/if_agrvar.h>
64 #include <net/agr/if_agrvar_impl.h>
65 #include <net/agr/if_agrioctl.h>
66 #include <net/agr/if_agrsubr.h>
67 #include <net/agr/if_agrethervar.h>
68
69 #include "ioconf.h"
70
71 static int agr_clone_create(struct if_clone *, int);
72 static int agr_clone_destroy(struct ifnet *);
73 static void agr_start(struct ifnet *);
74 static int agr_setconfig(struct agr_softc *, const struct agrreq *);
75 static int agr_getconfig(struct agr_softc *, struct agrreq *);
76 static int agr_getportlist(struct agr_softc *, struct agrreq *);
77 static int agr_addport(struct ifnet *, struct ifnet *);
78 static int agr_remport(struct ifnet *, struct ifnet *);
79 static int agrreq_copyin(const void *, struct agrreq *);
80 static int agrreq_copyout(void *, struct agrreq *);
81 static int agr_ioctl(struct ifnet *, u_long, void *);
82 static struct agr_port *agr_select_tx_port(struct agr_softc *, struct mbuf *);
83 static int agr_ioctl_filter(struct ifnet *, u_long, void *);
84 static void agr_reset_iftype(struct ifnet *);
85 static int agr_config_promisc(struct agr_softc *);
86 static int agrport_config_promisc_callback(struct agr_port *, void *);
87 static int agrport_config_promisc(struct agr_port *, bool);
88 static int agrport_cleanup(struct agr_softc *, struct agr_port *);
89
90 static int agr_enter(struct agr_softc *);
91 static void agr_exit(struct agr_softc *);
92 static int agr_pause(struct agr_softc *);
93 static void agr_evacuate(struct agr_softc *);
94 static void agr_sync(void);
95 static void agr_ports_lock(struct agr_softc *);
96 static void agr_ports_unlock(struct agr_softc *);
97 static bool agr_ports_enter(struct agr_softc *);
98 static void agr_ports_exit(struct agr_softc *);
99
100 static struct if_clone agr_cloner =
101 IF_CLONE_INITIALIZER("agr", agr_clone_create, agr_clone_destroy);
102
103 static u_int agr_count;
104
105 /*
106 * EXPORTED FUNCTIONS
107 */
108
109 /*
110 * agrattch: device attach routine.
111 */
112
113 void
114 agrattach(int count)
115 {
116
117 /*
118 * Nothing to do here, initialization is handled by the
119 * module initialization code in agrinit() below).
120 */
121 }
122
123 static void
124 agrinit(void)
125 {
126 if_clone_attach(&agr_cloner);
127 }
128
129 static int
130 agrdetach(void)
131 {
132 int error = 0;
133
134 if (agr_count != 0)
135 error = EBUSY;
136
137 if (error == 0)
138 if_clone_detach(&agr_cloner);
139
140 return error;
141 }
142
143 /*
144 * agr_input: frame collector.
145 */
146
147 void
148 agr_input(struct ifnet *ifp_port, struct mbuf *m)
149 {
150 struct ethercom *ec = (struct ethercom *)ifp_port;
151 struct agr_port *port;
152 struct ifnet *ifp;
153
154 port = ifp_port->if_agrprivate;
155 KASSERT(port);
156 ifp = port->port_agrifp;
157 if ((port->port_flags & AGRPORT_COLLECTING) == 0) {
158 m_freem(m);
159 ifp->if_ierrors++;
160 return;
161 }
162
163 m_set_rcvif(m, ifp);
164
165 /*
166 * If VLANs are configured on the interface, check to
167 * see if the device performed the decapsulation and
168 * provided us with the tag.
169 */
170 if (ec->ec_nvlans && vlan_has_tag(m)) {
171 MODULE_HOOK_CALL_VOID(if_vlan_vlan_input_hook, (ifp, m),
172 m_freem(m));
173 return;
174 }
175
176 if_percpuq_enqueue(ifp->if_percpuq, m);
177 }
178
179 /*
180 * EXPORTED AGR-INTERNAL FUNCTIONS
181 */
182
183 void
184 agr_lock(struct agr_softc *sc)
185 {
186
187 mutex_enter(&sc->sc_lock);
188 }
189
190 void
191 agr_unlock(struct agr_softc *sc)
192 {
193
194 mutex_exit(&sc->sc_lock);
195 }
196
197 /*
198 * agr_xmit_frame: transmit a pre-built frame.
199 */
200
201 int
202 agr_xmit_frame(struct ifnet *ifp_port, struct mbuf *m)
203 {
204 int error;
205
206 struct sockaddr_storage dst0;
207 struct sockaddr *dst;
208 int hdrlen;
209
210 /*
211 * trim off link level header and let if_output re-add it.
212 * XXX better to introduce an API to transmit pre-built frames.
213 */
214
215 hdrlen = ifp_port->if_hdrlen;
216 if (m->m_pkthdr.len < hdrlen) {
217 m_freem(m);
218 return EINVAL;
219 }
220 memset(&dst0, 0, sizeof(dst0));
221 dst = (struct sockaddr *)&dst0;
222 dst->sa_family = pseudo_AF_HDRCMPLT;
223 dst->sa_len = hdrlen;
224 m_copydata(m, 0, hdrlen, &dst->sa_data);
225 m_adj(m, hdrlen);
226
227 error = if_output_lock(ifp_port, ifp_port, m, dst, NULL);
228
229 return error;
230 }
231
232 int
233 agrport_ioctl(struct agr_port *port, u_long cmd, void *arg)
234 {
235 struct ifnet *ifp = port->port_ifp;
236
237 KASSERT(ifp->if_agrprivate == (void *)port);
238 KASSERT(ifp->if_ioctl == agr_ioctl_filter);
239
240 return (*port->port_ioctl)(ifp, cmd, arg);
241 }
242
243 /*
244 * INTERNAL FUNCTIONS
245 */
246 /*
247 * Check for vlan attach/detach.
248 * ec->ec_nvlans is directly modified by the vlan driver.
249 * We keep a local count in sc (sc->sc_nvlans) to detect
250 * when the vlan driver attaches or detaches.
251 * Note the agr interface must be up for this to work.
252 */
253 static void
254 agr_vlan_check(struct ifnet *ifp, struct agr_softc *sc)
255 {
256 struct ethercom *ec = (void *)ifp;
257
258 /* vlans in sync? */
259 if (sc->sc_nvlans == ec->ec_nvlans) {
260 return;
261 }
262
263 if (sc->sc_nvlans == 0) {
264 /* vlan added */
265 agr_port_foreach(sc, agr_vlan_add, NULL);
266 sc->sc_nvlans = ec->ec_nvlans;
267 } else if (ec->ec_nvlans == 0) {
268 bool force_zero = false;
269 /* vlan removed */
270 agr_port_foreach(sc, agr_vlan_del, &force_zero);
271 sc->sc_nvlans = 0;
272 }
273 }
274
275 static int
276 agr_clone_create(struct if_clone *ifc, int unit)
277 {
278 struct agr_softc *sc;
279 struct ifnet *ifp;
280 int error;
281
282 sc = agr_alloc_softc();
283 error = agrtimer_init(sc);
284 if (error) {
285 agr_free_softc(sc);
286 return error;
287 }
288 TAILQ_INIT(&sc->sc_ports);
289 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NET);
290 mutex_init(&sc->sc_entry_mtx, MUTEX_DEFAULT, IPL_NONE);
291 cv_init(&sc->sc_insc_cv, "agrsoftc");
292 cv_init(&sc->sc_ports_cv, "agrports");
293 ifp = &sc->sc_if;
294 snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
295 ifc->ifc_name, unit);
296
297 ifp->if_softc = sc;
298 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
299 ifp->if_start = agr_start;
300 ifp->if_ioctl = agr_ioctl;
301 IFQ_SET_READY(&ifp->if_snd);
302
303 if_attach(ifp);
304
305 agr_reset_iftype(ifp);
306 atomic_inc_uint(&agr_count);
307 return 0;
308 }
309
310 static void
311 agr_reset_iftype(struct ifnet *ifp)
312 {
313
314 ifp->if_type = IFT_OTHER;
315 ifp->if_dlt = DLT_NULL;
316 ifp->if_addrlen = 0;
317 if_alloc_sadl(ifp);
318 }
319
320 static int
321 agr_clone_destroy(struct ifnet *ifp)
322 {
323 struct agr_softc *sc = ifp->if_softc;
324 int error;
325
326 if ((error = agr_pause(sc)) != 0)
327 return error;
328
329 if_detach(ifp);
330 agrtimer_destroy(sc);
331 /* Now that the ifnet has been detached, and our
332 * component ifnets are disconnected, there can be
333 * no new threads in the softc. Wait for every
334 * thread to get out of the softc.
335 */
336 agr_evacuate(sc);
337 mutex_destroy(&sc->sc_lock);
338 mutex_destroy(&sc->sc_entry_mtx);
339 cv_destroy(&sc->sc_insc_cv);
340 cv_destroy(&sc->sc_ports_cv);
341 agr_free_softc(sc);
342
343 atomic_dec_uint(&agr_count);
344 return 0;
345 }
346
347 static struct agr_port *
348 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m)
349 {
350
351 return (*sc->sc_iftop->iftop_select_tx_port)(sc, m);
352 }
353
354 #if 0 /* "generic" version */
355 static struct agr_port *
356 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m)
357 {
358 struct agr_port *port;
359 uint32_t hash;
360
361 hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m);
362 if (sc->sc_nports == 0)
363 return NULL;
364 hash %= sc->sc_nports;
365 port = TAILQ_FIRST(&sc->sc_ports);
366 KASSERT(port != NULL);
367 while (hash--) {
368 port = TAILQ_NEXT(port, port_q);
369 KASSERT(port != NULL);
370 }
371
372 return port;
373 }
374 #endif /* 0 */
375
376 static void
377 agr_start(struct ifnet *ifp)
378 {
379 struct agr_softc *sc = ifp->if_softc;
380 struct mbuf *m;
381
382 AGR_LOCK(sc);
383
384 while (/* CONSTCOND */ 1) {
385 struct agr_port *port;
386
387 IFQ_DEQUEUE(&ifp->if_snd, m);
388 if (m == NULL) {
389 break;
390 }
391 bpf_mtap(ifp, m, BPF_D_OUT);
392 port = agr_select_tx_port(sc, m);
393 if (port) {
394 int error;
395
396 error = agr_xmit_frame(port->port_ifp, m);
397 if (error) {
398 ifp->if_oerrors++;
399 } else {
400 ifp->if_opackets++;
401 }
402 } else {
403 m_freem(m);
404 ifp->if_oerrors++;
405 }
406 }
407
408 AGR_UNLOCK(sc);
409
410 ifp->if_flags &= ~IFF_OACTIVE;
411 }
412
413 static int
414 agr_setconfig(struct agr_softc *sc, const struct agrreq *ar)
415 {
416 struct ifnet *ifp = &sc->sc_if;
417 int cmd = ar->ar_cmd;
418 struct ifnet *ifp_port;
419 int error = 0;
420 char ifname[IFNAMSIZ];
421
422 memset(ifname, 0, sizeof(ifname));
423 error = copyin(ar->ar_buf, ifname,
424 MIN(ar->ar_buflen, sizeof(ifname) - 1));
425 if (error) {
426 return error;
427 }
428 ifp_port = ifunit(ifname);
429 if (ifp_port == NULL) {
430 return ENOENT;
431 }
432
433 agr_ports_lock(sc);
434 switch (cmd) {
435 case AGRCMD_ADDPORT:
436 error = agr_addport(ifp, ifp_port);
437 break;
438
439 case AGRCMD_REMPORT:
440 error = agr_remport(ifp, ifp_port);
441 break;
442
443 default:
444 error = EINVAL;
445 break;
446 }
447 agr_ports_unlock(sc);
448
449 return error;
450 }
451
452 static int
453 agr_getportlist(struct agr_softc *sc, struct agrreq *ar)
454 {
455 struct agr_port *port;
456 struct agrportlist apl;
457 struct agrportinfo api;
458 char *cp = ar->ar_buf;
459 size_t bufleft = (cp == NULL) ? 0 : ar->ar_buflen;
460 int error;
461
462 if (cp != NULL) {
463 memset(&apl, 0, sizeof(apl));
464 memset(&api, 0, sizeof(api));
465
466 if (bufleft < sizeof(apl)) {
467 return E2BIG;
468 }
469 apl.apl_nports = sc->sc_nports;
470 error = copyout(&apl, cp, sizeof(apl));
471 if (error) {
472 return error;
473 }
474 cp += sizeof(apl);
475 }
476 bufleft -= sizeof(apl);
477
478 TAILQ_FOREACH(port, &sc->sc_ports, port_q) {
479 if (cp != NULL) {
480 if (bufleft < sizeof(api)) {
481 return E2BIG;
482 }
483 memcpy(api.api_ifname, port->port_ifp->if_xname,
484 sizeof(api.api_ifname));
485 api.api_flags = 0;
486 if (port->port_flags & AGRPORT_COLLECTING) {
487 api.api_flags |= AGRPORTINFO_COLLECTING;
488 }
489 if (port->port_flags & AGRPORT_DISTRIBUTING) {
490 api.api_flags |= AGRPORTINFO_DISTRIBUTING;
491 }
492 error = copyout(&api, cp, sizeof(api));
493 if (error) {
494 return error;
495 }
496 cp += sizeof(api);
497 }
498 bufleft -= sizeof(api);
499 }
500
501 if (cp == NULL) {
502 ar->ar_buflen = -bufleft; /* necessary buffer size */
503 }
504
505 return 0;
506 }
507
508 static int
509 agr_getconfig(struct agr_softc *sc, struct agrreq *ar)
510 {
511 int cmd = ar->ar_cmd;
512 int error;
513
514 (void)agr_ports_enter(sc);
515 switch (cmd) {
516 case AGRCMD_PORTLIST:
517 error = agr_getportlist(sc, ar);
518 break;
519
520 default:
521 error = EINVAL;
522 break;
523 }
524 agr_ports_exit(sc);
525
526 return error;
527 }
528
529 static int
530 agr_addport(struct ifnet *ifp, struct ifnet *ifp_port)
531 {
532 const struct ifaddr *ifa;
533 struct agr_softc *sc = ifp->if_softc;
534 struct agr_port *port = NULL;
535 int error = 0;
536 int s;
537
538 if (ifp_port->if_ioctl == NULL) {
539 error = EOPNOTSUPP;
540 goto out;
541 }
542
543 if (ifp_port->if_agrprivate) {
544 error = EBUSY;
545 goto out;
546 }
547
548 if (ifp_port->if_start == agr_start) {
549 error = EINVAL;
550 goto out;
551 }
552
553 port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF,
554 M_WAITOK | M_ZERO);
555 if (port == NULL) {
556 error = ENOMEM;
557 goto out;
558 }
559 port->port_flags = AGRPORT_LARVAL;
560
561 s = pserialize_read_enter();
562 IFADDR_READER_FOREACH(ifa, ifp_port) {
563 if (ifa->ifa_addr->sa_family != AF_LINK) {
564 pserialize_read_exit(s);
565 error = EBUSY;
566 goto out;
567 }
568 }
569 pserialize_read_exit(s);
570
571 if (sc->sc_nports == 0) {
572 switch (ifp_port->if_type) {
573 case IFT_ETHER:
574 sc->sc_iftop = &agrether_ops;
575 break;
576
577 default:
578 error = EPROTONOSUPPORT; /* XXX */
579 goto out;
580 }
581
582 error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port);
583 if (error)
584 goto out;
585 agrtimer_start(sc);
586 } else {
587 if (ifp->if_type != ifp_port->if_type) {
588 error = EINVAL;
589 goto out;
590 }
591 if (ifp->if_addrlen != ifp_port->if_addrlen) {
592 error = EINVAL;
593 goto out;
594 }
595 }
596
597 memcpy(port->port_origlladdr, CLLADDR(ifp_port->if_sadl),
598 ifp_port->if_addrlen);
599
600 /*
601 * start to modify ifp_port.
602 */
603
604 /*
605 * XXX this should probably be SIOCALIFADDR but that doesn't
606 * appear to work (ENOTTY). We want to change the mac address
607 * of each port to that of the first port. No need for arps
608 * since there are no inet addresses assigned to the ports.
609 */
610 IFNET_LOCK(ifp_port);
611 error = if_addr_init(ifp_port, ifp->if_dl, true);
612 IFNET_UNLOCK(ifp_port);
613
614 if (error) {
615 printf("%s: if_addr_init error %d\n", __func__, error);
616 goto cleanup;
617 }
618 port->port_flags |= AGRPORT_LADDRCHANGED;
619
620 ifp->if_type = ifp_port->if_type;
621 AGR_LOCK(sc);
622
623 port->port_ifp = ifp_port;
624 ifp_port->if_agrprivate = port;
625 port->port_agrifp = ifp;
626 TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q);
627 sc->sc_nports++;
628
629 port->port_ioctl = ifp_port->if_ioctl;
630 ifp_port->if_ioctl = agr_ioctl_filter;
631
632 port->port_flags |= AGRPORT_ATTACHED;
633
634 AGR_UNLOCK(sc);
635
636 error = (*sc->sc_iftop->iftop_portinit)(sc, port);
637 if (error) {
638 printf("%s: portinit error %d\n", __func__, error);
639 goto cleanup;
640 }
641
642 agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0);
643 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, true);
644 if (error) {
645 printf("%s: configmulti error %d\n", __func__, error);
646 goto cleanup;
647 }
648
649 AGR_LOCK(sc);
650 port->port_flags &= ~AGRPORT_LARVAL;
651 AGR_UNLOCK(sc);
652 out:
653 if (error && port) {
654 free(port, M_DEVBUF);
655 }
656 if (error == 0)
657 ifp->if_flags |= IFF_RUNNING;
658 return error;
659
660 cleanup:
661 if (agrport_cleanup(sc, port)) {
662 printf("%s: error on cleanup\n", __func__);
663
664 port = NULL; /* XXX */
665 }
666
667 if (sc->sc_nports == 0) {
668 KASSERT(TAILQ_EMPTY(&sc->sc_ports));
669 agrtimer_stop(sc);
670 (*sc->sc_iftop->iftop_dtor)(sc);
671 sc->sc_iftop = NULL;
672 agr_reset_iftype(ifp);
673 } else {
674 KASSERT(!TAILQ_EMPTY(&sc->sc_ports));
675 }
676
677 goto out;
678 }
679
680 static int
681 agr_remport(struct ifnet *ifp, struct ifnet *ifp_port)
682 {
683 struct agr_softc *sc = ifp->if_softc;
684 struct agr_port *port;
685 int error = 0;
686
687 if (ifp_port->if_agrprivate == NULL) {
688 error = ENOENT;
689 return error;
690 }
691
692 port = ifp_port->if_agrprivate;
693 if (port->port_agrifp != ifp) {
694 error = EINVAL;
695 return error;
696 }
697
698 KASSERT(sc->sc_nports > 0);
699
700 AGR_LOCK(sc);
701 port->port_flags |= AGRPORT_DETACHING;
702 AGR_UNLOCK(sc);
703
704 error = (*sc->sc_iftop->iftop_portfini)(sc, port);
705 if (error) {
706 /* XXX XXX */
707 printf("%s: portfini error %d\n", __func__, error);
708 goto out;
709 }
710
711 error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, false);
712 if (error) {
713 /* XXX XXX */
714 printf("%s: configmulti_port error %d\n", __func__, error);
715 goto out;
716 }
717
718 error = agrport_cleanup(sc, port);
719 if (error) {
720 /* XXX XXX */
721 printf("%s: agrport_cleanup error %d\n", __func__, error);
722 goto out;
723 }
724
725 free(port, M_DEVBUF);
726
727 out:
728 if (sc->sc_nports == 0) {
729 KASSERT(TAILQ_EMPTY(&sc->sc_ports));
730 agrtimer_stop(sc);
731 (*sc->sc_iftop->iftop_dtor)(sc);
732 sc->sc_iftop = NULL;
733 /* XXX should purge all addresses? */
734 agr_reset_iftype(ifp);
735 } else {
736 KASSERT(!TAILQ_EMPTY(&sc->sc_ports));
737 }
738
739 return error;
740 }
741
742 static int
743 agrport_cleanup(struct agr_softc *sc, struct agr_port *port)
744 {
745 struct ifnet *ifp_port = port->port_ifp;
746 int error;
747 int result = 0;
748
749 error = agrport_config_promisc(port, false);
750 if (error) {
751 printf("%s: config_promisc error %d\n", __func__, error);
752 result = error;
753 }
754
755 if ((port->port_flags & AGRPORT_LADDRCHANGED)) {
756 #if 0
757 memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr,
758 ifp_port->if_addrlen);
759 if (ifp_port->if_init != NULL) {
760 error = (*ifp_port->if_init)(ifp_port);
761 }
762 #else
763 union {
764 struct sockaddr sa;
765 struct sockaddr_dl sdl;
766 struct sockaddr_storage ss;
767 } u;
768 struct ifaddr ifa;
769
770 sockaddr_dl_init(&u.sdl, sizeof(u.ss),
771 0, ifp_port->if_type, NULL, 0,
772 port->port_origlladdr, ifp_port->if_addrlen);
773 memset(&ifa, 0, sizeof(ifa));
774 ifa.ifa_addr = &u.sa;
775 error = agrport_ioctl(port, SIOCINITIFADDR, &ifa);
776 #endif
777 if (error) {
778 printf("%s: if_init error %d\n", __func__, error);
779 result = error;
780 } else {
781 port->port_flags &= ~AGRPORT_LADDRCHANGED;
782 }
783 }
784
785 AGR_LOCK(sc);
786 if ((port->port_flags & AGRPORT_ATTACHED)) {
787 ifp_port->if_agrprivate = NULL;
788
789 TAILQ_REMOVE(&sc->sc_ports, port, port_q);
790 sc->sc_nports--;
791
792 KASSERT(ifp_port->if_ioctl == agr_ioctl_filter);
793 ifp_port->if_ioctl = port->port_ioctl;
794
795 port->port_flags &= ~AGRPORT_ATTACHED;
796 }
797 AGR_UNLOCK(sc);
798
799 return result;
800 }
801
802 static int
803 agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr)
804 {
805 struct agr_softc *sc = ifp->if_softc;
806 int error;
807
808 error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr,
809 (cmd == SIOCADDMULTI));
810
811 return error;
812 }
813
814 /*
815 * XXX an incomplete hack; can't filter ioctls handled ifioctl().
816 *
817 * the intention here is to prevent operations on underlying interfaces
818 * so that their states are not changed in the way that agr(4) doesn't
819 * expect. cf. the BUGS section in the agr(4) manual page.
820 */
821 static int
822 agr_ioctl_filter(struct ifnet *ifp, u_long cmd, void *arg)
823 {
824 struct agr_port *port = ifp->if_agrprivate;
825 int error;
826
827 KASSERT(port);
828
829 switch (cmd) {
830 case SIOCADDMULTI: /* add m'cast addr */
831 case SIOCAIFADDR: /* add/chg IF alias */
832 case SIOCALIFADDR: /* add IF addr */
833 case SIOCDELMULTI: /* del m'cast addr */
834 case SIOCDIFADDR: /* delete IF addr */
835 case SIOCDIFPHYADDR: /* delete gif addrs */
836 case SIOCDLIFADDR: /* delete IF addr */
837 case SIOCINITIFADDR:
838 case SIOCSDRVSPEC: /* set driver-specific parameters */
839 case SIOCSIFADDR: /* set ifnet address */
840 case SIOCSIFBRDADDR: /* set broadcast addr */
841 case SIOCSIFDSTADDR: /* set p-p address */
842 case SIOCSIFGENERIC: /* generic IF set op */
843 case SIOCSIFMEDIA: /* set net media */
844 case SIOCSIFMETRIC: /* set IF metric */
845 case SIOCSIFMTU: /* set ifnet mtu */
846 case SIOCSIFNETMASK: /* set net addr mask */
847 case SIOCSIFPHYADDR: /* set gif addres */
848 case SIOCSLIFPHYADDR: /* set gif addrs */
849 case SIOCSVH: /* set carp param */
850 error = EBUSY;
851 break;
852 case SIOCSIFCAP: /* XXX */
853 case SIOCSIFFLAGS: /* XXX */
854 default:
855 error = agrport_ioctl(port, cmd, arg);
856 break;
857 }
858 return error;
859 }
860
861 static int
862 agrreq_copyin(const void *ubuf, struct agrreq *ar)
863 {
864 int error;
865
866 error = copyin(ubuf, ar, sizeof(*ar));
867 if (error) {
868 return error;
869 }
870
871 if (ar->ar_version != AGRREQ_VERSION) {
872 return EINVAL;
873 }
874
875 return 0;
876 }
877
878 static int
879 agrreq_copyout(void *ubuf, struct agrreq *ar)
880 {
881 int error;
882
883 KASSERT(ar->ar_version == AGRREQ_VERSION);
884
885 error = copyout(ar, ubuf, sizeof(*ar));
886 if (error) {
887 return error;
888 }
889
890 return 0;
891 }
892
893 /* Make sure that if any interrupt handlers are out of the softc. */
894 static void
895 agr_sync(void)
896 {
897 uint64_t h;
898
899 if (!mp_online)
900 return;
901
902 h = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL);
903 xc_wait(h);
904 }
905
906 static int
907 agr_pause(struct agr_softc *sc)
908 {
909 int error;
910
911 mutex_enter(&sc->sc_entry_mtx);
912 if ((error = sc->sc_noentry) != 0)
913 goto out;
914
915 sc->sc_noentry = EBUSY;
916
917 while (sc->sc_insc != 0)
918 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx);
919
920 if (sc->sc_nports == 0) {
921 sc->sc_noentry = ENXIO;
922 } else {
923 sc->sc_noentry = 0;
924 error = EBUSY;
925 }
926 cv_broadcast(&sc->sc_insc_cv);
927 out:
928 mutex_exit(&sc->sc_entry_mtx);
929 return error;
930 }
931
932 static void
933 agr_evacuate(struct agr_softc *sc)
934 {
935 mutex_enter(&sc->sc_entry_mtx);
936 cv_broadcast(&sc->sc_insc_cv);
937 while (sc->sc_insc != 0 || sc->sc_paused != 0)
938 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx);
939 mutex_exit(&sc->sc_entry_mtx);
940
941 agr_sync();
942 }
943
944 static int
945 agr_enter(struct agr_softc *sc)
946 {
947 int error;
948
949 mutex_enter(&sc->sc_entry_mtx);
950 sc->sc_paused++;
951 while ((error = sc->sc_noentry) == EBUSY)
952 cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx);
953 sc->sc_paused--;
954 if (error == 0)
955 sc->sc_insc++;
956 mutex_exit(&sc->sc_entry_mtx);
957
958 return error;
959 }
960
961 static void
962 agr_exit(struct agr_softc *sc)
963 {
964 mutex_enter(&sc->sc_entry_mtx);
965 if (--sc->sc_insc == 0)
966 cv_signal(&sc->sc_insc_cv);
967 mutex_exit(&sc->sc_entry_mtx);
968 }
969
970 static bool
971 agr_ports_enter(struct agr_softc *sc)
972 {
973 mutex_enter(&sc->sc_entry_mtx);
974 while (sc->sc_wrports)
975 cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx);
976 sc->sc_rdports++;
977 mutex_exit(&sc->sc_entry_mtx);
978
979 return true;
980 }
981
982 static void
983 agr_ports_exit(struct agr_softc *sc)
984 {
985 mutex_enter(&sc->sc_entry_mtx);
986 if (--sc->sc_rdports == 0)
987 cv_signal(&sc->sc_ports_cv);
988 mutex_exit(&sc->sc_entry_mtx);
989 }
990
991 static void
992 agr_ports_lock(struct agr_softc *sc)
993 {
994 mutex_enter(&sc->sc_entry_mtx);
995 while (sc->sc_rdports != 0)
996 cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx);
997 sc->sc_wrports = true;
998 mutex_exit(&sc->sc_entry_mtx);
999 }
1000
1001 static void
1002 agr_ports_unlock(struct agr_softc *sc)
1003 {
1004 mutex_enter(&sc->sc_entry_mtx);
1005 sc->sc_wrports = false;
1006 cv_signal(&sc->sc_ports_cv);
1007 mutex_exit(&sc->sc_entry_mtx);
1008 }
1009
1010 static int
1011 agr_ioctl(struct ifnet *ifp, const u_long cmd, void *data)
1012 {
1013 struct agr_softc *sc = ifp->if_softc;
1014 struct ifreq *ifr = (struct ifreq *)data;
1015 struct ifaddr *ifa = (struct ifaddr *)data;
1016 struct agrreq ar;
1017 int error;
1018 bool in_ports = false;
1019 int s;
1020
1021 if ((error = agr_enter(sc)) != 0)
1022 return error;
1023
1024 s = splnet();
1025
1026 switch (cmd) {
1027 case SIOCINITIFADDR:
1028 in_ports = agr_ports_enter(sc);
1029 if (sc->sc_nports == 0) {
1030 error = EINVAL;
1031 break;
1032 }
1033 ifp->if_flags |= IFF_UP;
1034 switch (ifa->ifa_addr->sa_family) {
1035 #if defined(INET)
1036 case AF_INET:
1037 arp_ifinit(ifp, ifa);
1038 break;
1039 #endif
1040 default:
1041 break;
1042 }
1043 break;
1044
1045 #if 0 /* notyet */
1046 case SIOCSIFMTU:
1047 #endif
1048
1049 case SIOCSIFFLAGS:
1050 /*
1051 * Check for a change in vlan status. This ioctl is the
1052 * only way we can tell that a vlan has attached or detached.
1053 * Note the agr interface must be up.
1054 */
1055 agr_vlan_check(ifp, sc);
1056
1057 if ((error = ifioctl_common(ifp, cmd, data)) != 0)
1058 break;
1059 agr_config_promisc(sc);
1060 break;
1061
1062 case SIOCSETAGR:
1063 splx(s);
1064 error = kauth_authorize_network(kauth_cred_get(),
1065 KAUTH_NETWORK_INTERFACE,
1066 KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd,
1067 NULL);
1068 if (!error) {
1069 error = agrreq_copyin(ifr->ifr_data, &ar);
1070 }
1071 if (!error) {
1072 error = agr_setconfig(sc, &ar);
1073 }
1074 s = splnet();
1075 break;
1076
1077 case SIOCGETAGR:
1078 splx(s);
1079 error = agrreq_copyin(ifr->ifr_data, &ar);
1080 if (!error) {
1081 error = agr_getconfig(sc, &ar);
1082 }
1083 if (!error) {
1084 error = agrreq_copyout(ifr->ifr_data, &ar);
1085 }
1086 s = splnet();
1087 break;
1088
1089 case SIOCADDMULTI:
1090 case SIOCDELMULTI:
1091 in_ports = agr_ports_enter(sc);
1092 if (sc->sc_nports == 0)
1093 error = EINVAL;
1094 else
1095 error = agr_ioctl_multi(ifp, cmd, ifr);
1096 break;
1097
1098 default:
1099 error = ifioctl_common(ifp, cmd, data);
1100 break;
1101 }
1102
1103 if (in_ports)
1104 agr_ports_exit(sc);
1105
1106 splx(s);
1107
1108 agr_exit(sc);
1109
1110 return error;
1111 }
1112
1113 static int
1114 agr_config_promisc(struct agr_softc *sc)
1115 {
1116 int error;
1117
1118 agr_port_foreach(sc, agrport_config_promisc_callback, &error);
1119
1120 return error;
1121 }
1122
1123 static int
1124 agrport_config_promisc_callback(struct agr_port *port, void *arg)
1125 {
1126 struct agr_softc *sc = AGR_SC_FROM_PORT(port);
1127 int *errorp = arg;
1128 int error;
1129 bool promisc;
1130
1131 promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0;
1132
1133 error = agrport_config_promisc(port, promisc);
1134 if (error) {
1135 *errorp = error;
1136 }
1137
1138 return 0;
1139 }
1140
1141 static int
1142 agrport_config_promisc(struct agr_port *port, bool promisc)
1143 {
1144 int error;
1145
1146 if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) ||
1147 (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) {
1148 return 0;
1149 }
1150
1151 error = ifpromisc(port->port_ifp, promisc);
1152 if (error == 0) {
1153 if (promisc) {
1154 port->port_flags |= AGRPORT_PROMISC;
1155 } else {
1156 port->port_flags &= ~AGRPORT_PROMISC;
1157 }
1158 }
1159
1160 return error;
1161 }
1162
1163 /*
1164 * Module infrastructure
1165 */
1166 #include <net/if_module.h>
1167
1168 IF_MODULE(MODULE_CLASS_DRIVER, agr, NULL)
1169