xref: /openbsd-src/sys/net/if_trunk.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: if_trunk.c,v 1.89 2014/07/12 18:44:22 tedu Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
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 #include "bpfilter.h"
20 #include "trunk.h"
21 
22 #include <sys/param.h>
23 #include <sys/kernel.h>
24 #include <sys/malloc.h>
25 #include <sys/mbuf.h>
26 #include <sys/queue.h>
27 #include <sys/socket.h>
28 #include <sys/sockio.h>
29 #include <sys/systm.h>
30 #include <sys/timeout.h>
31 #include <sys/hash.h>
32 
33 #include <dev/rndvar.h>
34 
35 #include <net/if.h>
36 #include <net/if_arp.h>
37 #include <net/if_dl.h>
38 #include <net/if_media.h>
39 #include <net/if_types.h>
40 #if NBPFILTER > 0
41 #include <net/bpf.h>
42 #endif
43 
44 #ifdef INET
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/if_ether.h>
48 #include <netinet/ip.h>
49 #endif
50 
51 #ifdef INET6
52 #include <netinet/ip6.h>
53 #endif
54 
55 #include <net/if_vlan_var.h>
56 #include <net/if_trunk.h>
57 #include <net/trunklacp.h>
58 
59 
60 SLIST_HEAD(__trhead, trunk_softc) trunk_list;	/* list of trunks */
61 
62 void	 trunkattach(int);
63 int	 trunk_clone_create(struct if_clone *, int);
64 int	 trunk_clone_destroy(struct ifnet *);
65 void	 trunk_lladdr(struct arpcom *, u_int8_t *);
66 int	 trunk_capabilities(struct trunk_softc *);
67 void	 trunk_port_lladdr(struct trunk_port *, u_int8_t *);
68 int	 trunk_port_create(struct trunk_softc *, struct ifnet *);
69 int	 trunk_port_destroy(struct trunk_port *);
70 void	 trunk_port_watchdog(struct ifnet *);
71 void	 trunk_port_state(void *);
72 void	 trunk_port_ifdetach(void *);
73 int	 trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
74 struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
75 int	 trunk_port_checkstacking(struct trunk_softc *);
76 void	 trunk_port2req(struct trunk_port *, struct trunk_reqport *);
77 int	 trunk_ioctl(struct ifnet *, u_long, caddr_t);
78 int	 trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
79 int	 trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
80 void	 trunk_ether_purgemulti(struct trunk_softc *);
81 int	 trunk_ether_cmdmulti(struct trunk_port *, u_long);
82 int	 trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
83 void	 trunk_start(struct ifnet *);
84 void	 trunk_init(struct ifnet *);
85 void	 trunk_stop(struct ifnet *);
86 void	 trunk_watchdog(struct ifnet *);
87 int	 trunk_media_change(struct ifnet *);
88 void	 trunk_media_status(struct ifnet *, struct ifmediareq *);
89 struct trunk_port *trunk_link_active(struct trunk_softc *,
90 	    struct trunk_port *);
91 const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *);
92 
93 struct if_clone trunk_cloner =
94     IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
95 
96 /* Simple round robin */
97 int	 trunk_rr_attach(struct trunk_softc *);
98 int	 trunk_rr_detach(struct trunk_softc *);
99 void	 trunk_rr_port_destroy(struct trunk_port *);
100 int	 trunk_rr_start(struct trunk_softc *, struct mbuf *);
101 int	 trunk_rr_input(struct trunk_softc *, struct trunk_port *,
102 	    struct ether_header *, struct mbuf *);
103 
104 /* Active failover */
105 int	 trunk_fail_attach(struct trunk_softc *);
106 int	 trunk_fail_detach(struct trunk_softc *);
107 int	 trunk_fail_start(struct trunk_softc *, struct mbuf *);
108 int	 trunk_fail_input(struct trunk_softc *, struct trunk_port *,
109 	    struct ether_header *, struct mbuf *);
110 
111 /* Loadbalancing */
112 int	 trunk_lb_attach(struct trunk_softc *);
113 int	 trunk_lb_detach(struct trunk_softc *);
114 int	 trunk_lb_port_create(struct trunk_port *);
115 void	 trunk_lb_port_destroy(struct trunk_port *);
116 int	 trunk_lb_start(struct trunk_softc *, struct mbuf *);
117 int	 trunk_lb_input(struct trunk_softc *, struct trunk_port *,
118 	    struct ether_header *, struct mbuf *);
119 int	 trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
120 
121 /* Broadcast mode */
122 int	 trunk_bcast_attach(struct trunk_softc *);
123 int	 trunk_bcast_detach(struct trunk_softc *);
124 int	 trunk_bcast_start(struct trunk_softc *, struct mbuf *);
125 int	 trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
126 	    struct ether_header *, struct mbuf *);
127 
128 /* 802.3ad LACP */
129 int	 trunk_lacp_attach(struct trunk_softc *);
130 int	 trunk_lacp_detach(struct trunk_softc *);
131 int	 trunk_lacp_start(struct trunk_softc *, struct mbuf *);
132 int	 trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
133 	    struct ether_header *, struct mbuf *);
134 
135 /* Trunk protocol table */
136 static const struct {
137 	enum trunk_proto	ti_proto;
138 	int			(*ti_attach)(struct trunk_softc *);
139 } trunk_protos[] = {
140 	{ TRUNK_PROTO_ROUNDROBIN,	trunk_rr_attach },
141 	{ TRUNK_PROTO_FAILOVER,		trunk_fail_attach },
142 	{ TRUNK_PROTO_LOADBALANCE,	trunk_lb_attach },
143 	{ TRUNK_PROTO_BROADCAST,	trunk_bcast_attach },
144 	{ TRUNK_PROTO_LACP,		trunk_lacp_attach },
145 	{ TRUNK_PROTO_NONE,		NULL }
146 };
147 
148 void
149 trunkattach(int count)
150 {
151 	SLIST_INIT(&trunk_list);
152 	if_clone_attach(&trunk_cloner);
153 }
154 
155 int
156 trunk_clone_create(struct if_clone *ifc, int unit)
157 {
158 	struct trunk_softc *tr;
159 	struct ifnet *ifp;
160 	int i, error = 0;
161 
162 	if ((tr = malloc(sizeof(struct trunk_softc),
163 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
164 		return (ENOMEM);
165 
166 	tr->tr_unit = unit;
167 	tr->tr_proto = TRUNK_PROTO_NONE;
168 	for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
169 		if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
170 			tr->tr_proto = trunk_protos[i].ti_proto;
171 			if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
172 				free(tr, M_DEVBUF, 0);
173 				return (error);
174 			}
175 			break;
176 		}
177 	}
178 	SLIST_INIT(&tr->tr_ports);
179 
180 	/* Initialise pseudo media types */
181 	ifmedia_init(&tr->tr_media, 0, trunk_media_change,
182 	    trunk_media_status);
183 	ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
184 	ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
185 
186 	ifp = &tr->tr_ac.ac_if;
187 	ifp->if_softc = tr;
188 	ifp->if_start = trunk_start;
189 	ifp->if_watchdog = trunk_watchdog;
190 	ifp->if_ioctl = trunk_ioctl;
191 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
192 	ifp->if_capabilities = trunk_capabilities(tr);
193 
194 	IFQ_SET_MAXLEN(&ifp->if_snd, 1);
195 	IFQ_SET_READY(&ifp->if_snd);
196 
197 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
198 	    ifc->ifc_name, unit);
199 
200 	/*
201 	 * Attach as an ordinary ethernet device, children will be attached
202 	 * as special device IFT_IEEE8023ADLAG.
203 	 */
204 	if_attach(ifp);
205 	ether_ifattach(ifp);
206 
207 	/* Insert into the global list of trunks */
208 	SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
209 
210 	return (0);
211 }
212 
213 int
214 trunk_clone_destroy(struct ifnet *ifp)
215 {
216 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
217 	struct trunk_port *tp;
218 	int error, s;
219 
220 	/* Remove any multicast groups that we may have joined. */
221 	trunk_ether_purgemulti(tr);
222 
223 	s = splnet();
224 
225 	/* Shutdown and remove trunk ports, return on error */
226 	while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
227 		if ((error = trunk_port_destroy(tp)) != 0) {
228 			splx(s);
229 			return (error);
230 		}
231 	}
232 
233 	ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
234 	ether_ifdetach(ifp);
235 	if_detach(ifp);
236 
237 	SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
238 	free(tr, M_DEVBUF, 0);
239 
240 	splx(s);
241 
242 	return (0);
243 }
244 
245 void
246 trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
247 {
248 	struct ifnet *ifp = &ac->ac_if;
249 	struct sockaddr_dl *sdl;
250 
251 	sdl = ifp->if_sadl;
252 	sdl->sdl_type = IFT_ETHER;
253 	sdl->sdl_alen = ETHER_ADDR_LEN;
254 	bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
255 	bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
256 }
257 
258 int
259 trunk_capabilities(struct trunk_softc *tr)
260 {
261 	struct trunk_port *tp;
262 	int cap = ~0, priv;
263 
264 	/* Preserve private capabilities */
265 	priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
266 
267 	/* Get capabilities from the trunk ports */
268 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
269 		cap &= tp->tp_capabilities;
270 
271 	if (tr->tr_ifflags & IFF_DEBUG) {
272 		printf("%s: capabilities 0x%08x\n",
273 		    tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
274 	}
275 
276 	return (cap == ~0 ? priv : (cap | priv));
277 }
278 
279 void
280 trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
281 {
282 	struct ifnet *ifp = tp->tp_if;
283 
284 	/* Set the link layer address */
285 	trunk_lladdr((struct arpcom *)ifp, lladdr);
286 
287 	/* Reset the port to update the lladdr */
288 	ifnewlladdr(ifp);
289 }
290 
291 int
292 trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
293 {
294 	struct trunk_softc *tr_ptr;
295 	struct trunk_port *tp;
296 	int error = 0;
297 
298 	/* Limit the maximal number of trunk ports */
299 	if (tr->tr_count >= TRUNK_MAX_PORTS)
300 		return (ENOSPC);
301 
302 	/* New trunk port has to be in an idle state */
303 	if (ifp->if_flags & IFF_OACTIVE)
304 		return (EBUSY);
305 
306 	/* Check if port has already been associated to a trunk */
307 	if (trunk_port_get(NULL, ifp) != NULL)
308 		return (EBUSY);
309 
310 	/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
311 	if (ifp->if_type != IFT_ETHER)
312 		return (EPROTONOSUPPORT);
313 
314 	/* Take MTU from the first member port */
315 	if (SLIST_EMPTY(&tr->tr_ports)) {
316 		if (tr->tr_ifflags & IFF_DEBUG)
317 			printf("%s: first port, setting trunk mtu %u\n",
318 			    tr->tr_ifname, ifp->if_mtu);
319 		tr->tr_ac.ac_if.if_mtu = ifp->if_mtu;
320 		tr->tr_ac.ac_if.if_hardmtu = ifp->if_mtu;
321 	} else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) {
322 		printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname,
323 		    ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu);
324 		return (EINVAL);
325 	}
326 
327 	if ((error = ifpromisc(ifp, 1)) != 0)
328 		return (error);
329 
330 	if ((tp = malloc(sizeof(struct trunk_port),
331 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
332 		return (ENOMEM);
333 
334 	/* Check if port is a stacked trunk */
335 	SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
336 		if (ifp == &tr_ptr->tr_ac.ac_if) {
337 			tp->tp_flags |= TRUNK_PORT_STACK;
338 			if (trunk_port_checkstacking(tr_ptr) >=
339 			    TRUNK_MAX_STACKING) {
340 				free(tp, M_DEVBUF, 0);
341 				return (E2BIG);
342 			}
343 		}
344 	}
345 
346 	/* Change the interface type */
347 	tp->tp_iftype = ifp->if_type;
348 	ifp->if_type = IFT_IEEE8023ADLAG;
349 	ifp->if_tp = (caddr_t)tp;
350 	tp->tp_watchdog = ifp->if_watchdog;
351 	ifp->if_watchdog = trunk_port_watchdog;
352 	tp->tp_ioctl = ifp->if_ioctl;
353 	ifp->if_ioctl = trunk_port_ioctl;
354 
355 	tp->tp_if = ifp;
356 	tp->tp_trunk = tr;
357 
358 	/* Save port link layer address */
359 	bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
360 
361 	if (SLIST_EMPTY(&tr->tr_ports)) {
362 		tr->tr_primary = tp;
363 		tp->tp_flags |= TRUNK_PORT_MASTER;
364 		trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
365 	}
366 
367 	/* Update link layer address for this port */
368 	trunk_port_lladdr(tp,
369 	    ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr);
370 
371 	/* Insert into the list of ports */
372 	SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
373 	tr->tr_count++;
374 
375 	/* Update trunk capabilities */
376 	tr->tr_capabilities = trunk_capabilities(tr);
377 
378 	/* Add multicast addresses to this port */
379 	trunk_ether_cmdmulti(tp, SIOCADDMULTI);
380 
381 	/* Register callback for physical link state changes */
382 	tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
383 	    trunk_port_state, tp);
384 
385 	/* Register callback if parent wants to unregister */
386 	tp->dh_cookie = hook_establish(ifp->if_detachhooks, 0,
387 	    trunk_port_ifdetach, tp);
388 
389 	if (tr->tr_port_create != NULL)
390 		error = (*tr->tr_port_create)(tp);
391 
392 	return (error);
393 }
394 
395 int
396 trunk_port_checkstacking(struct trunk_softc *tr)
397 {
398 	struct trunk_softc *tr_ptr;
399 	struct trunk_port *tp;
400 	int m = 0;
401 
402 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
403 		if (tp->tp_flags & TRUNK_PORT_STACK) {
404 			tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
405 			m = MAX(m, trunk_port_checkstacking(tr_ptr));
406 		}
407 	}
408 
409 	return (m + 1);
410 }
411 
412 int
413 trunk_port_destroy(struct trunk_port *tp)
414 {
415 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
416 	struct trunk_port *tp_ptr;
417 	struct ifnet *ifp = tp->tp_if;
418 
419 	if (tr->tr_port_destroy != NULL)
420 		(*tr->tr_port_destroy)(tp);
421 
422 	/* Remove multicast addresses from this port */
423 	trunk_ether_cmdmulti(tp, SIOCDELMULTI);
424 
425 	/* Port has to be down */
426 	if (ifp->if_flags & IFF_UP)
427 		if_down(ifp);
428 
429 	ifpromisc(ifp, 0);
430 
431 	/* Restore interface */
432 	ifp->if_type = tp->tp_iftype;
433 	ifp->if_watchdog = tp->tp_watchdog;
434 	ifp->if_ioctl = tp->tp_ioctl;
435 	ifp->if_tp = NULL;
436 
437 	hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie);
438 	hook_disestablish(ifp->if_detachhooks, tp->dh_cookie);
439 
440 	/* Finally, remove the port from the trunk */
441 	SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
442 	tr->tr_count--;
443 
444 	/* Update the primary interface */
445 	if (tp == tr->tr_primary) {
446 		u_int8_t lladdr[ETHER_ADDR_LEN];
447 
448 		if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
449 			bzero(&lladdr, ETHER_ADDR_LEN);
450 		} else {
451 			bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
452 			    lladdr, ETHER_ADDR_LEN);
453 			tp_ptr->tp_flags = TRUNK_PORT_MASTER;
454 		}
455 		trunk_lladdr(&tr->tr_ac, lladdr);
456 		tr->tr_primary = tp_ptr;
457 
458 		/* Update link layer address for each port */
459 		SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
460 			trunk_port_lladdr(tp_ptr, lladdr);
461 	}
462 
463 	/* Reset the port lladdr */
464 	trunk_port_lladdr(tp, tp->tp_lladdr);
465 
466 	free(tp, M_DEVBUF, 0);
467 
468 	/* Update trunk capabilities */
469 	tr->tr_capabilities = trunk_capabilities(tr);
470 
471 	return (0);
472 }
473 
474 void
475 trunk_port_watchdog(struct ifnet *ifp)
476 {
477 	struct trunk_port *tp;
478 
479 	/* Should be checked by the caller */
480 	if (ifp->if_type != IFT_IEEE8023ADLAG)
481 		return;
482 	if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
483 	    tp->tp_trunk == NULL)
484 		return;
485 
486 	if (tp->tp_watchdog != NULL)
487 		(*tp->tp_watchdog)(ifp);
488 }
489 
490 
491 int
492 trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
493 {
494 	struct trunk_reqport *rp = (struct trunk_reqport *)data;
495 	struct trunk_softc *tr;
496 	struct trunk_port *tp = NULL;
497 	int s, error = 0;
498 
499 	s = splnet();
500 
501 	/* Should be checked by the caller */
502 	if (ifp->if_type != IFT_IEEE8023ADLAG ||
503 	    (tp = (struct trunk_port *)ifp->if_tp) == NULL ||
504 	    (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
505 		error = EINVAL;
506 		goto fallback;
507 	}
508 
509 	switch (cmd) {
510 	case SIOCGTRUNKPORT:
511 		if (rp->rp_portname[0] == '\0' ||
512 		    ifunit(rp->rp_portname) != ifp) {
513 			error = EINVAL;
514 			break;
515 		}
516 
517 		/* Search in all trunks if the global flag is set */
518 		if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
519 		    NULL : tr, ifp)) == NULL) {
520 			error = ENOENT;
521 			break;
522 		}
523 
524 		trunk_port2req(tp, rp);
525 		break;
526 	case SIOCSIFMTU:
527 		/* Do not allow the MTU to be changed once joined */
528 		error = EINVAL;
529 		break;
530 	default:
531 		error = ENOTTY;
532 		goto fallback;
533 	}
534 
535 	splx(s);
536 	return (error);
537 
538  fallback:
539 	splx(s);
540 
541 	if (tp != NULL)
542 		error = (*tp->tp_ioctl)(ifp, cmd, data);
543 
544 	return (error);
545 }
546 
547 void
548 trunk_port_ifdetach(void *arg)
549 {
550 	struct trunk_port *tp = (struct trunk_port *)arg;
551 
552 	trunk_port_destroy(tp);
553 }
554 
555 struct trunk_port *
556 trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
557 {
558 	struct trunk_port *tp;
559 	struct trunk_softc *tr_ptr;
560 
561 	if (tr != NULL) {
562 		/* Search port in specified trunk */
563 		SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
564 			if (tp->tp_if == ifp)
565 				return (tp);
566 		}
567 	} else {
568 		/* Search all trunks for the selected port */
569 		SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
570 			SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
571 				if (tp->tp_if == ifp)
572 					return (tp);
573 			}
574 		}
575 	}
576 
577 	return (NULL);
578 }
579 
580 void
581 trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
582 {
583 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
584 
585 	strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
586 	strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
587 	rp->rp_prio = tp->tp_prio;
588 	if (tr->tr_portreq != NULL)
589 		(*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc);
590 
591 	/* Add protocol specific flags */
592 	switch (tr->tr_proto) {
593 	case TRUNK_PROTO_FAILOVER:
594 		rp->rp_flags = tp->tp_flags;
595 		if (tp == trunk_link_active(tr, tr->tr_primary))
596 			rp->rp_flags |= TRUNK_PORT_ACTIVE;
597 		break;
598 
599 	case TRUNK_PROTO_ROUNDROBIN:
600 	case TRUNK_PROTO_LOADBALANCE:
601 	case TRUNK_PROTO_BROADCAST:
602 		rp->rp_flags = tp->tp_flags;
603 		if (TRUNK_PORTACTIVE(tp))
604 			rp->rp_flags |= TRUNK_PORT_ACTIVE;
605 		break;
606 
607 	case TRUNK_PROTO_LACP:
608 		/* LACP has a different definition of active */
609 		rp->rp_flags = lacp_port_status(tp);
610 		break;
611 	default:
612 		break;
613 	}
614 }
615 
616 int
617 trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
618 {
619 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
620 	struct trunk_reqall *ra = (struct trunk_reqall *)data;
621 	struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
622 	struct ifreq *ifr = (struct ifreq *)data;
623 	struct ifaddr *ifa = (struct ifaddr *)data;
624 	struct trunk_port *tp;
625 	struct ifnet *tpif;
626 	int s, i, error = 0;
627 
628 	s = splnet();
629 
630 	bzero(&rpbuf, sizeof(rpbuf));
631 
632 	switch (cmd) {
633 	case SIOCGTRUNK:
634 		ra->ra_proto = tr->tr_proto;
635 		if (tr->tr_req != NULL)
636 			(*tr->tr_req)(tr, (caddr_t)&ra->ra_psc);
637 		ra->ra_ports = i = 0;
638 		tp = SLIST_FIRST(&tr->tr_ports);
639 		while (tp && ra->ra_size >=
640 		    i + sizeof(struct trunk_reqport)) {
641 			trunk_port2req(tp, &rpbuf);
642 			error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
643 			    sizeof(struct trunk_reqport));
644 			if (error)
645 				break;
646 			i += sizeof(struct trunk_reqport);
647 			ra->ra_ports++;
648 			tp = SLIST_NEXT(tp, tp_entries);
649 		}
650 		break;
651 	case SIOCSTRUNK:
652 		if ((error = suser(curproc, 0)) != 0) {
653 			error = EPERM;
654 			break;
655 		}
656 		if (ra->ra_proto >= TRUNK_PROTO_MAX) {
657 			error = EPROTONOSUPPORT;
658 			break;
659 		}
660 		if (tr->tr_proto != TRUNK_PROTO_NONE)
661 			error = tr->tr_detach(tr);
662 		if (error != 0)
663 			break;
664 		for (i = 0; i < (sizeof(trunk_protos) /
665 		    sizeof(trunk_protos[0])); i++) {
666 			if (trunk_protos[i].ti_proto == ra->ra_proto) {
667 				if (tr->tr_ifflags & IFF_DEBUG)
668 					printf("%s: using proto %u\n",
669 					    tr->tr_ifname,
670 					    trunk_protos[i].ti_proto);
671 				tr->tr_proto = trunk_protos[i].ti_proto;
672 				if (tr->tr_proto != TRUNK_PROTO_NONE)
673 					error = trunk_protos[i].ti_attach(tr);
674 				goto out;
675 			}
676 		}
677 		error = EPROTONOSUPPORT;
678 		break;
679 	case SIOCGTRUNKPORT:
680 		if (rp->rp_portname[0] == '\0' ||
681 		    (tpif = ifunit(rp->rp_portname)) == NULL) {
682 			error = EINVAL;
683 			break;
684 		}
685 
686 		/* Search in all trunks if the global flag is set */
687 		if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
688 		    NULL : tr, tpif)) == NULL) {
689 			error = ENOENT;
690 			break;
691 		}
692 
693 		trunk_port2req(tp, rp);
694 		break;
695 	case SIOCSTRUNKPORT:
696 		if ((error = suser(curproc, 0)) != 0) {
697 			error = EPERM;
698 			break;
699 		}
700 		if (rp->rp_portname[0] == '\0' ||
701 		    (tpif = ifunit(rp->rp_portname)) == NULL) {
702 			error = EINVAL;
703 			break;
704 		}
705 		error = trunk_port_create(tr, tpif);
706 		break;
707 	case SIOCSTRUNKDELPORT:
708 		if ((error = suser(curproc, 0)) != 0) {
709 			error = EPERM;
710 			break;
711 		}
712 		if (rp->rp_portname[0] == '\0' ||
713 		    (tpif = ifunit(rp->rp_portname)) == NULL) {
714 			error = EINVAL;
715 			break;
716 		}
717 
718 		/* Search in all trunks if the global flag is set */
719 		if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
720 		    NULL : tr, tpif)) == NULL) {
721 			error = ENOENT;
722 			break;
723 		}
724 
725 		error = trunk_port_destroy(tp);
726 		break;
727 	case SIOCSIFADDR:
728 		ifp->if_flags |= IFF_UP;
729 #ifdef INET
730 		if (ifa->ifa_addr->sa_family == AF_INET)
731 			arp_ifinit(&tr->tr_ac, ifa);
732 #endif /* INET */
733 		error = ENETRESET;
734 		break;
735 	case SIOCSIFFLAGS:
736 		error = ENETRESET;
737 		break;
738 	case SIOCADDMULTI:
739 		error = trunk_ether_addmulti(tr, ifr);
740 		break;
741 	case SIOCDELMULTI:
742 		error = trunk_ether_delmulti(tr, ifr);
743 		break;
744 	case SIOCSIFMEDIA:
745 	case SIOCGIFMEDIA:
746 		error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
747 		break;
748 	case SIOCSIFLLADDR:
749 		/* Update the port lladdrs as well */
750 		SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
751 			trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
752 		error = ENETRESET;
753 		break;
754 	default:
755 		error = ether_ioctl(ifp, &tr->tr_ac, cmd, data);
756 	}
757 
758 	if (error == ENETRESET) {
759 		if (ifp->if_flags & IFF_UP) {
760 			if ((ifp->if_flags & IFF_RUNNING) == 0)
761 				trunk_init(ifp);
762 		} else {
763 			if (ifp->if_flags & IFF_RUNNING)
764 				trunk_stop(ifp);
765 		}
766 		error = 0;
767 	}
768 
769  out:
770 	splx(s);
771 	return (error);
772 }
773 
774 int
775 trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
776 {
777 	struct trunk_mc *mc;
778 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
779 	int error;
780 
781 	/* Ignore ENETRESET error code */
782 	if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
783 		return (error);
784 
785 	if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
786 		error = ENOMEM;
787 		goto failed;
788 	}
789 
790 	ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
791 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
792 	bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
793 	SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
794 
795 	if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
796 	    (caddr_t)ifr)) != 0) {
797 		trunk_ether_delmulti(tr, ifr);
798 		return (error);
799 	}
800 
801 	return (error);
802 
803  failed:
804 	ether_delmulti(ifr, &tr->tr_ac);
805 
806 	return (error);
807 }
808 
809 int
810 trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
811 {
812 	struct ether_multi *enm;
813 	struct trunk_mc *mc;
814 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
815 	int error;
816 
817 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
818 		return (error);
819 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
820 	if (enm == NULL)
821 		return (EINVAL);
822 
823 	SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
824 		if (mc->mc_enm == enm)
825 			break;
826 
827 	/* We won't delete entries we didn't add */
828 	if (mc == NULL)
829 		return (EINVAL);
830 
831 	if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
832 		return (error);
833 
834 	if ((error = trunk_ioctl_allports(tr, SIOCDELMULTI,
835 	    (caddr_t)ifr)) != 0) {
836 		/* XXX At least one port failed to remove the address */
837 		if (tr->tr_ifflags & IFF_DEBUG) {
838 			printf("%s: failed to remove multicast address "
839 			    "on all ports\n", tr->tr_ifname);
840 		}
841 	}
842 
843 	SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
844 	free(mc, M_DEVBUF, 0);
845 
846 	return (0);
847 }
848 
849 void
850 trunk_ether_purgemulti(struct trunk_softc *tr)
851 {
852 	struct trunk_mc *mc;
853 	struct trunk_ifreq ifs;
854 	struct ifreq *ifr = &ifs.ifreq.ifreq;
855 
856 	while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
857 		bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
858 
859 		/* Try to remove multicast address on all ports */
860 		trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
861 
862 		SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
863 		free(mc, M_DEVBUF, 0);
864 	}
865 }
866 
867 int
868 trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
869 {
870 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
871 	struct trunk_mc *mc;
872 	struct trunk_ifreq ifs;
873 	struct ifreq *ifr = &ifs.ifreq.ifreq;
874 	int ret, error = 0;
875 
876 	bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
877 	SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
878 		bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
879 
880 		if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
881 			if (tr->tr_ifflags & IFF_DEBUG) {
882 				printf("%s: ioctl %lu failed on %s: %d\n",
883 				    tr->tr_ifname, cmd, tp->tp_ifname, ret);
884 			}
885 			/* Store last known error and continue */
886 			error = ret;
887 		}
888 	}
889 
890 	return (error);
891 }
892 
893 int
894 trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
895 {
896 	struct ifreq *ifr = (struct ifreq *)data;
897 	struct trunk_port *tp;
898 	int ret, error = 0;
899 
900 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
901 		bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
902 		if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
903 			if (tr->tr_ifflags & IFF_DEBUG) {
904 				printf("%s: ioctl %lu failed on %s: %d\n",
905 				    tr->tr_ifname, cmd, tp->tp_ifname, ret);
906 			}
907 			/* Store last known error and continue */
908 			error = ret;
909 		}
910 	}
911 
912 	return (error);
913 }
914 
915 void
916 trunk_start(struct ifnet *ifp)
917 {
918 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
919 	struct mbuf *m;
920 	int error;
921 
922 	for (;;) {
923 		IFQ_DEQUEUE(&ifp->if_snd, m);
924 		if (m == NULL)
925 			break;
926 
927 #if NBPFILTER > 0
928 		if (ifp->if_bpf)
929 			bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
930 #endif
931 
932 		if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) {
933 			error = (*tr->tr_start)(tr, m);
934 			if (error == 0)
935 				ifp->if_opackets++;
936 			else
937 				ifp->if_oerrors++;
938 		} else {
939 			m_freem(m);
940 			if (tr->tr_proto != TRUNK_PROTO_NONE)
941 				ifp->if_oerrors++;
942 		}
943 	}
944 }
945 
946 int
947 trunk_enqueue(struct ifnet *ifp, struct mbuf *m)
948 {
949 	int len, error = 0;
950 	u_short mflags;
951 
952 	splassert(IPL_NET);
953 
954 	/* Send mbuf */
955 	mflags = m->m_flags;
956 	len = m->m_pkthdr.len;
957 	IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
958 	if (error)
959 		return (error);
960 	if_start(ifp);
961 
962 	ifp->if_obytes += len;
963 	if (mflags & M_MCAST)
964 		ifp->if_omcasts++;
965 
966 	return (error);
967 }
968 
969 u_int32_t
970 trunk_hashmbuf(struct mbuf *m, u_int32_t key)
971 {
972 	u_int16_t etype, ether_vtag;
973 	u_int32_t p = 0;
974 	u_int16_t *vlan, vlanbuf[2];
975 	int off;
976 	struct ether_header *eh;
977 #ifdef INET
978 	struct ip *ip, ipbuf;
979 #endif
980 #ifdef INET6
981 	u_int32_t flow;
982 	struct ip6_hdr *ip6, ip6buf;
983 #endif
984 
985 	off = sizeof(*eh);
986 	if (m->m_len < off)
987 		return (p);
988 	eh = mtod(m, struct ether_header *);
989 	etype = ntohs(eh->ether_type);
990 	p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key);
991 	p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
992 
993 	/* Special handling for encapsulating VLAN frames */
994 	if (m->m_flags & M_VLANTAG) {
995 		ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
996 		p = hash32_buf(&ether_vtag, sizeof(ether_vtag), p);
997 	} else if (etype == ETHERTYPE_VLAN) {
998 		if ((vlan = (u_int16_t *)
999 		    trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
1000 			return (p);
1001 		ether_vtag = EVL_VLANOFTAG(*vlan);
1002 		p = hash32_buf(&ether_vtag, sizeof(ether_vtag), p);
1003 		etype = ntohs(vlan[1]);
1004 		off += EVL_ENCAPLEN;
1005 	}
1006 
1007 	switch (etype) {
1008 #ifdef INET
1009 	case ETHERTYPE_IP:
1010 		if ((ip = (struct ip *)
1011 		    trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
1012 			return (p);
1013 		p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p);
1014 		p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
1015 		break;
1016 #endif
1017 #ifdef INET6
1018 	case ETHERTYPE_IPV6:
1019 		if ((ip6 = (struct ip6_hdr *)
1020 		    trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
1021 			return (p);
1022 		p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
1023 		p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
1024 		flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
1025 		p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */
1026 		break;
1027 #endif
1028 	}
1029 
1030 	return (p);
1031 }
1032 
1033 void
1034 trunk_init(struct ifnet *ifp)
1035 {
1036 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1037 	int s;
1038 
1039 	s = splnet();
1040 
1041 	ifp->if_flags |= IFF_RUNNING;
1042 	ifp->if_flags &= ~IFF_OACTIVE;
1043 
1044 	if (tr->tr_init != NULL)
1045 		(*tr->tr_init)(tr);
1046 
1047 	splx(s);
1048 }
1049 
1050 void
1051 trunk_stop(struct ifnet *ifp)
1052 {
1053 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1054 	int s;
1055 
1056 	s = splnet();
1057 
1058 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
1059 
1060 	if (tr->tr_stop != NULL)
1061 		(*tr->tr_stop)(tr);
1062 
1063 	splx(s);
1064 }
1065 
1066 void
1067 trunk_watchdog(struct ifnet *ifp)
1068 {
1069 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1070 
1071 	if (tr->tr_proto != TRUNK_PROTO_NONE &&
1072 	    (*tr->tr_watchdog)(tr) != 0) {
1073 		ifp->if_oerrors++;
1074 	}
1075 
1076 }
1077 
1078 int
1079 trunk_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
1080 {
1081 	struct trunk_softc *tr;
1082 	struct trunk_port *tp;
1083 	struct ifnet *trifp = NULL;
1084 	int error = 0;
1085 
1086 	/* Should be checked by the caller */
1087 	if (ifp->if_type != IFT_IEEE8023ADLAG) {
1088 		error = EPROTONOSUPPORT;
1089 		goto bad;
1090 	}
1091 	if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
1092 	    (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
1093 		error = ENOENT;
1094 		goto bad;
1095 	}
1096 	trifp = &tr->tr_ac.ac_if;
1097 	if (tr->tr_proto == TRUNK_PROTO_NONE) {
1098 		error = ENOENT;
1099 		goto bad;
1100 	}
1101 
1102 #if NBPFILTER > 0
1103 	if (trifp->if_bpf && tr->tr_proto != TRUNK_PROTO_FAILOVER)
1104 		bpf_mtap_hdr(trifp->if_bpf, (char *)eh, ETHER_HDR_LEN, m,
1105 		    BPF_DIRECTION_IN, NULL);
1106 #endif
1107 
1108 	error = (*tr->tr_input)(tr, tp, eh, m);
1109 	if (error != 0)
1110 		return (error);
1111 
1112 	trifp->if_ipackets++;
1113 	return (0);
1114 
1115  bad:
1116 	if (error > 0 && trifp != NULL)
1117 		trifp->if_ierrors++;
1118 	m_freem(m);
1119 	return (error);
1120 }
1121 
1122 int
1123 trunk_media_change(struct ifnet *ifp)
1124 {
1125 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1126 
1127 	if (tr->tr_ifflags & IFF_DEBUG)
1128 		printf("%s\n", __func__);
1129 
1130 	/* Ignore */
1131 	return (0);
1132 }
1133 
1134 void
1135 trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1136 {
1137 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1138 	struct trunk_port *tp;
1139 
1140 	imr->ifm_status = IFM_AVALID;
1141 	imr->ifm_active = IFM_ETHER | IFM_AUTO;
1142 
1143 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1144 		if (TRUNK_PORTACTIVE(tp))
1145 			imr->ifm_status |= IFM_ACTIVE;
1146 	}
1147 }
1148 
1149 void
1150 trunk_port_state(void *arg)
1151 {
1152 	struct trunk_port *tp = (struct trunk_port *)arg;
1153 	struct trunk_softc *tr = NULL;
1154 
1155 	if (tp != NULL)
1156 		tr = (struct trunk_softc *)tp->tp_trunk;
1157 	if (tr == NULL)
1158 		return;
1159 	if (tr->tr_linkstate != NULL)
1160 		(*tr->tr_linkstate)(tp);
1161 	trunk_link_active(tr, tp);
1162 }
1163 
1164 struct trunk_port *
1165 trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
1166 {
1167 	struct trunk_port *tp_next, *rval = NULL;
1168 	int new_link = LINK_STATE_DOWN;
1169 
1170 	/*
1171 	 * Search a port which reports an active link state.
1172 	 */
1173 
1174 	if (tp == NULL)
1175 		goto search;
1176 	if (TRUNK_PORTACTIVE(tp)) {
1177 		rval = tp;
1178 		goto found;
1179 	}
1180 	if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
1181 	    TRUNK_PORTACTIVE(tp_next)) {
1182 		rval = tp_next;
1183 		goto found;
1184 	}
1185 
1186  search:
1187 	SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
1188 		if (TRUNK_PORTACTIVE(tp_next)) {
1189 			rval = tp_next;
1190 			goto found;
1191 		}
1192 	}
1193 
1194  found:
1195 	if (rval != NULL) {
1196 		/*
1197 		 * The IEEE 802.1D standard assumes that a trunk with
1198 		 * multiple ports is always full duplex. This is valid
1199 		 * for load sharing trunks and if at least two links
1200 		 * are active. Unfortunately, checking the latter would
1201 		 * be too expensive at this point.
1202 		 */
1203 		if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
1204 		    (tr->tr_count > 1))
1205 			new_link = LINK_STATE_FULL_DUPLEX;
1206 		else
1207 			new_link = rval->tp_link_state;
1208 	}
1209 
1210 	if (tr->tr_ac.ac_if.if_link_state != new_link) {
1211 		tr->tr_ac.ac_if.if_link_state = new_link;
1212 		if_link_state_change(&tr->tr_ac.ac_if);
1213 	}
1214 
1215 	return (rval);
1216 }
1217 
1218 const void *
1219 trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
1220 {
1221 	if (m->m_pkthdr.len < (off + len))
1222 		return (NULL);
1223 	else if (m->m_len < (off + len)) {
1224 		m_copydata(m, off, len, buf);
1225 		return (buf);
1226 	}
1227 	return (mtod(m, caddr_t) + off);
1228 }
1229 
1230 /*
1231  * Simple round robin trunking
1232  */
1233 
1234 int
1235 trunk_rr_attach(struct trunk_softc *tr)
1236 {
1237 	struct trunk_port *tp;
1238 
1239 	tr->tr_detach = trunk_rr_detach;
1240 	tr->tr_start = trunk_rr_start;
1241 	tr->tr_input = trunk_rr_input;
1242 	tr->tr_init = NULL;
1243 	tr->tr_stop = NULL;
1244 	tr->tr_linkstate = NULL;
1245 	tr->tr_port_create = NULL;
1246 	tr->tr_port_destroy = trunk_rr_port_destroy;
1247 	tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1248 	tr->tr_req = NULL;
1249 	tr->tr_portreq = NULL;
1250 
1251 	tp = SLIST_FIRST(&tr->tr_ports);
1252 	tr->tr_psc = (caddr_t)tp;
1253 
1254 	return (0);
1255 }
1256 
1257 int
1258 trunk_rr_detach(struct trunk_softc *tr)
1259 {
1260 	tr->tr_psc = NULL;
1261 	return (0);
1262 }
1263 
1264 void
1265 trunk_rr_port_destroy(struct trunk_port *tp)
1266 {
1267 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1268 
1269 	if (tp == (struct trunk_port *)tr->tr_psc)
1270 		tr->tr_psc = NULL;
1271 }
1272 
1273 int
1274 trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
1275 {
1276 	struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
1277 	int error = 0;
1278 
1279 	if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) {
1280 		m_freem(m);
1281 		return (ENOENT);
1282 	}
1283 
1284 	/* Send mbuf */
1285 	if ((error = trunk_enqueue(tp->tp_if, m)) != 0)
1286 		return (error);
1287 
1288 	/* Get next active port */
1289 	tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
1290 	tr->tr_psc = (caddr_t)tp_next;
1291 
1292 	return (0);
1293 }
1294 
1295 int
1296 trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp,
1297     struct ether_header *eh, struct mbuf *m)
1298 {
1299 	struct ifnet *ifp = &tr->tr_ac.ac_if;
1300 
1301 	/* Just pass in the packet to our trunk device */
1302 	m->m_pkthdr.rcvif = ifp;
1303 
1304 	return (0);
1305 }
1306 
1307 /*
1308  * Active failover
1309  */
1310 
1311 int
1312 trunk_fail_attach(struct trunk_softc *tr)
1313 {
1314 	tr->tr_detach = trunk_fail_detach;
1315 	tr->tr_start = trunk_fail_start;
1316 	tr->tr_input = trunk_fail_input;
1317 	tr->tr_init = NULL;
1318 	tr->tr_stop = NULL;
1319 	tr->tr_port_create = NULL;
1320 	tr->tr_port_destroy = NULL;
1321 	tr->tr_linkstate = NULL;
1322 	tr->tr_req = NULL;
1323 	tr->tr_portreq = NULL;
1324 
1325 	return (0);
1326 }
1327 
1328 int
1329 trunk_fail_detach(struct trunk_softc *tr)
1330 {
1331 	return (0);
1332 }
1333 
1334 int
1335 trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
1336 {
1337 	struct trunk_port *tp;
1338 
1339 	/* Use the master port if active or the next available port */
1340 	if ((tp = trunk_link_active(tr, tr->tr_primary)) == NULL) {
1341 		m_freem(m);
1342 		return (ENOENT);
1343 	}
1344 
1345 	/* Send mbuf */
1346 	return (trunk_enqueue(tp->tp_if, m));
1347 }
1348 
1349 int
1350 trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp,
1351     struct ether_header *eh, struct mbuf *m)
1352 {
1353 	struct ifnet *ifp = &tr->tr_ac.ac_if;
1354 	struct trunk_port *tmp_tp;
1355 	int accept = 0;
1356 
1357 	if (tp == tr->tr_primary) {
1358 		accept = 1;
1359 	} else if (tr->tr_primary->tp_link_state == LINK_STATE_DOWN) {
1360 		tmp_tp = trunk_link_active(tr, NULL);
1361 		/*
1362 		 * If tmp_tp is null, we've received a packet when all
1363 		 * our links are down. Weird, but process it anyways.
1364 		 */
1365 		if ((tmp_tp == NULL || tmp_tp == tp))
1366 			accept = 1;
1367 	}
1368 	if (!accept) {
1369 		m_freem(m);
1370 		return (-1);
1371 	}
1372 #if NBPFILTER > 0
1373 	if (ifp->if_bpf)
1374 		bpf_mtap_hdr(ifp->if_bpf, (char *)eh, ETHER_HDR_LEN, m,
1375 		    BPF_DIRECTION_IN, NULL);
1376 #endif
1377 
1378 	m->m_pkthdr.rcvif = ifp;
1379 	return (0);
1380 }
1381 
1382 /*
1383  * Loadbalancing
1384  */
1385 
1386 int
1387 trunk_lb_attach(struct trunk_softc *tr)
1388 {
1389 	struct trunk_lb *lb;
1390 
1391 	if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
1392 		return (ENOMEM);
1393 
1394 	tr->tr_detach = trunk_lb_detach;
1395 	tr->tr_start = trunk_lb_start;
1396 	tr->tr_input = trunk_lb_input;
1397 	tr->tr_port_create = trunk_lb_port_create;
1398 	tr->tr_port_destroy = trunk_lb_port_destroy;
1399 	tr->tr_linkstate = NULL;
1400 	tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1401 	tr->tr_req = NULL;
1402 	tr->tr_portreq = NULL;
1403 	tr->tr_init = NULL;
1404 	tr->tr_stop = NULL;
1405 
1406 	lb->lb_key = arc4random();
1407 	tr->tr_psc = (caddr_t)lb;
1408 
1409 	return (0);
1410 }
1411 
1412 int
1413 trunk_lb_detach(struct trunk_softc *tr)
1414 {
1415 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1416 	if (lb != NULL)
1417 		free(lb, M_DEVBUF, 0);
1418 	return (0);
1419 }
1420 
1421 int
1422 trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
1423 {
1424 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1425 	struct trunk_port *tp_next;
1426 	int i = 0;
1427 
1428 	bzero(&lb->lb_ports, sizeof(lb->lb_ports));
1429 	SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
1430 		if (tp_next == tp)
1431 			continue;
1432 		if (i >= TRUNK_MAX_PORTS)
1433 			return (EINVAL);
1434 		if (tr->tr_ifflags & IFF_DEBUG)
1435 			printf("%s: port %s at index %d\n",
1436 			    tr->tr_ifname, tp_next->tp_ifname, i);
1437 		lb->lb_ports[i++] = tp_next;
1438 	}
1439 
1440 	return (0);
1441 }
1442 
1443 int
1444 trunk_lb_port_create(struct trunk_port *tp)
1445 {
1446 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1447 	return (trunk_lb_porttable(tr, NULL));
1448 }
1449 
1450 void
1451 trunk_lb_port_destroy(struct trunk_port *tp)
1452 {
1453 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1454 	trunk_lb_porttable(tr, tp);
1455 }
1456 
1457 int
1458 trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
1459 {
1460 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1461 	struct trunk_port *tp = NULL;
1462 	u_int32_t p = 0;
1463 
1464 	p = trunk_hashmbuf(m, lb->lb_key);
1465 	p %= tr->tr_count;
1466 	tp = lb->lb_ports[p];
1467 
1468 	/*
1469 	 * Check the port's link state. This will return the next active
1470 	 * port if the link is down or the port is NULL.
1471 	 */
1472 	if ((tp = trunk_link_active(tr, tp)) == NULL) {
1473 		m_freem(m);
1474 		return (ENOENT);
1475 	}
1476 
1477 	/* Send mbuf */
1478 	return (trunk_enqueue(tp->tp_if, m));
1479 }
1480 
1481 int
1482 trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp,
1483     struct ether_header *eh, struct mbuf *m)
1484 {
1485 	struct ifnet *ifp = &tr->tr_ac.ac_if;
1486 
1487 	/* Just pass in the packet to our trunk device */
1488 	m->m_pkthdr.rcvif = ifp;
1489 
1490 	return (0);
1491 }
1492 
1493 /*
1494  * Broadcast mode
1495  */
1496 
1497 int
1498 trunk_bcast_attach(struct trunk_softc *tr)
1499 {
1500 	tr->tr_detach = trunk_bcast_detach;
1501 	tr->tr_start = trunk_bcast_start;
1502 	tr->tr_input = trunk_bcast_input;
1503 	tr->tr_init = NULL;
1504 	tr->tr_stop = NULL;
1505 	tr->tr_port_create = NULL;
1506 	tr->tr_port_destroy = NULL;
1507 	tr->tr_linkstate = NULL;
1508 	tr->tr_req = NULL;
1509 	tr->tr_portreq = NULL;
1510 
1511 	return (0);
1512 }
1513 
1514 int
1515 trunk_bcast_detach(struct trunk_softc *tr)
1516 {
1517 	return (0);
1518 }
1519 
1520 int
1521 trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0)
1522 {
1523 	int			 active_ports = 0;
1524 	int			 errors = 0;
1525 	int			 ret;
1526 	struct trunk_port	*tp, *last = NULL;
1527 	struct mbuf		*m;
1528 
1529 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1530 		if (!TRUNK_PORTACTIVE(tp))
1531 			continue;
1532 
1533 		active_ports++;
1534 
1535 		if (last != NULL) {
1536 			m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
1537 			if (m == NULL) {
1538 				ret = ENOBUFS;
1539 				errors++;
1540 				break;
1541 			}
1542 
1543 			ret = trunk_enqueue(last->tp_if, m);
1544 			if (ret != 0)
1545 				errors++;
1546 		}
1547 		last = tp;
1548 	}
1549 	if (last == NULL) {
1550 		m_freem(m0);
1551 		return (ENOENT);
1552 	}
1553 
1554 	ret = trunk_enqueue(last->tp_if, m0);
1555 	if (ret != 0)
1556 		errors++;
1557 
1558 	if (errors == active_ports)
1559 		return (ret);
1560 
1561 	return (0);
1562 }
1563 
1564 int
1565 trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp,
1566     struct ether_header *eh, struct mbuf *m)
1567 {
1568 	struct ifnet *ifp = &tr->tr_ac.ac_if;
1569 
1570 	m->m_pkthdr.rcvif = ifp;
1571 	return (0);
1572 }
1573 
1574 /*
1575  * 802.3ad LACP
1576  */
1577 
1578 int
1579 trunk_lacp_attach(struct trunk_softc *tr)
1580 {
1581 	struct trunk_port *tp;
1582 	int error;
1583 
1584 	tr->tr_detach = trunk_lacp_detach;
1585 	tr->tr_port_create = lacp_port_create;
1586 	tr->tr_port_destroy = lacp_port_destroy;
1587 	tr->tr_linkstate = lacp_linkstate;
1588 	tr->tr_start = trunk_lacp_start;
1589 	tr->tr_input = trunk_lacp_input;
1590 	tr->tr_init = lacp_init;
1591 	tr->tr_stop = lacp_stop;
1592 	tr->tr_req = lacp_req;
1593 	tr->tr_portreq = lacp_portreq;
1594 
1595 	error = lacp_attach(tr);
1596 	if (error)
1597 		return (error);
1598 
1599 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1600 		lacp_port_create(tp);
1601 
1602 	return (error);
1603 }
1604 
1605 int
1606 trunk_lacp_detach(struct trunk_softc *tr)
1607 {
1608 	struct trunk_port *tp;
1609 	int error;
1610 
1611 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1612 		lacp_port_destroy(tp);
1613 
1614 	/* unlocking is safe here */
1615 	error = lacp_detach(tr);
1616 
1617 	return (error);
1618 }
1619 
1620 int
1621 trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
1622 {
1623 	struct trunk_port *tp;
1624 
1625 	tp = lacp_select_tx_port(tr, m);
1626 	if (tp == NULL) {
1627 		m_freem(m);
1628 		return (EBUSY);
1629 	}
1630 
1631 	/* Send mbuf */
1632 	return (trunk_enqueue(tp->tp_if, m));
1633 }
1634 
1635 int
1636 trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp,
1637     struct ether_header *eh, struct mbuf *m)
1638 {
1639 	struct ifnet *ifp = &tr->tr_ac.ac_if;
1640 
1641 	m = lacp_input(tp, eh, m);
1642 	if (m == NULL)
1643 		return (-1);
1644 
1645 	m->m_pkthdr.rcvif = ifp;
1646 	return (0);
1647 }
1648