xref: /netbsd-src/sys/net/agr/if_agr.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /*	$NetBSD: if_agr.c,v 1.3 2005/12/11 12:24:54 christos 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.3 2005/12/11 12:24:54 christos Exp $");
31 
32 #include "bpfilter.h"
33 #include "opt_inet.h"
34 
35 #include <sys/param.h>
36 #include <sys/callout.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
41 #include <sys/queue.h>
42 #include <sys/sockio.h>
43 #include <sys/proc.h>	/* XXX for curproc */
44 
45 #if NBPFILTER > 0
46 #include <net/bpf.h>
47 #endif
48 #include <net/if.h>
49 #include <net/if_dl.h>
50 #include <net/if_types.h>
51 
52 #if defined(INET)
53 #include <netinet/in.h>
54 #include <netinet/if_inarp.h>
55 #endif
56 
57 #include <net/agr/if_agrvar.h>
58 #include <net/agr/if_agrvar_impl.h>
59 #include <net/agr/if_agrioctl.h>
60 #include <net/agr/if_agrsubr.h>
61 #include <net/agr/if_agrethervar.h>
62 
63 void agrattach(int);
64 
65 static int agr_clone_create(struct if_clone *, int);
66 static int agr_clone_destroy(struct ifnet *);
67 static void agr_start(struct ifnet *);
68 static int agr_setconfig(struct ifnet *, const struct agrreq *);
69 static int agr_getconfig(struct ifnet *, struct agrreq *);
70 static int agr_getportlist(struct ifnet *, struct agrreq *);
71 static int agr_addport(struct ifnet *, struct ifnet *);
72 static int agr_remport(struct ifnet *, struct ifnet *);
73 static int agrreq_copyin(const void *, struct agrreq *);
74 static int agrreq_copyout(void *, struct agrreq *);
75 static int agr_ioctl(struct ifnet *, u_long, caddr_t);
76 static struct agr_port *agr_select_tx_port(struct agr_softc *, struct mbuf *);
77 static int agr_ioctl_filter(struct ifnet *, u_long, caddr_t);
78 static void agr_reset_iftype(struct ifnet *);
79 static int agr_config_promisc(struct agr_softc *);
80 static int agrport_config_promisc_callback(struct agr_port *, void *);
81 static int agrport_config_promisc(struct agr_port *, boolean_t);
82 static int agrport_cleanup(struct agr_softc *, struct agr_port *);
83 
84 static struct if_clone agr_cloner =
85     IF_CLONE_INITIALIZER("agr", agr_clone_create, agr_clone_destroy);
86 
87 /*
88  * EXPORTED FUNCTIONS
89  */
90 
91 /*
92  * agrattch: device attach routine.
93  */
94 
95 void
96 agrattach(int count)
97 {
98 
99 	if_clone_attach(&agr_cloner);
100 }
101 
102 /*
103  * agr_input: frame collector.
104  */
105 
106 void
107 agr_input(struct ifnet *ifp_port, struct mbuf *m)
108 {
109 	struct agr_port *port;
110 	struct ifnet *ifp;
111 
112 	port = ifp_port->if_agrprivate;
113 	KASSERT(port);
114 	ifp = port->port_agrifp;
115 	if ((port->port_flags & AGRPORT_COLLECTING) == 0) {
116 		m_freem(m);
117 		ifp->if_ierrors++;
118 		return;
119 	}
120 
121 	ifp->if_ipackets++;
122 	m->m_pkthdr.rcvif = ifp;
123 
124 #if NBPFILTER > 0
125 	if (ifp->if_bpf) {
126 		bpf_mtap(ifp->if_bpf, m);
127 	}
128 #endif
129 
130 	(*ifp->if_input)(ifp, m);
131 }
132 
133 /*
134  * EXPORTED AGR-INTERNAL FUNCTIONS
135  */
136 
137 int
138 agr_lock(struct agr_softc *sc)
139 {
140 	int s;
141 
142 	s = splnet();
143 	simple_lock(&sc->sc_lock);
144 
145 	return s;
146 }
147 
148 void
149 agr_unlock(struct agr_softc *sc, int savedipl)
150 {
151 
152 	simple_unlock(&sc->sc_lock);
153 	splx(savedipl);
154 }
155 
156 void
157 agr_ioctl_lock(struct agr_softc *sc)
158 {
159 
160 	lockmgr(&sc->sc_ioctl_lock, LK_EXCLUSIVE, NULL);
161 }
162 
163 void
164 agr_ioctl_unlock(struct agr_softc *sc)
165 {
166 
167 	lockmgr(&sc->sc_ioctl_lock, LK_RELEASE, NULL);
168 }
169 
170 /*
171  * agr_xmit_frame: transmit a pre-built frame.
172  */
173 
174 int
175 agr_xmit_frame(struct ifnet *ifp_port, struct mbuf *m)
176 {
177 	int error;
178 
179 	struct sockaddr_storage dst0;
180 	struct sockaddr *dst;
181 	int hdrlen;
182 
183 	/*
184 	 * trim off link level header and let if_output re-add it.
185 	 * XXX better to introduce an API to transmit pre-built frames.
186 	 */
187 
188 	hdrlen = ifp_port->if_hdrlen;
189 	if (m->m_pkthdr.len < hdrlen) {
190 		m_freem(m);
191 		return EINVAL;
192 	}
193 	memset(&dst0, 0, sizeof(dst0));
194 	dst = (struct sockaddr *)&dst0;
195 	dst->sa_family = pseudo_AF_HDRCMPLT;
196 	dst->sa_len = hdrlen;
197 	m_copydata(m, 0, hdrlen, &dst->sa_data);
198 	m_adj(m, hdrlen);
199 
200 	error = (*ifp_port->if_output)(ifp_port, m, dst, NULL);
201 
202 	return error;
203 }
204 
205 int
206 agrport_ioctl(struct agr_port *port, u_long cmd, caddr_t arg)
207 {
208 	struct ifnet *ifp = port->port_ifp;
209 
210 	KASSERT(ifp->if_agrprivate == (void *)port);
211 	KASSERT(ifp->if_ioctl == agr_ioctl_filter);
212 
213 	return (*port->port_ioctl)(ifp, cmd, arg);
214 }
215 
216 /*
217  * INTERNAL FUNCTIONS
218  */
219 
220 static int
221 agr_clone_create(struct if_clone *ifc, int unit)
222 {
223 	struct agr_softc *sc;
224 	struct ifnet *ifp;
225 
226 	sc = agr_alloc_softc();
227 	TAILQ_INIT(&sc->sc_ports);
228 	lockinit(&sc->sc_ioctl_lock, PSOCK, "agrioctl", 0, 0);
229 	simple_lock_init(&sc->sc_lock);
230 	agrtimer_init(sc);
231 	ifp = &sc->sc_if;
232 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
233 	    ifc->ifc_name, unit);
234 
235 	ifp->if_softc = sc;
236 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
237 	ifp->if_start = agr_start;
238 	ifp->if_ioctl = agr_ioctl;
239 	IFQ_SET_READY(&ifp->if_snd);
240 
241 	if_attach(ifp);
242 
243 	agr_reset_iftype(ifp);
244 
245 	return 0;
246 }
247 
248 static void
249 agr_reset_iftype(struct ifnet *ifp)
250 {
251 
252 	ifp->if_type = IFT_OTHER;
253 	ifp->if_dlt = DLT_NULL;
254 	ifp->if_addrlen = 0;
255 	if_alloc_sadl(ifp);
256 }
257 
258 static int
259 agr_clone_destroy(struct ifnet *ifp)
260 {
261 	struct agr_softc *sc = ifp->if_softc;
262 	int error;
263 	int s;
264 
265 	agr_ioctl_lock(sc);
266 
267 	s = AGR_LOCK(sc);
268 	if (sc->sc_nports > 0) {
269 		error = EBUSY;
270 	} else {
271 		error = 0;
272 	}
273 	AGR_UNLOCK(sc, s);
274 
275 	agr_ioctl_unlock(sc);
276 
277 	if (error == 0) {
278 		if_detach(ifp);
279 		agr_free_softc(sc);
280 	}
281 
282 	return error;
283 }
284 
285 static struct agr_port *
286 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m)
287 {
288 
289 	return (*sc->sc_iftop->iftop_select_tx_port)(sc, m);
290 }
291 
292 #if 0 /* "generic" version */
293 static struct agr_port *
294 agr_select_tx_port(struct agr_softc *sc, struct mbuf *m)
295 {
296 	struct agr_port *port;
297 	uint32_t hash;
298 
299 	hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m);
300 	if (sc->sc_nports == 0)
301 		return NULL;
302 	hash %= sc->sc_nports;
303 	port = TAILQ_FIRST(&sc->sc_ports);
304 	KASSERT(port != NULL);
305 	while (hash--) {
306 		port = TAILQ_NEXT(port, port_q);
307 		KASSERT(port != NULL);
308 	}
309 
310 	return port;
311 }
312 #endif /* 0 */
313 
314 static void
315 agr_start(struct ifnet *ifp)
316 {
317 	struct agr_softc *sc = ifp->if_softc;
318 	struct mbuf *m;
319 	int s;
320 
321 	s = AGR_LOCK(sc);
322 
323 	while (/* CONSTCOND */ 1) {
324 		struct agr_port *port;
325 
326 		IFQ_DEQUEUE(&ifp->if_snd, m);
327 		if (m == NULL) {
328 			break;
329 		}
330 #if NBPFILTER > 0
331 		if (ifp->if_bpf) {
332 			bpf_mtap(ifp->if_bpf, m);
333 		}
334 #endif
335 		port = agr_select_tx_port(sc, m);
336 		if (port) {
337 			int error;
338 
339 			error = agr_xmit_frame(port->port_ifp, m);
340 			if (error) {
341 				ifp->if_oerrors++;
342 			} else {
343 				ifp->if_opackets++;
344 			}
345 		} else {
346 			m_freem(m);
347 			ifp->if_oerrors++;
348 		}
349 	}
350 
351 	AGR_UNLOCK(sc, s);
352 
353 	ifp->if_flags &= ~IFF_OACTIVE;
354 }
355 
356 static int
357 agr_setconfig(struct ifnet *ifp, const struct agrreq *ar)
358 {
359 	int cmd = ar->ar_cmd;
360 	struct ifnet *ifp_port;
361 	int error = 0;
362 	char ifname[IFNAMSIZ];
363 
364 	error = copyin(ar->ar_buf, ifname, MIN(ar->ar_buflen, sizeof(ifname)));
365 	if (error) {
366 		return error;
367 	}
368 	ifp_port = ifunit(ifname);
369 	if (ifp_port == NULL) {
370 		return ENOENT;
371 	}
372 
373 	switch (cmd) {
374 	case AGRCMD_ADDPORT:
375 		error = agr_addport(ifp, ifp_port);
376 		break;
377 
378 	case AGRCMD_REMPORT:
379 		error = agr_remport(ifp, ifp_port);
380 		break;
381 
382 	default:
383 		error = EINVAL;
384 		break;
385 	}
386 
387 	return error;
388 }
389 
390 static int
391 agr_getportlist(struct ifnet *ifp, struct agrreq *ar)
392 {
393 	struct agr_softc *sc = ifp->if_softc;
394 	struct agr_port *port;
395 	struct agrportlist apl;
396 	struct agrportinfo api;
397 	char *cp = ar->ar_buf;
398 	size_t bufleft = (cp == NULL) ? 0 : ar->ar_buflen;
399 	int error;
400 
401 	if (cp != NULL) {
402 		memset(&apl, 0, sizeof(apl));
403 		memset(&api, 0, sizeof(api));
404 
405 		if (bufleft < sizeof(apl)) {
406 			return E2BIG;
407 		}
408 		apl.apl_nports = sc->sc_nports;
409 		error = copyout(&apl, cp, sizeof(apl));
410 		if (error) {
411 			return error;
412 		}
413 		cp += sizeof(apl);
414 	}
415 	bufleft -= sizeof(apl);
416 
417 	TAILQ_FOREACH(port, &sc->sc_ports, port_q) {
418 		if (cp != NULL) {
419 			if (bufleft < sizeof(api)) {
420 				return E2BIG;
421 			}
422 			memcpy(api.api_ifname, port->port_ifp->if_xname,
423 			    sizeof(api.api_ifname));
424 			api.api_flags = 0;
425 			if (port->port_flags & AGRPORT_COLLECTING) {
426 				api.api_flags |= AGRPORTINFO_COLLECTING;
427 			}
428 			if (port->port_flags & AGRPORT_DISTRIBUTING) {
429 				api.api_flags |= AGRPORTINFO_DISTRIBUTING;
430 			}
431 			error = copyout(&api, cp, sizeof(api));
432 			if (error) {
433 				return error;
434 			}
435 			cp += sizeof(api);
436 		}
437 		bufleft -= sizeof(api);
438 	}
439 
440 	if (cp == NULL) {
441 		ar->ar_buflen = -bufleft; /* necessary buffer size */
442 	}
443 
444 	return 0;
445 }
446 
447 static int
448 agr_getconfig(struct ifnet *ifp, struct agrreq *ar)
449 {
450 	int cmd = ar->ar_cmd;
451 	int error;
452 
453 	switch (cmd) {
454 	case AGRCMD_PORTLIST:
455 		error = agr_getportlist(ifp, ar);
456 		break;
457 
458 	default:
459 		error = EINVAL;
460 		break;
461 	}
462 
463 	return error;
464 }
465 
466 static int
467 agr_addport(struct ifnet *ifp, struct ifnet *ifp_port)
468 {
469 	struct agr_softc *sc = ifp->if_softc;
470 	struct agr_port *port = NULL;
471 	int error = 0;
472 	int s;
473 
474 	if (ifp_port->if_ioctl == NULL) {
475 		error = EOPNOTSUPP;
476 		goto out;
477 	}
478 
479 	if (ifp_port->if_agrprivate) {
480 		error = EBUSY;
481 		goto out;
482 	}
483 
484 	if (ifp_port->if_start == agr_start) {
485 		error = EINVAL;
486 		goto out;
487 	}
488 
489 	port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF,
490 	    M_WAITOK | M_ZERO);
491 	if (port == NULL) {
492 		error = ENOMEM;
493 		goto out;
494 	}
495 	port->port_flags = AGRPORT_LARVAL;
496 
497 	if (TAILQ_NEXT(TAILQ_FIRST(&ifp_port->if_addrlist), ifa_list) != NULL) {
498 		error = EBUSY;
499 		goto out;
500 	}
501 
502 	if (sc->sc_nports == 0) {
503 		switch (ifp_port->if_type) {
504 		case IFT_ETHER:
505 			sc->sc_iftop = &agrether_ops;
506 			break;
507 
508 		default:
509 			error = EPROTONOSUPPORT; /* XXX */
510 			goto out;
511 		}
512 
513 		error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port);
514 		if (error)
515 			goto out;
516 		agrtimer_start(sc);
517 	} else {
518 		if (ifp->if_type != ifp_port->if_type) {
519 			error = EINVAL;
520 			goto out;
521 		}
522 		if (ifp->if_addrlen != ifp_port->if_addrlen) {
523 			error = EINVAL;
524 			goto out;
525 		}
526 	}
527 
528 	memcpy(port->port_origlladdr, LLADDR(ifp_port->if_sadl),
529 	    ifp_port->if_addrlen);
530 
531 	/*
532 	 * start to modify ifp_port.
533 	 */
534 
535 	error = (*ifp_port->if_ioctl)(ifp_port, SIOCSIFADDR,
536 	    (caddr_t)TAILQ_FIRST(&ifp->if_addrlist));
537 
538 	if (error) {
539 		printf("%s: SIOCSIFADDR error %d\n", __func__, error);
540 		goto cleanup;
541 	}
542 	port->port_flags |= AGRPORT_LADDRCHANGED;
543 
544 	ifp->if_type = ifp_port->if_type;
545 	s = AGR_LOCK(sc);
546 
547 	port->port_ifp = ifp_port;
548 	ifp_port->if_agrprivate = port;
549 	port->port_agrifp = ifp;
550 	TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q);
551 	sc->sc_nports++;
552 
553 	port->port_ioctl = ifp_port->if_ioctl;
554 	ifp_port->if_ioctl = agr_ioctl_filter;
555 
556 	port->port_flags |= AGRPORT_ATTACHED;
557 
558 	AGR_UNLOCK(sc, s);
559 
560 	error = (*sc->sc_iftop->iftop_portinit)(sc, port);
561 	if (error) {
562 		printf("%s: portinit error %d\n", __func__, error);
563 		goto cleanup;
564 	}
565 
566 	ifp->if_flags |= IFF_RUNNING;
567 
568 	agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0);
569 	error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, TRUE);
570 	if (error) {
571 		printf("%s: configmulti error %d\n", __func__, error);
572 		goto cleanup;
573 	}
574 
575 	s = AGR_LOCK(sc);
576 	port->port_flags &= ~AGRPORT_LARVAL;
577 	AGR_UNLOCK(sc, s);
578 out:
579 	if (error && port) {
580 		free(port, M_DEVBUF);
581 	}
582 	return error;
583 
584 cleanup:
585 	if (agrport_cleanup(sc, port)) {
586 		printf("%s: error on cleanup\n", __func__);
587 
588 		port = NULL; /* XXX */
589 	}
590 
591 	if (sc->sc_nports == 0) {
592 		KASSERT(TAILQ_EMPTY(&sc->sc_ports));
593 		agrtimer_stop(sc);
594 		(*sc->sc_iftop->iftop_dtor)(sc);
595 		sc->sc_iftop = NULL;
596 		agr_reset_iftype(ifp);
597 	} else {
598 		KASSERT(!TAILQ_EMPTY(&sc->sc_ports));
599 	}
600 
601 	goto out;
602 }
603 
604 static int
605 agr_remport(struct ifnet *ifp, struct ifnet *ifp_port)
606 {
607 	struct agr_softc *sc = ifp->if_softc;
608 	struct agr_port *port;
609 	int error = 0;
610 	int s;
611 
612 	if (ifp_port->if_agrprivate == NULL) {
613 		error = ENOENT;
614 		return error;
615 	}
616 
617 	port = ifp_port->if_agrprivate;
618 	if (port->port_agrifp != ifp) {
619 		error = EINVAL;
620 		return error;
621 	}
622 
623 	KASSERT(sc->sc_nports > 0);
624 
625 #if 0
626 	if (sc->sc_nports == 1 &&
627 	    TAILQ_NEXT(TAILQ_FIRST(&ifp->if_addrlist), ifa_list) != NULL) {
628 		error = EBUSY;
629 		return error;
630 	}
631 #endif
632 
633 	s = AGR_LOCK(sc);
634 	port->port_flags |= AGRPORT_DETACHING;
635 	AGR_UNLOCK(sc, s);
636 
637 	error = (*sc->sc_iftop->iftop_portfini)(sc, port);
638 	if (error) {
639 		/* XXX XXX */
640 		printf("%s: portfini error %d\n", __func__, error);
641 		goto out;
642 	}
643 
644 	error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, FALSE);
645 	if (error) {
646 		/* XXX XXX */
647 		printf("%s: configmulti_port error %d\n", __func__, error);
648 		goto out;
649 	}
650 
651 	error = agrport_cleanup(sc, port);
652 	if (error) {
653 		/* XXX XXX */
654 		printf("%s: agrport_cleanup error %d\n", __func__, error);
655 		goto out;
656 	}
657 
658 	free(port, M_DEVBUF);
659 
660 out:
661 	if (sc->sc_nports == 0) {
662 		KASSERT(TAILQ_EMPTY(&sc->sc_ports));
663 		agrtimer_stop(sc);
664 		(*sc->sc_iftop->iftop_dtor)(sc);
665 		sc->sc_iftop = NULL;
666 		/* XXX should purge all addresses? */
667 		agr_reset_iftype(ifp);
668 	} else {
669 		KASSERT(!TAILQ_EMPTY(&sc->sc_ports));
670 	}
671 
672 	return error;
673 }
674 
675 static int
676 agrport_cleanup(struct agr_softc *sc, struct agr_port *port)
677 {
678 	struct ifnet *ifp_port = port->port_ifp;
679 	int error;
680 	int result = 0;
681 	int s;
682 
683 	error = agrport_config_promisc(port, FALSE);
684 	if (error) {
685 		printf("%s: config_promisc error %d\n", __func__, error);
686 		result = error;
687 	}
688 
689 	if ((port->port_flags & AGRPORT_LADDRCHANGED)) {
690 #if 0
691 		memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr,
692 		    ifp_port->if_addrlen);
693 		if (ifp_port->if_init != NULL) {
694 			error = (*ifp_port->if_init)(ifp_port);
695 		}
696 #else
697 		struct sockaddr_dl *sdl;
698 		struct ifaddr ifa;
699 		int sdllen;
700 		int addrlen;
701 
702 		addrlen = ifp_port->if_addrlen;
703 		sdllen = sizeof(*sdl) - sizeof(sdl->sdl_data) + addrlen;
704 		sdl = malloc(sdllen, M_TEMP, M_WAITOK);
705 		if (sdl == NULL) {
706 			error = ENOMEM;
707 		} else {
708 			memset(sdl, 0, sdllen);
709 			sdl->sdl_len = sdllen;
710 			sdl->sdl_family = AF_LINK;
711 			sdl->sdl_type = ifp_port->if_type;
712 			sdl->sdl_alen = addrlen;
713 			memcpy(LLADDR(sdl), port->port_origlladdr, addrlen);
714 			memset(&ifa, 0, sizeof(ifa));
715 			ifa.ifa_addr = (struct sockaddr *)sdl;
716 			error = agrport_ioctl(port, SIOCSIFADDR, (caddr_t)&ifa);
717 			free(sdl, M_TEMP);
718 		}
719 #endif
720 		if (error) {
721 			printf("%s: if_init error %d\n", __func__, error);
722 			result = error;
723 		} else {
724 			port->port_flags &= ~AGRPORT_LADDRCHANGED;
725 		}
726 	}
727 
728 	s = AGR_LOCK(sc);
729 	if ((port->port_flags & AGRPORT_ATTACHED)) {
730 		ifp_port->if_agrprivate = NULL;
731 
732 		TAILQ_REMOVE(&sc->sc_ports, port, port_q);
733 		sc->sc_nports--;
734 
735 		KASSERT(ifp_port->if_ioctl == agr_ioctl_filter);
736 		ifp_port->if_ioctl = port->port_ioctl;
737 
738 		port->port_flags &= ~AGRPORT_ATTACHED;
739 	}
740 	AGR_UNLOCK(sc, s);
741 
742 	return result;
743 }
744 
745 static int
746 agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr)
747 {
748 	struct agr_softc *sc = ifp->if_softc;
749 	int error;
750 
751 	error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr,
752 	    (cmd == SIOCADDMULTI));
753 
754 	return error;
755 }
756 
757 /* XXX an incomplete hack; can't filter ioctls handled ifioctl(). */
758 static int
759 agr_ioctl_filter(struct ifnet *ifp, u_long cmd, caddr_t arg)
760 {
761 	struct agr_port *port = ifp->if_agrprivate;
762 	int error;
763 
764 	KASSERT(port);
765 
766 	switch (cmd) {
767 	case SIOCGIFADDR:
768 	case SIOCGIFMEDIA:
769 	case SIOCSIFFLAGS: /* XXX */
770 		error = agrport_ioctl(port, cmd, arg);
771 		break;
772 	default:
773 		error = EBUSY;
774 		break;
775 	}
776 	return error;
777 }
778 
779 static int
780 agrreq_copyin(const void *ubuf, struct agrreq *ar)
781 {
782 	int error;
783 
784 	error = copyin(ubuf, ar, sizeof(*ar));
785 	if (error) {
786 		return error;
787 	}
788 
789 	if (ar->ar_version != AGRREQ_VERSION) {
790 		return EINVAL;
791 	}
792 
793 	return 0;
794 }
795 
796 static int
797 agrreq_copyout(void *ubuf, struct agrreq *ar)
798 {
799 	int error;
800 
801 	KASSERT(ar->ar_version == AGRREQ_VERSION);
802 
803 	error = copyout(ar, ubuf, sizeof(*ar));
804 	if (error) {
805 		return error;
806 	}
807 
808 	return 0;
809 }
810 
811 static int
812 agr_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
813 {
814 	struct agr_softc *sc = ifp->if_softc;
815 	struct ifreq *ifr = (struct ifreq *)data;
816 	struct ifaddr *ifa = (struct ifaddr *)data;
817 	struct sockaddr *sa;
818 	struct agrreq ar;
819 	struct proc *p;
820 	int error = 0;
821 	int s;
822 
823 	agr_ioctl_lock(sc);
824 
825 	s = splnet();
826 
827 	switch (cmd) {
828 	case SIOCSIFADDR:
829 		if (sc->sc_nports == 0) {
830 			error = EINVAL;
831 			break;
832 		}
833 		ifp->if_flags |= IFF_UP;
834 		switch (ifa->ifa_addr->sa_family) {
835 #if defined(INET)
836 		case AF_INET:
837 			arp_ifinit(ifp, ifa);
838 			break;
839 #endif
840 		default:
841 			break;
842 		}
843 		break;
844 
845 	case SIOCGIFADDR:
846 		sa = (struct sockaddr *)&ifr->ifr_data;
847 		memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ifp->if_addrlen);
848 		break;
849 
850 #if 0 /* notyet */
851 	case SIOCSIFMTU:
852 #endif
853 
854 	case SIOCSIFFLAGS:
855 		agr_config_promisc(sc);
856 		break;
857 
858 	case SIOCSETAGR:
859 		splx(s);
860 		p = curproc; /* XXX */
861 		error = suser(p->p_ucred, &p->p_acflag);
862 		if (!error) {
863 			error = agrreq_copyin(ifr->ifr_data, &ar);
864 		}
865 		if (!error) {
866 			error = agr_setconfig(ifp, &ar);
867 		}
868 		s = splnet();
869 		break;
870 
871 	case SIOCGETAGR:
872 		splx(s);
873 		error = agrreq_copyin(ifr->ifr_data, &ar);
874 		if (!error) {
875 			error = agr_getconfig(ifp, &ar);
876 		}
877 		if (!error) {
878 			error = agrreq_copyout(ifr->ifr_data, &ar);
879 		}
880 		s = splnet();
881 		break;
882 
883 	case SIOCADDMULTI:
884 	case SIOCDELMULTI:
885 		if (sc->sc_nports == 0) {
886 			error = EINVAL;
887 			break;
888 		}
889 		error = agr_ioctl_multi(ifp, cmd, ifr);
890 		break;
891 
892 	default:
893 		error = EINVAL;
894 		break;
895 	}
896 
897 	splx(s);
898 
899 	agr_ioctl_unlock(sc);
900 
901 	return error;
902 }
903 
904 static int
905 agr_config_promisc(struct agr_softc *sc)
906 {
907 	int error;
908 
909 	agr_port_foreach(sc, agrport_config_promisc_callback, &error);
910 
911 	return error;
912 }
913 
914 static int
915 agrport_config_promisc_callback(struct agr_port *port, void *arg)
916 {
917 	struct agr_softc *sc = AGR_SC_FROM_PORT(port);
918 	int *errorp = arg;
919 	int error;
920 	boolean_t promisc;
921 
922 	promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0;
923 
924 	error = agrport_config_promisc(port, promisc);
925 	if (error) {
926 		*errorp = error;
927 	}
928 
929 	return 0;
930 }
931 
932 static int
933 agrport_config_promisc(struct agr_port *port, boolean_t promisc)
934 {
935 	int error;
936 
937 	if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) ||
938 	    (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) {
939 		return 0;
940 	}
941 
942 	error = ifpromisc(port->port_ifp, promisc);
943 	if (error == 0) {
944 		if (promisc) {
945 			port->port_flags |= AGRPORT_PROMISC;
946 		} else {
947 			port->port_flags &= ~AGRPORT_PROMISC;
948 		}
949 	}
950 
951 	return error;
952 }
953