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