xref: /openbsd-src/sys/net/if_trunk.c (revision 938ff1ae9de381d8345ce36468f0e90c37e6e5a3)
1*938ff1aeSbluhm /*	$OpenBSD: if_trunk.c,v 1.154 2023/12/23 10:52:54 bluhm Exp $	*/
21daccd23Sreyk 
31daccd23Sreyk /*
401b746f8Sreyk  * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
51daccd23Sreyk  *
61daccd23Sreyk  * Permission to use, copy, modify, and distribute this software for any
71daccd23Sreyk  * purpose with or without fee is hereby granted, provided that the above
81daccd23Sreyk  * copyright notice and this permission notice appear in all copies.
91daccd23Sreyk  *
101daccd23Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111daccd23Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121daccd23Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131daccd23Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141daccd23Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151daccd23Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161daccd23Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171daccd23Sreyk  */
181daccd23Sreyk 
191daccd23Sreyk #include <sys/param.h>
201daccd23Sreyk #include <sys/kernel.h>
211daccd23Sreyk #include <sys/malloc.h>
221daccd23Sreyk #include <sys/mbuf.h>
231daccd23Sreyk #include <sys/queue.h>
241daccd23Sreyk #include <sys/socket.h>
251daccd23Sreyk #include <sys/sockio.h>
261daccd23Sreyk #include <sys/systm.h>
2732f1989bSmikeb #include <sys/task.h>
281a83ae42Stedu #include <sys/timeout.h>
29fcf4ed95Stedu 
30fcf4ed95Stedu #include <crypto/siphash.h>
315009642bSreyk 
321daccd23Sreyk #include <net/if.h>
331daccd23Sreyk #include <net/if_dl.h>
341daccd23Sreyk #include <net/if_media.h>
351daccd23Sreyk #include <net/if_types.h>
365f704deeSflorian #include <net/route.h>
371daccd23Sreyk 
381daccd23Sreyk #include <netinet/in.h>
391daccd23Sreyk #include <netinet/if_ether.h>
405009642bSreyk #include <netinet/ip.h>
411daccd23Sreyk 
425009642bSreyk #ifdef INET6
435009642bSreyk #include <netinet/ip6.h>
445009642bSreyk #endif
455009642bSreyk 
465009642bSreyk #include <net/if_vlan_var.h>
471daccd23Sreyk #include <net/if_trunk.h>
48a14b8f16Smpf #include <net/trunklacp.h>
49a14b8f16Smpf 
508a8777f1Smpi #include "bpfilter.h"
518a8777f1Smpi #if NBPFILTER > 0
528a8777f1Smpi #include <net/bpf.h>
538a8777f1Smpi #endif
541daccd23Sreyk 
551daccd23Sreyk SLIST_HEAD(__trhead, trunk_softc) trunk_list;	/* list of trunks */
561daccd23Sreyk 
571daccd23Sreyk void	 trunkattach(int);
581daccd23Sreyk int	 trunk_clone_create(struct if_clone *, int);
591daccd23Sreyk int	 trunk_clone_destroy(struct ifnet *);
6008b2233fSreyk void	 trunk_lladdr(struct arpcom *, u_int8_t *);
61202a2b76Sreyk int	 trunk_capabilities(struct trunk_softc *);
6208b2233fSreyk void	 trunk_port_lladdr(struct trunk_port *, u_int8_t *);
631daccd23Sreyk int	 trunk_port_create(struct trunk_softc *, struct ifnet *);
641daccd23Sreyk int	 trunk_port_destroy(struct trunk_port *);
65f0a858ccSmcbride void	 trunk_port_state(void *);
66dd83e934Smpi void	 trunk_port_ifdetach(void *);
671daccd23Sreyk int	 trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
681527ed55Smpi int	 trunk_port_output(struct ifnet *, struct mbuf *, struct sockaddr *,
691527ed55Smpi 	    struct rtentry *);
701daccd23Sreyk struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
71aa76cea4Sreyk int	 trunk_port_checkstacking(struct trunk_softc *);
721daccd23Sreyk void	 trunk_port2req(struct trunk_port *, struct trunk_reqport *);
731daccd23Sreyk int	 trunk_ioctl(struct ifnet *, u_long, caddr_t);
74202a2b76Sreyk int	 trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
75202a2b76Sreyk int	 trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
76202a2b76Sreyk void	 trunk_ether_purgemulti(struct trunk_softc *);
77202a2b76Sreyk int	 trunk_ether_cmdmulti(struct trunk_port *, u_long);
78202a2b76Sreyk int	 trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
7923293512Sdlg void	 trunk_input(struct ifnet *, struct mbuf *);
801daccd23Sreyk void	 trunk_start(struct ifnet *);
81557bb425Sreyk void	 trunk_init(struct ifnet *);
82557bb425Sreyk void	 trunk_stop(struct ifnet *);
831daccd23Sreyk int	 trunk_media_change(struct ifnet *);
841daccd23Sreyk void	 trunk_media_status(struct ifnet *, struct ifmediareq *);
851daccd23Sreyk struct trunk_port *trunk_link_active(struct trunk_softc *,
861daccd23Sreyk 	    struct trunk_port *);
879f114d03Sbrad const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *);
881daccd23Sreyk 
891daccd23Sreyk struct if_clone trunk_cloner =
901daccd23Sreyk     IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
911daccd23Sreyk 
921daccd23Sreyk /* Simple round robin */
931daccd23Sreyk int	 trunk_rr_attach(struct trunk_softc *);
941daccd23Sreyk int	 trunk_rr_detach(struct trunk_softc *);
95202a2b76Sreyk void	 trunk_rr_port_destroy(struct trunk_port *);
961daccd23Sreyk int	 trunk_rr_start(struct trunk_softc *, struct mbuf *);
971daccd23Sreyk int	 trunk_rr_input(struct trunk_softc *, struct trunk_port *,
980ff5889aSmpi 	    struct mbuf *);
991daccd23Sreyk 
10008b2233fSreyk /* Active failover */
10108b2233fSreyk int	 trunk_fail_attach(struct trunk_softc *);
10208b2233fSreyk int	 trunk_fail_detach(struct trunk_softc *);
10371773c22Smikeb int	 trunk_fail_port_create(struct trunk_port *);
10471773c22Smikeb void	 trunk_fail_port_destroy(struct trunk_port *);
10508b2233fSreyk int	 trunk_fail_start(struct trunk_softc *, struct mbuf *);
10608b2233fSreyk int	 trunk_fail_input(struct trunk_softc *, struct trunk_port *,
1070ff5889aSmpi 	    struct mbuf *);
10871773c22Smikeb void	 trunk_fail_linkstate(struct trunk_port *);
10908b2233fSreyk 
1105009642bSreyk /* Loadbalancing */
1115009642bSreyk int	 trunk_lb_attach(struct trunk_softc *);
1125009642bSreyk int	 trunk_lb_detach(struct trunk_softc *);
1135009642bSreyk int	 trunk_lb_port_create(struct trunk_port *);
1145009642bSreyk void	 trunk_lb_port_destroy(struct trunk_port *);
1155009642bSreyk int	 trunk_lb_start(struct trunk_softc *, struct mbuf *);
1165009642bSreyk int	 trunk_lb_input(struct trunk_softc *, struct trunk_port *,
1170ff5889aSmpi 	    struct mbuf *);
1185009642bSreyk int	 trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
1195009642bSreyk 
120c0faeffaSpyr /* Broadcast mode */
121c0faeffaSpyr int	 trunk_bcast_attach(struct trunk_softc *);
122c0faeffaSpyr int	 trunk_bcast_detach(struct trunk_softc *);
123c0faeffaSpyr int	 trunk_bcast_start(struct trunk_softc *, struct mbuf *);
124c0faeffaSpyr int	 trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
1250ff5889aSmpi 	    struct mbuf *);
126c0faeffaSpyr 
127a14b8f16Smpf /* 802.3ad LACP */
128a14b8f16Smpf int	 trunk_lacp_attach(struct trunk_softc *);
129a14b8f16Smpf int	 trunk_lacp_detach(struct trunk_softc *);
130a14b8f16Smpf int	 trunk_lacp_start(struct trunk_softc *, struct mbuf *);
131a14b8f16Smpf int	 trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
1320ff5889aSmpi 	    struct mbuf *);
133a14b8f16Smpf 
1341daccd23Sreyk /* Trunk protocol table */
1351daccd23Sreyk static const struct {
1361daccd23Sreyk 	enum trunk_proto	ti_proto;
1371daccd23Sreyk 	int			(*ti_attach)(struct trunk_softc *);
1381daccd23Sreyk } trunk_protos[] = {
1391daccd23Sreyk 	{ TRUNK_PROTO_ROUNDROBIN,	trunk_rr_attach },
14008b2233fSreyk 	{ TRUNK_PROTO_FAILOVER,		trunk_fail_attach },
1415009642bSreyk 	{ TRUNK_PROTO_LOADBALANCE,	trunk_lb_attach },
142c0faeffaSpyr 	{ TRUNK_PROTO_BROADCAST,	trunk_bcast_attach },
143a14b8f16Smpf 	{ TRUNK_PROTO_LACP,		trunk_lacp_attach },
1445009642bSreyk 	{ TRUNK_PROTO_NONE,		NULL }
1451daccd23Sreyk };
1461daccd23Sreyk 
1471daccd23Sreyk void
trunkattach(int count)1481daccd23Sreyk trunkattach(int count)
1491daccd23Sreyk {
1501daccd23Sreyk 	SLIST_INIT(&trunk_list);
1511daccd23Sreyk 	if_clone_attach(&trunk_cloner);
1521daccd23Sreyk }
1531daccd23Sreyk 
1541daccd23Sreyk int
trunk_clone_create(struct if_clone * ifc,int unit)1551daccd23Sreyk trunk_clone_create(struct if_clone *ifc, int unit)
1561daccd23Sreyk {
1571daccd23Sreyk 	struct trunk_softc *tr;
1581daccd23Sreyk 	struct ifnet *ifp;
1591daccd23Sreyk 	int i, error = 0;
1601daccd23Sreyk 
161809d3a3eSbluhm 	tr = malloc(sizeof(*tr), M_DEVBUF, M_WAITOK|M_ZERO);
1621daccd23Sreyk 	tr->tr_proto = TRUNK_PROTO_NONE;
1631daccd23Sreyk 	for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
1641daccd23Sreyk 		if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
1651daccd23Sreyk 			tr->tr_proto = trunk_protos[i].ti_proto;
166aa1f0d07Sreyk 			if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
1670985b883Sderaadt 				free(tr, M_DEVBUF, sizeof *tr);
1681daccd23Sreyk 				return (error);
169aa1f0d07Sreyk 			}
1701daccd23Sreyk 			break;
1711daccd23Sreyk 		}
1721daccd23Sreyk 	}
1731daccd23Sreyk 	SLIST_INIT(&tr->tr_ports);
1741daccd23Sreyk 
1751daccd23Sreyk 	/* Initialise pseudo media types */
1761daccd23Sreyk 	ifmedia_init(&tr->tr_media, 0, trunk_media_change,
1771daccd23Sreyk 	    trunk_media_status);
1781daccd23Sreyk 	ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
1791daccd23Sreyk 	ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
1801daccd23Sreyk 
1811daccd23Sreyk 	ifp = &tr->tr_ac.ac_if;
1821daccd23Sreyk 	ifp->if_softc = tr;
1831daccd23Sreyk 	ifp->if_start = trunk_start;
1841daccd23Sreyk 	ifp->if_ioctl = trunk_ioctl;
1851daccd23Sreyk 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
186202a2b76Sreyk 	ifp->if_capabilities = trunk_capabilities(tr);
187b5aed6b3Smvs 	ifp->if_xflags = IFXF_CLONED;
1881daccd23Sreyk 
1891daccd23Sreyk 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
1901daccd23Sreyk 	    ifc->ifc_name, unit);
1911daccd23Sreyk 
1921daccd23Sreyk 	/*
1937bda3ff2Sstsp 	 * Attach as an ordinary ethernet device, children will be attached
1941daccd23Sreyk 	 * as special device IFT_IEEE8023ADLAG.
1951daccd23Sreyk 	 */
196*938ff1aeSbluhm 	if_counters_alloc(ifp);
1971daccd23Sreyk 	if_attach(ifp);
1981daccd23Sreyk 	ether_ifattach(ifp);
1991daccd23Sreyk 
2001daccd23Sreyk 	/* Insert into the global list of trunks */
2011daccd23Sreyk 	SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
2021daccd23Sreyk 
2031daccd23Sreyk 	return (0);
2041daccd23Sreyk }
2051daccd23Sreyk 
2061daccd23Sreyk int
trunk_clone_destroy(struct ifnet * ifp)2071daccd23Sreyk trunk_clone_destroy(struct ifnet *ifp)
2081daccd23Sreyk {
2091daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
2101daccd23Sreyk 	struct trunk_port *tp;
211aa28b9a6Smpi 	int error;
212aa76cea4Sreyk 
213202a2b76Sreyk 	/* Remove any multicast groups that we may have joined. */
214202a2b76Sreyk 	trunk_ether_purgemulti(tr);
215202a2b76Sreyk 
2161daccd23Sreyk 	/* Shutdown and remove trunk ports, return on error */
217aa28b9a6Smpi 	NET_LOCK();
218aa76cea4Sreyk 	while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
2199606f8b7Smpi 		if ((error = trunk_port_destroy(tp)) != 0) {
220aa28b9a6Smpi 			NET_UNLOCK();
2211daccd23Sreyk 			return (error);
222aa76cea4Sreyk 		}
2239606f8b7Smpi 	}
224aa28b9a6Smpi 	NET_UNLOCK();
2251daccd23Sreyk 
2261daccd23Sreyk 	ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
2271daccd23Sreyk 	ether_ifdetach(ifp);
2281daccd23Sreyk 	if_detach(ifp);
2291daccd23Sreyk 
2301daccd23Sreyk 	SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
2310985b883Sderaadt 	free(tr, M_DEVBUF, sizeof *tr);
2321daccd23Sreyk 
2331daccd23Sreyk 	return (0);
2341daccd23Sreyk }
2351daccd23Sreyk 
2361daccd23Sreyk void
trunk_lladdr(struct arpcom * ac,u_int8_t * lladdr)23708b2233fSreyk trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
2381daccd23Sreyk {
23908b2233fSreyk 	struct ifnet *ifp = &ac->ac_if;
2401daccd23Sreyk 	struct sockaddr_dl *sdl;
2411daccd23Sreyk 
242c26ed9b1Smpi 	sdl = ifp->if_sadl;
2431daccd23Sreyk 	sdl->sdl_type = IFT_ETHER;
2441daccd23Sreyk 	sdl->sdl_alen = ETHER_ADDR_LEN;
2451daccd23Sreyk 	bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
24608b2233fSreyk 	bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
2471daccd23Sreyk }
2481daccd23Sreyk 
2491daccd23Sreyk int
trunk_capabilities(struct trunk_softc * tr)250202a2b76Sreyk trunk_capabilities(struct trunk_softc *tr)
251202a2b76Sreyk {
252202a2b76Sreyk 	struct trunk_port *tp;
253c1b90172Sreyk 	int cap = ~0, priv;
254202a2b76Sreyk 
255c1b90172Sreyk 	/* Preserve private capabilities */
256c1b90172Sreyk 	priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
257c1b90172Sreyk 
258c1b90172Sreyk 	/* Get capabilities from the trunk ports */
259202a2b76Sreyk 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
260202a2b76Sreyk 		cap &= tp->tp_capabilities;
261202a2b76Sreyk 
262202a2b76Sreyk 	if (tr->tr_ifflags & IFF_DEBUG) {
263202a2b76Sreyk 		printf("%s: capabilities 0x%08x\n",
264c1b90172Sreyk 		    tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
265202a2b76Sreyk 	}
266202a2b76Sreyk 
267c1b90172Sreyk 	return (cap == ~0 ? priv : (cap | priv));
268202a2b76Sreyk }
269202a2b76Sreyk 
27008b2233fSreyk void
trunk_port_lladdr(struct trunk_port * tp,u_int8_t * lladdr)27108b2233fSreyk trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
27208b2233fSreyk {
27308b2233fSreyk 	struct ifnet *ifp = tp->tp_if;
27408b2233fSreyk 
27508b2233fSreyk 	/* Set the link layer address */
27608b2233fSreyk 	trunk_lladdr((struct arpcom *)ifp, lladdr);
27708b2233fSreyk 
27808b2233fSreyk 	/* Reset the port to update the lladdr */
27910a7a318Sderaadt 	ifnewlladdr(ifp);
28008b2233fSreyk }
28108b2233fSreyk 
282202a2b76Sreyk int
trunk_port_create(struct trunk_softc * tr,struct ifnet * ifp)2831daccd23Sreyk trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
2841daccd23Sreyk {
285aa76cea4Sreyk 	struct trunk_softc *tr_ptr;
2861daccd23Sreyk 	struct trunk_port *tp;
287dc3d0bfdSdlg 	struct arpcom *ac0;
2881daccd23Sreyk 	int error = 0;
2891daccd23Sreyk 
2901daccd23Sreyk 	/* Limit the maximal number of trunk ports */
2911daccd23Sreyk 	if (tr->tr_count >= TRUNK_MAX_PORTS)
2921daccd23Sreyk 		return (ENOSPC);
2931daccd23Sreyk 
2941daccd23Sreyk 	/* Check if port has already been associated to a trunk */
2951daccd23Sreyk 	if (trunk_port_get(NULL, ifp) != NULL)
2961daccd23Sreyk 		return (EBUSY);
2971daccd23Sreyk 
2981daccd23Sreyk 	/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
2991daccd23Sreyk 	if (ifp->if_type != IFT_ETHER)
3001daccd23Sreyk 		return (EPROTONOSUPPORT);
3011daccd23Sreyk 
302dc3d0bfdSdlg 	ac0 = (struct arpcom *)ifp;
303dc3d0bfdSdlg 	if (ac0->ac_trunkport != NULL)
304dc3d0bfdSdlg 		return (EBUSY);
305dc3d0bfdSdlg 
306084aeae2Ssthen 	/* Take MTU from the first member port */
307084aeae2Ssthen 	if (SLIST_EMPTY(&tr->tr_ports)) {
308084aeae2Ssthen 		if (tr->tr_ifflags & IFF_DEBUG)
309084aeae2Ssthen 			printf("%s: first port, setting trunk mtu %u\n",
310084aeae2Ssthen 			    tr->tr_ifname, ifp->if_mtu);
311084aeae2Ssthen 		tr->tr_ac.ac_if.if_mtu = ifp->if_mtu;
31248017b02Sdlg 		tr->tr_ac.ac_if.if_hardmtu = ifp->if_hardmtu;
313084aeae2Ssthen 	} else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) {
314084aeae2Ssthen 		printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname,
315084aeae2Ssthen 		    ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu);
316084aeae2Ssthen 		return (EINVAL);
317084aeae2Ssthen 	}
318084aeae2Ssthen 
3191daccd23Sreyk 	if ((error = ifpromisc(ifp, 1)) != 0)
3201daccd23Sreyk 		return (error);
3211daccd23Sreyk 
3220985b883Sderaadt 	if ((tp = malloc(sizeof *tp, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
3231daccd23Sreyk 		return (ENOMEM);
3241daccd23Sreyk 
325aa76cea4Sreyk 	/* Check if port is a stacked trunk */
326aa76cea4Sreyk 	SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
327aa76cea4Sreyk 		if (ifp == &tr_ptr->tr_ac.ac_if) {
328aa76cea4Sreyk 			tp->tp_flags |= TRUNK_PORT_STACK;
329aa76cea4Sreyk 			if (trunk_port_checkstacking(tr_ptr) >=
330aa76cea4Sreyk 			    TRUNK_MAX_STACKING) {
3310985b883Sderaadt 				free(tp, M_DEVBUF, sizeof *tp);
332aa76cea4Sreyk 				return (E2BIG);
333aa76cea4Sreyk 			}
334aa76cea4Sreyk 		}
335aa76cea4Sreyk 	}
336aa76cea4Sreyk 
3371daccd23Sreyk 	/* Change the interface type */
3381daccd23Sreyk 	tp->tp_iftype = ifp->if_type;
3391daccd23Sreyk 	ifp->if_type = IFT_IEEE8023ADLAG;
3400ff5889aSmpi 
3411daccd23Sreyk 	tp->tp_ioctl = ifp->if_ioctl;
3421daccd23Sreyk 	ifp->if_ioctl = trunk_port_ioctl;
3431daccd23Sreyk 
3441527ed55Smpi 	tp->tp_output = ifp->if_output;
3451527ed55Smpi 	ifp->if_output = trunk_port_output;
3461527ed55Smpi 
3471daccd23Sreyk 	tp->tp_if = ifp;
348557bb425Sreyk 	tp->tp_trunk = tr;
3491daccd23Sreyk 
35008b2233fSreyk 	/* Save port link layer address */
35108b2233fSreyk 	bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
35208b2233fSreyk 
3531daccd23Sreyk 	if (SLIST_EMPTY(&tr->tr_ports)) {
3541daccd23Sreyk 		tr->tr_primary = tp;
355aa76cea4Sreyk 		tp->tp_flags |= TRUNK_PORT_MASTER;
35608b2233fSreyk 		trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
3571daccd23Sreyk 	}
3581daccd23Sreyk 
35908b2233fSreyk 	/* Insert into the list of ports */
3601daccd23Sreyk 	SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
3611daccd23Sreyk 	tr->tr_count++;
3621daccd23Sreyk 
36360311479Smikeb 	/* Update link layer address for this port */
36460311479Smikeb 	trunk_port_lladdr(tp,
36560311479Smikeb 	    ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr);
36660311479Smikeb 
367202a2b76Sreyk 	/* Update trunk capabilities */
368202a2b76Sreyk 	tr->tr_capabilities = trunk_capabilities(tr);
369202a2b76Sreyk 
370202a2b76Sreyk 	/* Add multicast addresses to this port */
371202a2b76Sreyk 	trunk_ether_cmdmulti(tp, SIOCADDMULTI);
372202a2b76Sreyk 
373f0a858ccSmcbride 	/* Register callback for physical link state changes */
3744f5e51a4Sdlg 	task_set(&tp->tp_ltask, trunk_port_state, tp);
3754f5e51a4Sdlg 	if_linkstatehook_add(ifp, &tp->tp_ltask);
376f0a858ccSmcbride 
377dd83e934Smpi 	/* Register callback if parent wants to unregister */
3783fe9d1bdSdlg 	task_set(&tp->tp_dtask, trunk_port_ifdetach, tp);
3793fe9d1bdSdlg 	if_detachhook_add(ifp, &tp->tp_dtask);
380dd83e934Smpi 
381202a2b76Sreyk 	if (tr->tr_port_create != NULL)
382202a2b76Sreyk 		error = (*tr->tr_port_create)(tp);
383202a2b76Sreyk 
384759099e8Smikeb 	/* Change input handler of the physical interface. */
38523293512Sdlg 	tp->tp_input = ifp->if_input;
38623293512Sdlg 	NET_ASSERT_LOCKED();
38723293512Sdlg 	ac0->ac_trunkport = tp;
38823293512Sdlg 	ifp->if_input = trunk_input;
389759099e8Smikeb 
390202a2b76Sreyk 	return (error);
3911daccd23Sreyk }
3921daccd23Sreyk 
3931daccd23Sreyk int
trunk_port_checkstacking(struct trunk_softc * tr)394aa76cea4Sreyk trunk_port_checkstacking(struct trunk_softc *tr)
395aa76cea4Sreyk {
396aa76cea4Sreyk 	struct trunk_softc *tr_ptr;
397aa76cea4Sreyk 	struct trunk_port *tp;
398aa76cea4Sreyk 	int m = 0;
399aa76cea4Sreyk 
400aa76cea4Sreyk 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
401aa76cea4Sreyk 		if (tp->tp_flags & TRUNK_PORT_STACK) {
402aa76cea4Sreyk 			tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
403aa76cea4Sreyk 			m = MAX(m, trunk_port_checkstacking(tr_ptr));
404aa76cea4Sreyk 		}
405aa76cea4Sreyk 	}
406aa76cea4Sreyk 
407aa76cea4Sreyk 	return (m + 1);
408aa76cea4Sreyk }
409aa76cea4Sreyk 
410aa76cea4Sreyk int
trunk_port_destroy(struct trunk_port * tp)4111daccd23Sreyk trunk_port_destroy(struct trunk_port *tp)
4121daccd23Sreyk {
4131daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
4141daccd23Sreyk 	struct trunk_port *tp_ptr;
4151daccd23Sreyk 	struct ifnet *ifp = tp->tp_if;
416dc3d0bfdSdlg 	struct arpcom *ac0 = (struct arpcom *)ifp;
4171daccd23Sreyk 
418759099e8Smikeb 	/* Restore previous input handler. */
41923293512Sdlg 	NET_ASSERT_LOCKED();
42023293512Sdlg 	ifp->if_input = tp->tp_input;
421dc3d0bfdSdlg 	ac0->ac_trunkport = NULL;
422759099e8Smikeb 
423202a2b76Sreyk 	/* Remove multicast addresses from this port */
424202a2b76Sreyk 	trunk_ether_cmdmulti(tp, SIOCDELMULTI);
425202a2b76Sreyk 
4261daccd23Sreyk 	ifpromisc(ifp, 0);
4271daccd23Sreyk 
428bfa26dceSsthen 	if (tr->tr_port_destroy != NULL)
429bfa26dceSsthen 		(*tr->tr_port_destroy)(tp);
430bfa26dceSsthen 
4310ff5889aSmpi 	/* Restore interface type. */
4321daccd23Sreyk 	ifp->if_type = tp->tp_iftype;
4330ff5889aSmpi 
4341daccd23Sreyk 	ifp->if_ioctl = tp->tp_ioctl;
4351527ed55Smpi 	ifp->if_output = tp->tp_output;
4361daccd23Sreyk 
4373fe9d1bdSdlg 	if_detachhook_del(ifp, &tp->tp_dtask);
4384f5e51a4Sdlg 	if_linkstatehook_del(ifp, &tp->tp_ltask);
439f0a858ccSmcbride 
4401daccd23Sreyk 	/* Finally, remove the port from the trunk */
4411daccd23Sreyk 	SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
4421daccd23Sreyk 	tr->tr_count--;
4431daccd23Sreyk 
4441daccd23Sreyk 	/* Update the primary interface */
4451daccd23Sreyk 	if (tp == tr->tr_primary) {
4461daccd23Sreyk 		u_int8_t lladdr[ETHER_ADDR_LEN];
44708b2233fSreyk 
44808b2233fSreyk 		if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
4491daccd23Sreyk 			bzero(&lladdr, ETHER_ADDR_LEN);
4501daccd23Sreyk 		} else {
45108b2233fSreyk 			bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
45208b2233fSreyk 			    lladdr, ETHER_ADDR_LEN);
4531daccd23Sreyk 			tp_ptr->tp_flags = TRUNK_PORT_MASTER;
4541daccd23Sreyk 		}
45508b2233fSreyk 		trunk_lladdr(&tr->tr_ac, lladdr);
4561daccd23Sreyk 		tr->tr_primary = tp_ptr;
45708b2233fSreyk 
45808b2233fSreyk 		/* Update link layer address for each port */
45908b2233fSreyk 		SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
46008b2233fSreyk 			trunk_port_lladdr(tp_ptr, lladdr);
4611daccd23Sreyk 	}
4621daccd23Sreyk 
46308b2233fSreyk 	/* Reset the port lladdr */
46408b2233fSreyk 	trunk_port_lladdr(tp, tp->tp_lladdr);
46508b2233fSreyk 
466de7ac31dSmvs 	if_put(ifp);
4670985b883Sderaadt 	free(tp, M_DEVBUF, sizeof *tp);
4681daccd23Sreyk 
469202a2b76Sreyk 	/* Update trunk capabilities */
470202a2b76Sreyk 	tr->tr_capabilities = trunk_capabilities(tr);
471202a2b76Sreyk 
4721daccd23Sreyk 	return (0);
4731daccd23Sreyk }
4741daccd23Sreyk 
4751daccd23Sreyk int
trunk_port_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)4761daccd23Sreyk trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
4771daccd23Sreyk {
4781daccd23Sreyk 	struct trunk_reqport *rp = (struct trunk_reqport *)data;
4791daccd23Sreyk 	struct trunk_softc *tr;
4801f54fc5cSchl 	struct trunk_port *tp = NULL;
481de7ac31dSmvs 	struct ifnet *ifp0 = NULL;
482a4d2316eSmpi 	int error = 0;
4831daccd23Sreyk 
4841daccd23Sreyk 	/* Should be checked by the caller */
4851daccd23Sreyk 	if (ifp->if_type != IFT_IEEE8023ADLAG ||
486d1b1f373Smpi 	    (tp = trunk_port_get(NULL, ifp)) == NULL ||
48753bcb891Sbrad 	    (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
48853bcb891Sbrad 		error = EINVAL;
4891daccd23Sreyk 		goto fallback;
49053bcb891Sbrad 	}
4911daccd23Sreyk 
4921daccd23Sreyk 	switch (cmd) {
4931daccd23Sreyk 	case SIOCGTRUNKPORT:
4941daccd23Sreyk 		if (rp->rp_portname[0] == '\0' ||
495de7ac31dSmvs 		    (ifp0 = if_unit(rp->rp_portname)) != ifp) {
496de7ac31dSmvs 			if_put(ifp0);
4971daccd23Sreyk 			error = EINVAL;
4981daccd23Sreyk 			break;
4991daccd23Sreyk 		}
500de7ac31dSmvs 		if_put(ifp0);
5011daccd23Sreyk 
5021daccd23Sreyk 		/* Search in all trunks if the global flag is set */
5031daccd23Sreyk 		if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
5041daccd23Sreyk 		    NULL : tr, ifp)) == NULL) {
5051daccd23Sreyk 			error = ENOENT;
5061daccd23Sreyk 			break;
5071daccd23Sreyk 		}
5081daccd23Sreyk 
5091daccd23Sreyk 		trunk_port2req(tp, rp);
5101daccd23Sreyk 		break;
511084aeae2Ssthen 	case SIOCSIFMTU:
512084aeae2Ssthen 		/* Do not allow the MTU to be changed once joined */
513084aeae2Ssthen 		error = EINVAL;
514084aeae2Ssthen 		break;
5151daccd23Sreyk 	default:
51653bcb891Sbrad 		error = ENOTTY;
5171daccd23Sreyk 		goto fallback;
5181daccd23Sreyk 	}
5191daccd23Sreyk 
5201daccd23Sreyk 	return (error);
5211daccd23Sreyk 
5221daccd23Sreyk  fallback:
5236f1718b9Sreyk 	if (tp != NULL)
52453bcb891Sbrad 		error = (*tp->tp_ioctl)(ifp, cmd, data);
5256f1718b9Sreyk 
52653bcb891Sbrad 	return (error);
5271daccd23Sreyk }
5281daccd23Sreyk 
5291527ed55Smpi int
trunk_port_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)5301527ed55Smpi trunk_port_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
5311527ed55Smpi     struct rtentry *rt)
5321527ed55Smpi {
5331527ed55Smpi 	/* restrict transmission on trunk members to bpf only */
5341527ed55Smpi 	if (ifp->if_type == IFT_IEEE8023ADLAG &&
5351527ed55Smpi 	    (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
5361527ed55Smpi 		m_freem(m);
5371527ed55Smpi 		return (EBUSY);
5381527ed55Smpi 	}
5391527ed55Smpi 
5401527ed55Smpi 	return (ether_output(ifp, m, dst, rt));
5411527ed55Smpi }
5421527ed55Smpi 
5431daccd23Sreyk void
trunk_port_ifdetach(void * arg)544dd83e934Smpi trunk_port_ifdetach(void *arg)
5451daccd23Sreyk {
546dd83e934Smpi 	struct trunk_port *tp = (struct trunk_port *)arg;
5471daccd23Sreyk 
5481daccd23Sreyk 	trunk_port_destroy(tp);
5491daccd23Sreyk }
5501daccd23Sreyk 
5511daccd23Sreyk struct trunk_port *
trunk_port_get(struct trunk_softc * tr,struct ifnet * ifp)5521daccd23Sreyk trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
5531daccd23Sreyk {
5541daccd23Sreyk 	struct trunk_port *tp;
5551daccd23Sreyk 	struct trunk_softc *tr_ptr;
5561daccd23Sreyk 
5571daccd23Sreyk 	if (tr != NULL) {
5581daccd23Sreyk 		/* Search port in specified trunk */
5591daccd23Sreyk 		SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
5601daccd23Sreyk 			if (tp->tp_if == ifp)
5611daccd23Sreyk 				return (tp);
5621daccd23Sreyk 		}
5631daccd23Sreyk 	} else {
5641daccd23Sreyk 		/* Search all trunks for the selected port */
5651daccd23Sreyk 		SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
5661daccd23Sreyk 			SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
5671daccd23Sreyk 				if (tp->tp_if == ifp)
5681daccd23Sreyk 					return (tp);
5691daccd23Sreyk 			}
5701daccd23Sreyk 		}
5711daccd23Sreyk 	}
5721daccd23Sreyk 
5731daccd23Sreyk 	return (NULL);
5741daccd23Sreyk }
5751daccd23Sreyk 
5761daccd23Sreyk void
trunk_port2req(struct trunk_port * tp,struct trunk_reqport * rp)5771daccd23Sreyk trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
5781daccd23Sreyk {
5791daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
580a14b8f16Smpf 
5811daccd23Sreyk 	strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
5821daccd23Sreyk 	strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
58308b2233fSreyk 	rp->rp_prio = tp->tp_prio;
584a14b8f16Smpf 	if (tr->tr_portreq != NULL)
585a14b8f16Smpf 		(*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc);
586a14b8f16Smpf 
587a14b8f16Smpf 	/* Add protocol specific flags */
588a14b8f16Smpf 	switch (tr->tr_proto) {
589a14b8f16Smpf 	case TRUNK_PROTO_FAILOVER:
5904f95250cSbrad 		rp->rp_flags = tp->tp_flags;
5914f95250cSbrad 		if (tp == trunk_link_active(tr, tr->tr_primary))
5924f95250cSbrad 			rp->rp_flags |= TRUNK_PORT_ACTIVE;
5934f95250cSbrad 		break;
5944f95250cSbrad 
595a14b8f16Smpf 	case TRUNK_PROTO_ROUNDROBIN:
596a14b8f16Smpf 	case TRUNK_PROTO_LOADBALANCE:
597a14b8f16Smpf 	case TRUNK_PROTO_BROADCAST:
5984b7039e3Sreyk 		rp->rp_flags = tp->tp_flags;
59950f51b7cSreyk 		if (TRUNK_PORTACTIVE(tp))
60008b2233fSreyk 			rp->rp_flags |= TRUNK_PORT_ACTIVE;
601a14b8f16Smpf 		break;
602a14b8f16Smpf 
603a14b8f16Smpf 	case TRUNK_PROTO_LACP:
604a14b8f16Smpf 		/* LACP has a different definition of active */
605de7ec0f8Sreyk 		rp->rp_flags = lacp_port_status(tp);
606a14b8f16Smpf 		break;
607a14b8f16Smpf 	default:
608a14b8f16Smpf 		break;
609a14b8f16Smpf 	}
6101daccd23Sreyk }
6111daccd23Sreyk 
6121daccd23Sreyk int
trunk_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)6131daccd23Sreyk trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
6141daccd23Sreyk {
6151daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
6161daccd23Sreyk 	struct trunk_reqall *ra = (struct trunk_reqall *)data;
6171daccd23Sreyk 	struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
618838a4c90Sccardenas 	struct trunk_opts *tro = (struct trunk_opts *)data;
6191daccd23Sreyk 	struct ifreq *ifr = (struct ifreq *)data;
620838a4c90Sccardenas 	struct lacp_softc *lsc;
6211daccd23Sreyk 	struct trunk_port *tp;
622838a4c90Sccardenas 	struct lacp_port *lp;
6231daccd23Sreyk 	struct ifnet *tpif;
624a4d2316eSmpi 	int i, error = 0;
6251daccd23Sreyk 
6261daccd23Sreyk 	bzero(&rpbuf, sizeof(rpbuf));
6271daccd23Sreyk 
6281daccd23Sreyk 	switch (cmd) {
6291daccd23Sreyk 	case SIOCGTRUNK:
6301daccd23Sreyk 		ra->ra_proto = tr->tr_proto;
631a14b8f16Smpf 		if (tr->tr_req != NULL)
632a14b8f16Smpf 			(*tr->tr_req)(tr, (caddr_t)&ra->ra_psc);
6331daccd23Sreyk 		ra->ra_ports = i = 0;
6341daccd23Sreyk 		tp = SLIST_FIRST(&tr->tr_ports);
6351daccd23Sreyk 		while (tp && ra->ra_size >=
6361daccd23Sreyk 		    i + sizeof(struct trunk_reqport)) {
6371daccd23Sreyk 			trunk_port2req(tp, &rpbuf);
6381daccd23Sreyk 			error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
6391daccd23Sreyk 			    sizeof(struct trunk_reqport));
6401daccd23Sreyk 			if (error)
6411daccd23Sreyk 				break;
6421daccd23Sreyk 			i += sizeof(struct trunk_reqport);
6431daccd23Sreyk 			ra->ra_ports++;
6441daccd23Sreyk 			tp = SLIST_NEXT(tp, tp_entries);
6451daccd23Sreyk 		}
6461daccd23Sreyk 		break;
6471daccd23Sreyk 	case SIOCSTRUNK:
6483e676399Smpi 		if ((error = suser(curproc)) != 0) {
6491daccd23Sreyk 			error = EPERM;
6501daccd23Sreyk 			break;
6511daccd23Sreyk 		}
6521daccd23Sreyk 		if (ra->ra_proto >= TRUNK_PROTO_MAX) {
6531daccd23Sreyk 			error = EPROTONOSUPPORT;
6541daccd23Sreyk 			break;
6551daccd23Sreyk 		}
65623293512Sdlg 
65778f69838Smikeb 		/*
65823293512Sdlg 		 * Use of ifp->if_input and ac->ac_trunkport is
65923293512Sdlg 		 * protected by NET_LOCK, but that may not be true
66023293512Sdlg 		 * in the future. The below comment and code flow is
66123293512Sdlg 		 * maintained to help in that future.
66223293512Sdlg 		 *
66378f69838Smikeb 		 * Serialize modifications to the trunk and trunk
66478f69838Smikeb 		 * ports via the ifih SRP: detaching trunk_input
66578f69838Smikeb 		 * from the trunk port will require all currently
66678f69838Smikeb 		 * running trunk_input's on this port to finish
66778f69838Smikeb 		 * granting us an exclusive access to it.
66878f69838Smikeb 		 */
66923293512Sdlg 		NET_ASSERT_LOCKED();
67023293512Sdlg 		SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
67123293512Sdlg 			/* if_ih_remove(tp->tp_if, trunk_input, tp); */
67223293512Sdlg 			tp->tp_if->if_input = tp->tp_input;
67323293512Sdlg 		}
6741daccd23Sreyk 		if (tr->tr_proto != TRUNK_PROTO_NONE)
6751daccd23Sreyk 			error = tr->tr_detach(tr);
6761daccd23Sreyk 		if (error != 0)
6771daccd23Sreyk 			break;
678a1bae61bSmikeb 		for (i = 0; i < nitems(trunk_protos); i++) {
6791daccd23Sreyk 			if (trunk_protos[i].ti_proto == ra->ra_proto) {
6801daccd23Sreyk 				if (tr->tr_ifflags & IFF_DEBUG)
6811daccd23Sreyk 					printf("%s: using proto %u\n",
6821daccd23Sreyk 					    tr->tr_ifname,
6831daccd23Sreyk 					    trunk_protos[i].ti_proto);
6841daccd23Sreyk 				tr->tr_proto = trunk_protos[i].ti_proto;
6851daccd23Sreyk 				if (tr->tr_proto != TRUNK_PROTO_NONE)
6861daccd23Sreyk 					error = trunk_protos[i].ti_attach(tr);
68723293512Sdlg 				SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
68823293512Sdlg 					/* if_ih_insert(tp->tp_if,
68923293512Sdlg 					    trunk_input, tp); */
69023293512Sdlg 					tp->tp_if->if_input = trunk_input;
69123293512Sdlg 				}
692a1bae61bSmikeb 				/* Update trunk capabilities */
693a1bae61bSmikeb 				tr->tr_capabilities = trunk_capabilities(tr);
6941daccd23Sreyk 				goto out;
6951daccd23Sreyk 			}
6961daccd23Sreyk 		}
6971daccd23Sreyk 		error = EPROTONOSUPPORT;
6981daccd23Sreyk 		break;
699838a4c90Sccardenas 	case SIOCGTRUNKOPTS:
700838a4c90Sccardenas 		/* Only LACP trunks have options atm */
701838a4c90Sccardenas 		if (tro->to_proto != TRUNK_PROTO_LACP) {
702838a4c90Sccardenas 			error = EPROTONOSUPPORT;
703838a4c90Sccardenas 			break;
704838a4c90Sccardenas 		}
705838a4c90Sccardenas 		lsc = LACP_SOFTC(tr);
706838a4c90Sccardenas 		tro->to_lacpopts.lacp_mode = lsc->lsc_mode;
707838a4c90Sccardenas 		tro->to_lacpopts.lacp_timeout = lsc->lsc_timeout;
708838a4c90Sccardenas 		tro->to_lacpopts.lacp_prio = lsc->lsc_sys_prio;
709838a4c90Sccardenas 		tro->to_lacpopts.lacp_portprio = lsc->lsc_port_prio;
710838a4c90Sccardenas 		tro->to_lacpopts.lacp_ifqprio = lsc->lsc_ifq_prio;
711838a4c90Sccardenas 		break;
712838a4c90Sccardenas 	case SIOCSTRUNKOPTS:
713838a4c90Sccardenas 		if ((error = suser(curproc)) != 0) {
714838a4c90Sccardenas 			error = EPERM;
715838a4c90Sccardenas 			break;
716838a4c90Sccardenas 		}
717838a4c90Sccardenas 		/* Only LACP trunks have options atm */
718838a4c90Sccardenas 		if (tro->to_proto != TRUNK_PROTO_LACP) {
719838a4c90Sccardenas 			error = EPROTONOSUPPORT;
720838a4c90Sccardenas 			break;
721838a4c90Sccardenas 		}
722838a4c90Sccardenas 		lsc = LACP_SOFTC(tr);
723838a4c90Sccardenas 		switch(tro->to_opts) {
724838a4c90Sccardenas 			case TRUNK_OPT_LACP_MODE:
725838a4c90Sccardenas 				/*
726838a4c90Sccardenas 				 * Ensure mode changes occur immediately
727838a4c90Sccardenas 				 * on all ports
728838a4c90Sccardenas 				 */
729838a4c90Sccardenas 				lsc->lsc_mode = tro->to_lacpopts.lacp_mode;
730838a4c90Sccardenas 				if (lsc->lsc_mode == 0) {
731838a4c90Sccardenas 					LIST_FOREACH(lp, &lsc->lsc_ports,
732838a4c90Sccardenas 					    lp_next)
733838a4c90Sccardenas 						lp->lp_state &=
734838a4c90Sccardenas 						    ~LACP_STATE_ACTIVITY;
735838a4c90Sccardenas 				} else {
736838a4c90Sccardenas 					LIST_FOREACH(lp, &lsc->lsc_ports,
737838a4c90Sccardenas 					    lp_next)
738838a4c90Sccardenas 						lp->lp_state |=
739838a4c90Sccardenas 						    LACP_STATE_ACTIVITY;
740838a4c90Sccardenas 				}
741838a4c90Sccardenas 				break;
742838a4c90Sccardenas 			case TRUNK_OPT_LACP_TIMEOUT:
743838a4c90Sccardenas 				/*
744838a4c90Sccardenas 				 * Ensure timeout changes occur immediately
745838a4c90Sccardenas 				 * on all ports
746838a4c90Sccardenas 				 */
747838a4c90Sccardenas 				lsc->lsc_timeout =
748838a4c90Sccardenas 				    tro->to_lacpopts.lacp_timeout;
749838a4c90Sccardenas 				if (lsc->lsc_timeout == 0) {
750838a4c90Sccardenas 					LIST_FOREACH(lp, &lsc->lsc_ports,
751838a4c90Sccardenas 					    lp_next)
752838a4c90Sccardenas 						lp->lp_state &=
753838a4c90Sccardenas 						    ~LACP_STATE_TIMEOUT;
754838a4c90Sccardenas 				} else {
755838a4c90Sccardenas 					LIST_FOREACH(lp, &lsc->lsc_ports,
756838a4c90Sccardenas 					    lp_next)
757838a4c90Sccardenas 						lp->lp_state |=
758838a4c90Sccardenas 						    LACP_STATE_TIMEOUT;
759838a4c90Sccardenas 				}
760838a4c90Sccardenas 				break;
761838a4c90Sccardenas 			case TRUNK_OPT_LACP_SYS_PRIO:
762838a4c90Sccardenas 				if (tro->to_lacpopts.lacp_prio == 0) {
763838a4c90Sccardenas 					error = EINVAL;
764838a4c90Sccardenas 					break;
765838a4c90Sccardenas 				}
766838a4c90Sccardenas 				lsc->lsc_sys_prio = tro->to_lacpopts.lacp_prio;
767838a4c90Sccardenas 				break;
768838a4c90Sccardenas 			case TRUNK_OPT_LACP_PORT_PRIO:
769838a4c90Sccardenas 				if (tro->to_lacpopts.lacp_portprio == 0) {
770838a4c90Sccardenas 					error = EINVAL;
771838a4c90Sccardenas 					break;
772838a4c90Sccardenas 				}
773838a4c90Sccardenas 				lsc->lsc_port_prio =
774838a4c90Sccardenas 				    tro->to_lacpopts.lacp_portprio;
775838a4c90Sccardenas 				break;
776838a4c90Sccardenas 			case TRUNK_OPT_LACP_IFQ_PRIO:
777838a4c90Sccardenas 				if (tro->to_lacpopts.lacp_ifqprio >
778838a4c90Sccardenas 				    IFQ_MAXPRIO) {
779838a4c90Sccardenas 					error = EINVAL;
780838a4c90Sccardenas 					break;
781838a4c90Sccardenas 				}
782838a4c90Sccardenas 				lsc->lsc_ifq_prio =
783838a4c90Sccardenas 				    tro->to_lacpopts.lacp_ifqprio;
784838a4c90Sccardenas 				break;
785838a4c90Sccardenas 		}
786838a4c90Sccardenas 		break;
7871daccd23Sreyk 	case SIOCGTRUNKPORT:
7881daccd23Sreyk 		if (rp->rp_portname[0] == '\0' ||
789de7ac31dSmvs 		    (tpif = if_unit(rp->rp_portname)) == NULL) {
7901daccd23Sreyk 			error = EINVAL;
7911daccd23Sreyk 			break;
7921daccd23Sreyk 		}
7931daccd23Sreyk 
7941daccd23Sreyk 		/* Search in all trunks if the global flag is set */
795de7ac31dSmvs 		tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
796de7ac31dSmvs 		    NULL : tr, tpif);
797de7ac31dSmvs 		if_put(tpif);
798de7ac31dSmvs 
799de7ac31dSmvs 		if(tp == NULL) {
8001daccd23Sreyk 			error = ENOENT;
8011daccd23Sreyk 			break;
8021daccd23Sreyk 		}
8031daccd23Sreyk 
8041daccd23Sreyk 		trunk_port2req(tp, rp);
8051daccd23Sreyk 		break;
8061daccd23Sreyk 	case SIOCSTRUNKPORT:
8073e676399Smpi 		if ((error = suser(curproc)) != 0) {
8081daccd23Sreyk 			error = EPERM;
8091daccd23Sreyk 			break;
8101daccd23Sreyk 		}
8111daccd23Sreyk 		if (rp->rp_portname[0] == '\0' ||
812de7ac31dSmvs 		    (tpif = if_unit(rp->rp_portname)) == NULL) {
8131daccd23Sreyk 			error = EINVAL;
8141daccd23Sreyk 			break;
8151daccd23Sreyk 		}
8161daccd23Sreyk 		error = trunk_port_create(tr, tpif);
817de7ac31dSmvs 		if (error != 0)
818de7ac31dSmvs 			if_put(tpif);
8191daccd23Sreyk 		break;
8201daccd23Sreyk 	case SIOCSTRUNKDELPORT:
8213e676399Smpi 		if ((error = suser(curproc)) != 0) {
8221daccd23Sreyk 			error = EPERM;
8231daccd23Sreyk 			break;
8241daccd23Sreyk 		}
8251daccd23Sreyk 		if (rp->rp_portname[0] == '\0' ||
826de7ac31dSmvs 		    (tpif = if_unit(rp->rp_portname)) == NULL) {
8271daccd23Sreyk 			error = EINVAL;
8281daccd23Sreyk 			break;
8291daccd23Sreyk 		}
8301daccd23Sreyk 
8311daccd23Sreyk 		/* Search in all trunks if the global flag is set */
832de7ac31dSmvs 		tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
833de7ac31dSmvs 		    NULL : tr, tpif);
834de7ac31dSmvs 		if_put(tpif);
835de7ac31dSmvs 
836de7ac31dSmvs 		if(tp == NULL) {
8371daccd23Sreyk 			error = ENOENT;
8381daccd23Sreyk 			break;
8391daccd23Sreyk 		}
8401daccd23Sreyk 
8411daccd23Sreyk 		error = trunk_port_destroy(tp);
8421daccd23Sreyk 		break;
8431daccd23Sreyk 	case SIOCSIFADDR:
8440366e53cSbrad 		ifp->if_flags |= IFF_UP;
84560e40e7bSmpi 		/* FALLTHROUGH */
8461daccd23Sreyk 	case SIOCSIFFLAGS:
8474cddaeaeSreyk 		error = ENETRESET;
8481daccd23Sreyk 		break;
8491daccd23Sreyk 	case SIOCADDMULTI:
850202a2b76Sreyk 		error = trunk_ether_addmulti(tr, ifr);
8511daccd23Sreyk 		break;
8521daccd23Sreyk 	case SIOCDELMULTI:
853202a2b76Sreyk 		error = trunk_ether_delmulti(tr, ifr);
8541daccd23Sreyk 		break;
8551daccd23Sreyk 	case SIOCSIFMEDIA:
8561daccd23Sreyk 	case SIOCGIFMEDIA:
8571daccd23Sreyk 		error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
8581daccd23Sreyk 		break;
85908b2233fSreyk 	case SIOCSIFLLADDR:
86008b2233fSreyk 		/* Update the port lladdrs as well */
86108b2233fSreyk 		SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
86208b2233fSreyk 			trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
863557bb425Sreyk 		error = ENETRESET;
86408b2233fSreyk 		break;
8651daccd23Sreyk 	default:
866775775feSbrad 		error = ether_ioctl(ifp, &tr->tr_ac, cmd, data);
8671daccd23Sreyk 	}
8681daccd23Sreyk 
8694cddaeaeSreyk 	if (error == ENETRESET) {
870557bb425Sreyk 		if (ifp->if_flags & IFF_UP) {
871557bb425Sreyk 			if ((ifp->if_flags & IFF_RUNNING) == 0)
872557bb425Sreyk 				trunk_init(ifp);
873557bb425Sreyk 		} else {
874557bb425Sreyk 			if (ifp->if_flags & IFF_RUNNING)
875557bb425Sreyk 				trunk_stop(ifp);
876557bb425Sreyk 		}
8774cddaeaeSreyk 		error = 0;
8784cddaeaeSreyk 	}
8794cddaeaeSreyk 
8801daccd23Sreyk  out:
8811daccd23Sreyk 	return (error);
8821daccd23Sreyk }
8831daccd23Sreyk 
884202a2b76Sreyk int
trunk_ether_addmulti(struct trunk_softc * tr,struct ifreq * ifr)885202a2b76Sreyk trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
886202a2b76Sreyk {
887202a2b76Sreyk 	struct trunk_mc *mc;
888202a2b76Sreyk 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
889202a2b76Sreyk 	int error;
890202a2b76Sreyk 
891202a2b76Sreyk 	/* Ignore ENETRESET error code */
892202a2b76Sreyk 	if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
893202a2b76Sreyk 		return (error);
894202a2b76Sreyk 
8958b28ab37Shenning 	if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
896202a2b76Sreyk 		error = ENOMEM;
897202a2b76Sreyk 		goto failed;
898202a2b76Sreyk 	}
899202a2b76Sreyk 
900202a2b76Sreyk 	ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
901202a2b76Sreyk 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
902202a2b76Sreyk 	bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
903202a2b76Sreyk 	SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
904202a2b76Sreyk 
905202a2b76Sreyk 	if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
906202a2b76Sreyk 	    (caddr_t)ifr)) != 0) {
907202a2b76Sreyk 		trunk_ether_delmulti(tr, ifr);
908202a2b76Sreyk 		return (error);
909202a2b76Sreyk 	}
910202a2b76Sreyk 
911202a2b76Sreyk 	return (error);
912202a2b76Sreyk 
913202a2b76Sreyk  failed:
914202a2b76Sreyk 	ether_delmulti(ifr, &tr->tr_ac);
915202a2b76Sreyk 
916202a2b76Sreyk 	return (error);
917202a2b76Sreyk }
918202a2b76Sreyk 
919202a2b76Sreyk int
trunk_ether_delmulti(struct trunk_softc * tr,struct ifreq * ifr)920202a2b76Sreyk trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
921202a2b76Sreyk {
922202a2b76Sreyk 	struct ether_multi *enm;
923202a2b76Sreyk 	struct trunk_mc *mc;
924202a2b76Sreyk 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
925202a2b76Sreyk 	int error;
926202a2b76Sreyk 
927202a2b76Sreyk 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
928202a2b76Sreyk 		return (error);
929202a2b76Sreyk 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
930ae84a4ccSmpf 	if (enm == NULL)
931ae84a4ccSmpf 		return (EINVAL);
932ae84a4ccSmpf 
933ae84a4ccSmpf 	SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
934ae84a4ccSmpf 		if (mc->mc_enm == enm)
935ae84a4ccSmpf 			break;
936ae84a4ccSmpf 
937ae84a4ccSmpf 	/* We won't delete entries we didn't add */
938ae84a4ccSmpf 	if (mc == NULL)
939ae84a4ccSmpf 		return (EINVAL);
940202a2b76Sreyk 
941202a2b76Sreyk 	if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
942202a2b76Sreyk 		return (error);
943202a2b76Sreyk 
94464d4a3f9Smpi 	/* We no longer use this multicast address.  Tell parent so. */
94564d4a3f9Smpi 	error = trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
94664d4a3f9Smpi 	if (error == 0) {
94764d4a3f9Smpi 		SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
94864d4a3f9Smpi 		free(mc, M_DEVBUF, sizeof(*mc));
94964d4a3f9Smpi 	} else {
950202a2b76Sreyk 		/* XXX At least one port failed to remove the address */
951202a2b76Sreyk 		if (tr->tr_ifflags & IFF_DEBUG) {
952202a2b76Sreyk 			printf("%s: failed to remove multicast address "
95364d4a3f9Smpi 			    "on all ports (%d)\n", tr->tr_ifname, error);
954202a2b76Sreyk 		}
95564d4a3f9Smpi 		(void)ether_addmulti(ifr, &tr->tr_ac);
956202a2b76Sreyk 	}
957202a2b76Sreyk 
958202a2b76Sreyk 	return (0);
959202a2b76Sreyk }
960202a2b76Sreyk 
961202a2b76Sreyk void
trunk_ether_purgemulti(struct trunk_softc * tr)962202a2b76Sreyk trunk_ether_purgemulti(struct trunk_softc *tr)
963202a2b76Sreyk {
964202a2b76Sreyk 	struct trunk_mc *mc;
965202a2b76Sreyk 	struct trunk_ifreq ifs;
966202a2b76Sreyk 	struct ifreq *ifr = &ifs.ifreq.ifreq;
967202a2b76Sreyk 
968202a2b76Sreyk 	while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
969202a2b76Sreyk 		bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
970202a2b76Sreyk 
971202a2b76Sreyk 		/* Try to remove multicast address on all ports */
972202a2b76Sreyk 		trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
973202a2b76Sreyk 
974202a2b76Sreyk 		SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
97564d4a3f9Smpi 		free(mc, M_DEVBUF, sizeof(*mc));
976202a2b76Sreyk 	}
977202a2b76Sreyk }
978202a2b76Sreyk 
979202a2b76Sreyk int
trunk_ether_cmdmulti(struct trunk_port * tp,u_long cmd)980202a2b76Sreyk trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
981202a2b76Sreyk {
982202a2b76Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
983202a2b76Sreyk 	struct trunk_mc *mc;
984202a2b76Sreyk 	struct trunk_ifreq ifs;
985202a2b76Sreyk 	struct ifreq *ifr = &ifs.ifreq.ifreq;
986202a2b76Sreyk 	int ret, error = 0;
987202a2b76Sreyk 
988202a2b76Sreyk 	bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
989202a2b76Sreyk 	SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
990202a2b76Sreyk 		bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
991202a2b76Sreyk 
992202a2b76Sreyk 		if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
993202a2b76Sreyk 			if (tr->tr_ifflags & IFF_DEBUG) {
994202a2b76Sreyk 				printf("%s: ioctl %lu failed on %s: %d\n",
995202a2b76Sreyk 				    tr->tr_ifname, cmd, tp->tp_ifname, ret);
996202a2b76Sreyk 			}
997202a2b76Sreyk 			/* Store last known error and continue */
998202a2b76Sreyk 			error = ret;
999202a2b76Sreyk 		}
1000202a2b76Sreyk 	}
1001202a2b76Sreyk 
1002202a2b76Sreyk 	return (error);
1003202a2b76Sreyk }
1004202a2b76Sreyk 
1005202a2b76Sreyk int
trunk_ioctl_allports(struct trunk_softc * tr,u_long cmd,caddr_t data)1006202a2b76Sreyk trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
1007202a2b76Sreyk {
1008202a2b76Sreyk 	struct ifreq *ifr = (struct ifreq *)data;
1009202a2b76Sreyk 	struct trunk_port *tp;
1010202a2b76Sreyk 	int ret, error = 0;
1011202a2b76Sreyk 
1012202a2b76Sreyk 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1013202a2b76Sreyk 		bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
1014202a2b76Sreyk 		if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
1015202a2b76Sreyk 			if (tr->tr_ifflags & IFF_DEBUG) {
1016202a2b76Sreyk 				printf("%s: ioctl %lu failed on %s: %d\n",
1017202a2b76Sreyk 				    tr->tr_ifname, cmd, tp->tp_ifname, ret);
1018202a2b76Sreyk 			}
1019202a2b76Sreyk 			/* Store last known error and continue */
1020202a2b76Sreyk 			error = ret;
1021202a2b76Sreyk 		}
1022202a2b76Sreyk 	}
1023202a2b76Sreyk 
1024202a2b76Sreyk 	return (error);
1025202a2b76Sreyk }
1026202a2b76Sreyk 
10271daccd23Sreyk void
trunk_start(struct ifnet * ifp)10281daccd23Sreyk trunk_start(struct ifnet *ifp)
10291daccd23Sreyk {
10301daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
10311daccd23Sreyk 	struct mbuf *m;
1032b8619708Sderaadt 	int error;
10331daccd23Sreyk 
1034b8619708Sderaadt 	for (;;) {
103563bcfa73Spatrick 		m = ifq_dequeue(&ifp->if_snd);
10361daccd23Sreyk 		if (m == NULL)
10371daccd23Sreyk 			break;
10381daccd23Sreyk 
10391daccd23Sreyk #if NBPFILTER > 0
10401daccd23Sreyk 		if (ifp->if_bpf)
1041cfdf8bdaSnaddy 			bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
10421daccd23Sreyk #endif
10431daccd23Sreyk 
1044ad49f3f6Sbrad 		if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) {
1045439ab1cfSbrad 			error = (*tr->tr_start)(tr, m);
104688a08f2aSdlg 			if (error != 0)
10471daccd23Sreyk 				ifp->if_oerrors++;
1048ad49f3f6Sbrad 		} else {
1049ad49f3f6Sbrad 			m_freem(m);
1050ad49f3f6Sbrad 			if (tr->tr_proto != TRUNK_PROTO_NONE)
1051ad49f3f6Sbrad 				ifp->if_oerrors++;
1052ad49f3f6Sbrad 		}
10531daccd23Sreyk 	}
10541daccd23Sreyk }
10551daccd23Sreyk 
1056557bb425Sreyk u_int32_t
trunk_hashmbuf(struct mbuf * m,SIPHASH_KEY * key)1057fcf4ed95Stedu trunk_hashmbuf(struct mbuf *m, SIPHASH_KEY *key)
1058557bb425Sreyk {
10594586ec53Sbrad 	u_int16_t etype, ether_vtag;
10608e3ca645Sbrad 	u_int32_t p = 0;
1061557bb425Sreyk 	u_int16_t *vlan, vlanbuf[2];
1062557bb425Sreyk 	int off;
1063557bb425Sreyk 	struct ether_header *eh;
1064557bb425Sreyk 	struct ip *ip, ipbuf;
1065557bb425Sreyk #ifdef INET6
10668e3ca645Sbrad 	u_int32_t flow;
1067557bb425Sreyk 	struct ip6_hdr *ip6, ip6buf;
1068557bb425Sreyk #endif
1069fcf4ed95Stedu 	SIPHASH_CTX ctx;
1070557bb425Sreyk 
1071379654e8Sdlg 	if (m->m_pkthdr.csum_flags & M_FLOWID)
1072379654e8Sdlg 		return (m->m_pkthdr.ph_flowid);
107360810709Sdlg 
1074fcf4ed95Stedu 	SipHash24_Init(&ctx, key);
1075557bb425Sreyk 	off = sizeof(*eh);
1076557bb425Sreyk 	if (m->m_len < off)
1077fcf4ed95Stedu 		goto done;
1078557bb425Sreyk 	eh = mtod(m, struct ether_header *);
1079557bb425Sreyk 	etype = ntohs(eh->ether_type);
1080fcf4ed95Stedu 	SipHash24_Update(&ctx, &eh->ether_shost, ETHER_ADDR_LEN);
1081fcf4ed95Stedu 	SipHash24_Update(&ctx, &eh->ether_dhost, ETHER_ADDR_LEN);
1082557bb425Sreyk 
1083557bb425Sreyk 	/* Special handling for encapsulating VLAN frames */
10844586ec53Sbrad 	if (m->m_flags & M_VLANTAG) {
10854586ec53Sbrad 		ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
1086fcf4ed95Stedu 		SipHash24_Update(&ctx, &ether_vtag, sizeof(ether_vtag));
10874586ec53Sbrad 	} else if (etype == ETHERTYPE_VLAN) {
1088557bb425Sreyk 		if ((vlan = (u_int16_t *)
10899f114d03Sbrad 		    trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
1090557bb425Sreyk 			return (p);
1091315af8dbSnaddy 		ether_vtag = EVL_VLANOFTAG(*vlan);
1092fcf4ed95Stedu 		SipHash24_Update(&ctx, &ether_vtag, sizeof(ether_vtag));
1093557bb425Sreyk 		etype = ntohs(vlan[1]);
1094557bb425Sreyk 		off += EVL_ENCAPLEN;
1095557bb425Sreyk 	}
1096557bb425Sreyk 
1097557bb425Sreyk 	switch (etype) {
1098557bb425Sreyk 	case ETHERTYPE_IP:
1099557bb425Sreyk 		if ((ip = (struct ip *)
11009f114d03Sbrad 		    trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
1101557bb425Sreyk 			return (p);
1102fcf4ed95Stedu 		SipHash24_Update(&ctx, &ip->ip_src, sizeof(struct in_addr));
1103fcf4ed95Stedu 		SipHash24_Update(&ctx, &ip->ip_dst, sizeof(struct in_addr));
1104557bb425Sreyk 		break;
1105557bb425Sreyk #ifdef INET6
1106557bb425Sreyk 	case ETHERTYPE_IPV6:
1107557bb425Sreyk 		if ((ip6 = (struct ip6_hdr *)
11089f114d03Sbrad 		    trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
1109557bb425Sreyk 			return (p);
1110fcf4ed95Stedu 		SipHash24_Update(&ctx, &ip6->ip6_src, sizeof(struct in6_addr));
1111fcf4ed95Stedu 		SipHash24_Update(&ctx, &ip6->ip6_dst, sizeof(struct in6_addr));
11122a2060f9Sbrad 		flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
1113fcf4ed95Stedu 		SipHash24_Update(&ctx, &flow, sizeof(flow)); /* IPv6 flow label */
1114557bb425Sreyk 		break;
1115557bb425Sreyk #endif
1116557bb425Sreyk 	}
1117557bb425Sreyk 
1118fcf4ed95Stedu done:
1119fcf4ed95Stedu 	return SipHash24_End(&ctx);
1120557bb425Sreyk }
1121557bb425Sreyk 
1122557bb425Sreyk void
trunk_init(struct ifnet * ifp)1123557bb425Sreyk trunk_init(struct ifnet *ifp)
1124557bb425Sreyk {
1125557bb425Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1126557bb425Sreyk 
1127557bb425Sreyk 	ifp->if_flags |= IFF_RUNNING;
1128557bb425Sreyk 
1129557bb425Sreyk 	if (tr->tr_init != NULL)
1130557bb425Sreyk 		(*tr->tr_init)(tr);
1131557bb425Sreyk }
1132557bb425Sreyk 
1133557bb425Sreyk void
trunk_stop(struct ifnet * ifp)1134557bb425Sreyk trunk_stop(struct ifnet *ifp)
1135557bb425Sreyk {
1136557bb425Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1137557bb425Sreyk 
1138f4c1be0cSdlg 	ifp->if_flags &= ~IFF_RUNNING;
1139557bb425Sreyk 
1140557bb425Sreyk 	if (tr->tr_stop != NULL)
1141557bb425Sreyk 		(*tr->tr_stop)(tr);
1142557bb425Sreyk }
1143557bb425Sreyk 
114423293512Sdlg void
trunk_input(struct ifnet * ifp,struct mbuf * m)114523293512Sdlg trunk_input(struct ifnet *ifp, struct mbuf *m)
11461daccd23Sreyk {
114723293512Sdlg 	struct arpcom *ac0 = (struct arpcom *)ifp;
11481daccd23Sreyk 	struct trunk_port *tp;
114923293512Sdlg 	struct trunk_softc *tr;
115034210da7Smcbride 	struct ifnet *trifp = NULL;
1151170d586fSmpi 	struct ether_header *eh;
11520ff5889aSmpi 
115323293512Sdlg 	if (m->m_len < sizeof(*eh))
115423293512Sdlg 		goto bad;
115523293512Sdlg 
1156fb492c37Smpi 	eh = mtod(m, struct ether_header *);
11570ff5889aSmpi 	if (ETHER_IS_MULTICAST(eh->ether_dhost))
11580ff5889aSmpi 		ifp->if_imcasts++;
11590ff5889aSmpi 
11601daccd23Sreyk 	/* Should be checked by the caller */
11614d02798dSmpi 	if (ifp->if_type != IFT_IEEE8023ADLAG)
11621daccd23Sreyk 		goto bad;
11634d02798dSmpi 
116423293512Sdlg 	tp = (struct trunk_port *)ac0->ac_trunkport;
1165d1b1f373Smpi 	if ((tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
11661daccd23Sreyk 		goto bad;
11674d02798dSmpi 
11681daccd23Sreyk 	trifp = &tr->tr_ac.ac_if;
11694d02798dSmpi 	if (tr->tr_proto == TRUNK_PROTO_NONE)
1170cb8233adSmpf 		goto bad;
11711daccd23Sreyk 
11720ff5889aSmpi 	if ((*tr->tr_input)(tr, tp, m)) {
11730ff5889aSmpi 		/*
11740ff5889aSmpi 		 * We stop here if the packet has been consumed
11750ff5889aSmpi 		 * by the protocol routine.
11760ff5889aSmpi 		 */
117723293512Sdlg 		return;
11780ff5889aSmpi 	}
117910c5ada1Smpf 
11804d02798dSmpi 	if ((trifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
11814d02798dSmpi 		goto bad;
11824d02798dSmpi 
11838c87278eSmpi 	/*
11848c87278eSmpi 	 * Drop promiscuously received packets if we are not in
11858c87278eSmpi 	 * promiscuous mode.
11868c87278eSmpi 	 */
11878c87278eSmpi 	if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
11888c87278eSmpi 	    (ifp->if_flags & IFF_PROMISC) &&
11898c87278eSmpi 	    (trifp->if_flags & IFF_PROMISC) == 0) {
11908c87278eSmpi 		if (bcmp(&tr->tr_ac.ac_enaddr, eh->ether_dhost,
11918c87278eSmpi 		    ETHER_ADDR_LEN)) {
11928c87278eSmpi 			m_freem(m);
119323293512Sdlg 			return;
11948c87278eSmpi 		}
11958c87278eSmpi 	}
11968c87278eSmpi 
11978c87278eSmpi 
119826415d92Sdlg 	if_vinput(trifp, m);
119923293512Sdlg 	return;
12001daccd23Sreyk 
12011daccd23Sreyk  bad:
12020ff5889aSmpi 	if (trifp != NULL)
12031daccd23Sreyk 		trifp->if_ierrors++;
1204cb8233adSmpf 	m_freem(m);
12051daccd23Sreyk }
12061daccd23Sreyk 
12071daccd23Sreyk int
trunk_media_change(struct ifnet * ifp)12081daccd23Sreyk trunk_media_change(struct ifnet *ifp)
12091daccd23Sreyk {
12101daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
12111daccd23Sreyk 
12121daccd23Sreyk 	if (tr->tr_ifflags & IFF_DEBUG)
12131daccd23Sreyk 		printf("%s\n", __func__);
12141daccd23Sreyk 
12151daccd23Sreyk 	/* Ignore */
12161daccd23Sreyk 	return (0);
12171daccd23Sreyk }
12181daccd23Sreyk 
12191daccd23Sreyk void
trunk_media_status(struct ifnet * ifp,struct ifmediareq * imr)12201daccd23Sreyk trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
12211daccd23Sreyk {
12221daccd23Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
12231daccd23Sreyk 	struct trunk_port *tp;
12241daccd23Sreyk 
12251daccd23Sreyk 	imr->ifm_status = IFM_AVALID;
12261daccd23Sreyk 	imr->ifm_active = IFM_ETHER | IFM_AUTO;
12271daccd23Sreyk 
1228a6a88c36Sbrad 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1229a6a88c36Sbrad 		if (TRUNK_PORTACTIVE(tp))
12301daccd23Sreyk 			imr->ifm_status |= IFM_ACTIVE;
12311daccd23Sreyk 	}
1232a6a88c36Sbrad }
12331daccd23Sreyk 
1234f0a858ccSmcbride void
trunk_port_state(void * arg)12356f1718b9Sreyk trunk_port_state(void *arg)
1236f0a858ccSmcbride {
12376f1718b9Sreyk 	struct trunk_port *tp = (struct trunk_port *)arg;
12386f1718b9Sreyk 	struct trunk_softc *tr = NULL;
1239f0a858ccSmcbride 
12406f1718b9Sreyk 	if (tp != NULL)
1241f0a858ccSmcbride 		tr = (struct trunk_softc *)tp->tp_trunk;
12426f1718b9Sreyk 	if (tr == NULL)
1243f0a858ccSmcbride 		return;
1244557bb425Sreyk 	if (tr->tr_linkstate != NULL)
1245557bb425Sreyk 		(*tr->tr_linkstate)(tp);
1246f0a858ccSmcbride 	trunk_link_active(tr, tp);
1247f0a858ccSmcbride }
1248f0a858ccSmcbride 
12491daccd23Sreyk struct trunk_port *
trunk_link_active(struct trunk_softc * tr,struct trunk_port * tp)12501daccd23Sreyk trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
12511daccd23Sreyk {
1252f0a858ccSmcbride 	struct trunk_port *tp_next, *rval = NULL;
1253c1b90172Sreyk 	int new_link = LINK_STATE_DOWN;
12541daccd23Sreyk 
12551daccd23Sreyk 	/*
12561daccd23Sreyk 	 * Search a port which reports an active link state.
12571daccd23Sreyk 	 */
12581daccd23Sreyk 
12591daccd23Sreyk 	if (tp == NULL)
12601daccd23Sreyk 		goto search;
126150f51b7cSreyk 	if (TRUNK_PORTACTIVE(tp)) {
1262f0a858ccSmcbride 		rval = tp;
1263f0a858ccSmcbride 		goto found;
1264f0a858ccSmcbride 	}
12651daccd23Sreyk 	if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
126650f51b7cSreyk 	    TRUNK_PORTACTIVE(tp_next)) {
1267f0a858ccSmcbride 		rval = tp_next;
1268f0a858ccSmcbride 		goto found;
1269f0a858ccSmcbride 	}
12701daccd23Sreyk 
12711daccd23Sreyk  search:
12721daccd23Sreyk 	SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
127350f51b7cSreyk 		if (TRUNK_PORTACTIVE(tp_next)) {
1274f0a858ccSmcbride 			rval = tp_next;
1275f0a858ccSmcbride 			goto found;
1276f0a858ccSmcbride 		}
12771daccd23Sreyk 	}
12781daccd23Sreyk 
1279f0a858ccSmcbride  found:
1280c1b90172Sreyk 	if (rval != NULL) {
1281c1b90172Sreyk 		/*
1282c1b90172Sreyk 		 * The IEEE 802.1D standard assumes that a trunk with
1283c1b90172Sreyk 		 * multiple ports is always full duplex. This is valid
1284c1b90172Sreyk 		 * for load sharing trunks and if at least two links
1285c1b90172Sreyk 		 * are active. Unfortunately, checking the latter would
1286c1b90172Sreyk 		 * be too expensive at this point.
1287c1b90172Sreyk 		 */
1288c1b90172Sreyk 		if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
1289c1b90172Sreyk 		    (tr->tr_count > 1))
1290c1b90172Sreyk 			new_link = LINK_STATE_FULL_DUPLEX;
1291c1b90172Sreyk 		else
1292c1b90172Sreyk 			new_link = rval->tp_link_state;
1293c1b90172Sreyk 	}
1294f0a858ccSmcbride 
1295f0a858ccSmcbride 	if (tr->tr_ac.ac_if.if_link_state != new_link) {
1296f0a858ccSmcbride 		tr->tr_ac.ac_if.if_link_state = new_link;
1297f0a858ccSmcbride 		if_link_state_change(&tr->tr_ac.ac_if);
1298f0a858ccSmcbride 	}
1299f0a858ccSmcbride 
1300f0a858ccSmcbride 	return (rval);
13011daccd23Sreyk }
13021daccd23Sreyk 
13039f114d03Sbrad const void *
trunk_gethdr(struct mbuf * m,u_int off,u_int len,void * buf)13049f114d03Sbrad trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
13059f114d03Sbrad {
13069f114d03Sbrad 	if (m->m_pkthdr.len < (off + len))
13079f114d03Sbrad 		return (NULL);
13089f114d03Sbrad 	else if (m->m_len < (off + len)) {
13099f114d03Sbrad 		m_copydata(m, off, len, buf);
13109f114d03Sbrad 		return (buf);
13119f114d03Sbrad 	}
1312f780d1a9Sdhill 	return (mtod(m, caddr_t) + off);
13139f114d03Sbrad }
13149f114d03Sbrad 
13151daccd23Sreyk /*
13161daccd23Sreyk  * Simple round robin trunking
13171daccd23Sreyk  */
13181daccd23Sreyk 
13191daccd23Sreyk int
trunk_rr_attach(struct trunk_softc * tr)13201daccd23Sreyk trunk_rr_attach(struct trunk_softc *tr)
13211daccd23Sreyk {
13221daccd23Sreyk 	struct trunk_port *tp;
13231daccd23Sreyk 
13241daccd23Sreyk 	tr->tr_detach = trunk_rr_detach;
13251daccd23Sreyk 	tr->tr_start = trunk_rr_start;
13261daccd23Sreyk 	tr->tr_input = trunk_rr_input;
1327557bb425Sreyk 	tr->tr_init = NULL;
1328557bb425Sreyk 	tr->tr_stop = NULL;
1329053958bcSkrw 	tr->tr_linkstate = NULL;
1330202a2b76Sreyk 	tr->tr_port_create = NULL;
1331202a2b76Sreyk 	tr->tr_port_destroy = trunk_rr_port_destroy;
1332c1b90172Sreyk 	tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1333a14b8f16Smpf 	tr->tr_req = NULL;
1334a14b8f16Smpf 	tr->tr_portreq = NULL;
13351daccd23Sreyk 
13361daccd23Sreyk 	tp = SLIST_FIRST(&tr->tr_ports);
13371daccd23Sreyk 	tr->tr_psc = (caddr_t)tp;
13381daccd23Sreyk 
13391daccd23Sreyk 	return (0);
13401daccd23Sreyk }
13411daccd23Sreyk 
13421daccd23Sreyk int
trunk_rr_detach(struct trunk_softc * tr)13431daccd23Sreyk trunk_rr_detach(struct trunk_softc *tr)
13441daccd23Sreyk {
13451daccd23Sreyk 	tr->tr_psc = NULL;
13461daccd23Sreyk 	return (0);
13471daccd23Sreyk }
13481daccd23Sreyk 
1349202a2b76Sreyk void
trunk_rr_port_destroy(struct trunk_port * tp)1350202a2b76Sreyk trunk_rr_port_destroy(struct trunk_port *tp)
1351202a2b76Sreyk {
1352202a2b76Sreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1353202a2b76Sreyk 
1354202a2b76Sreyk 	if (tp == (struct trunk_port *)tr->tr_psc)
1355202a2b76Sreyk 		tr->tr_psc = NULL;
1356202a2b76Sreyk }
1357202a2b76Sreyk 
13581daccd23Sreyk int
trunk_rr_start(struct trunk_softc * tr,struct mbuf * m)13591daccd23Sreyk trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
13601daccd23Sreyk {
13611daccd23Sreyk 	struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
13621daccd23Sreyk 	int error = 0;
13631daccd23Sreyk 
136423c71058Scanacar 	if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) {
136523c71058Scanacar 		m_freem(m);
13661daccd23Sreyk 		return (ENOENT);
136723c71058Scanacar 	}
13681daccd23Sreyk 
1369c38eb4ffSmpi 	if ((error = if_enqueue(tp->tp_if, m)) != 0)
13701daccd23Sreyk 		return (error);
13711daccd23Sreyk 
13721daccd23Sreyk 	/* Get next active port */
13731daccd23Sreyk 	tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
13741daccd23Sreyk 	tr->tr_psc = (caddr_t)tp_next;
13751daccd23Sreyk 
1376557bb425Sreyk 	return (0);
13771daccd23Sreyk }
13781daccd23Sreyk 
13791daccd23Sreyk int
trunk_rr_input(struct trunk_softc * tr,struct trunk_port * tp,struct mbuf * m)13800ff5889aSmpi trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
13811daccd23Sreyk {
138208b2233fSreyk 	/* Just pass in the packet to our trunk device */
138308b2233fSreyk 	return (0);
138408b2233fSreyk }
138508b2233fSreyk 
138608b2233fSreyk /*
138708b2233fSreyk  * Active failover
138808b2233fSreyk  */
138908b2233fSreyk 
139008b2233fSreyk int
trunk_fail_attach(struct trunk_softc * tr)139108b2233fSreyk trunk_fail_attach(struct trunk_softc *tr)
139208b2233fSreyk {
139308b2233fSreyk 	tr->tr_detach = trunk_fail_detach;
139408b2233fSreyk 	tr->tr_start = trunk_fail_start;
139508b2233fSreyk 	tr->tr_input = trunk_fail_input;
1396557bb425Sreyk 	tr->tr_init = NULL;
1397557bb425Sreyk 	tr->tr_stop = NULL;
139871773c22Smikeb 	tr->tr_port_create = trunk_fail_port_create;
139971773c22Smikeb 	tr->tr_port_destroy = trunk_fail_port_destroy;
140071773c22Smikeb 	tr->tr_linkstate = trunk_fail_linkstate;
1401a14b8f16Smpf 	tr->tr_req = NULL;
1402a14b8f16Smpf 	tr->tr_portreq = NULL;
14031daccd23Sreyk 
140471773c22Smikeb 	/* Get primary or the next active port */
140571773c22Smikeb 	tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
140671773c22Smikeb 
14071daccd23Sreyk 	return (0);
14081daccd23Sreyk }
14091daccd23Sreyk 
14101daccd23Sreyk int
trunk_fail_detach(struct trunk_softc * tr)141108b2233fSreyk trunk_fail_detach(struct trunk_softc *tr)
141208b2233fSreyk {
141371773c22Smikeb 	tr->tr_psc = NULL;
141408b2233fSreyk 	return (0);
141508b2233fSreyk }
141608b2233fSreyk 
141708b2233fSreyk int
trunk_fail_port_create(struct trunk_port * tp)141871773c22Smikeb trunk_fail_port_create(struct trunk_port *tp)
141971773c22Smikeb {
142071773c22Smikeb 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
142171773c22Smikeb 
142271773c22Smikeb 	/* Get primary or the next active port */
142371773c22Smikeb 	tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
142471773c22Smikeb 	return (0);
142571773c22Smikeb }
142671773c22Smikeb 
142771773c22Smikeb void
trunk_fail_port_destroy(struct trunk_port * tp)142871773c22Smikeb trunk_fail_port_destroy(struct trunk_port *tp)
142971773c22Smikeb {
143071773c22Smikeb 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
143171773c22Smikeb 	struct trunk_port *tp_next;
143271773c22Smikeb 
143371773c22Smikeb 	if ((caddr_t)tp == tr->tr_psc) {
143471773c22Smikeb 		/* Get the next active port */
143571773c22Smikeb 		tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
143671773c22Smikeb 		if (tp_next == tp)
143771773c22Smikeb 			tr->tr_psc = NULL;
143871773c22Smikeb 		else
143971773c22Smikeb 			tr->tr_psc = (caddr_t)tp_next;
144071773c22Smikeb 	} else {
144171773c22Smikeb 		/* Get primary or the next active port */
144271773c22Smikeb 		tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
144371773c22Smikeb 	}
144471773c22Smikeb }
144571773c22Smikeb 
144671773c22Smikeb int
trunk_fail_start(struct trunk_softc * tr,struct mbuf * m)144708b2233fSreyk trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
144808b2233fSreyk {
144971773c22Smikeb 	struct trunk_port *tp = (struct trunk_port *)tr->tr_psc;
145008b2233fSreyk 
145108b2233fSreyk 	/* Use the master port if active or the next available port */
145271773c22Smikeb 	if (tp == NULL) {
145323c71058Scanacar 		m_freem(m);
145408b2233fSreyk 		return (ENOENT);
145523c71058Scanacar 	}
145608b2233fSreyk 
1457c38eb4ffSmpi 	return (if_enqueue(tp->tp_if, m));
145808b2233fSreyk }
145908b2233fSreyk 
146008b2233fSreyk int
trunk_fail_input(struct trunk_softc * tr,struct trunk_port * tp,struct mbuf * m)14610ff5889aSmpi trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
14621daccd23Sreyk {
146371773c22Smikeb 	if ((caddr_t)tp == tr->tr_psc)
146410c5ada1Smpf 		return (0);
146525d05b8bSmikeb 	m_freem(m);
146671773c22Smikeb 	return (-1);
146771773c22Smikeb }
146871773c22Smikeb 
146971773c22Smikeb void
trunk_fail_linkstate(struct trunk_port * tp)147071773c22Smikeb trunk_fail_linkstate(struct trunk_port *tp)
147171773c22Smikeb {
147271773c22Smikeb 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
147371773c22Smikeb 
147471773c22Smikeb 	tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
147510c5ada1Smpf }
14765009642bSreyk 
14775009642bSreyk /*
14785009642bSreyk  * Loadbalancing
14795009642bSreyk  */
14805009642bSreyk 
14815009642bSreyk int
trunk_lb_attach(struct trunk_softc * tr)14825009642bSreyk trunk_lb_attach(struct trunk_softc *tr)
14835009642bSreyk {
14845009642bSreyk 	struct trunk_lb *lb;
14855009642bSreyk 
14868b28ab37Shenning 	if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
14875009642bSreyk 		return (ENOMEM);
14885009642bSreyk 
14895009642bSreyk 	tr->tr_detach = trunk_lb_detach;
14905009642bSreyk 	tr->tr_start = trunk_lb_start;
14915009642bSreyk 	tr->tr_input = trunk_lb_input;
14925009642bSreyk 	tr->tr_port_create = trunk_lb_port_create;
14935009642bSreyk 	tr->tr_port_destroy = trunk_lb_port_destroy;
1494557bb425Sreyk 	tr->tr_linkstate = NULL;
1495c1b90172Sreyk 	tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1496a14b8f16Smpf 	tr->tr_req = NULL;
1497a14b8f16Smpf 	tr->tr_portreq = NULL;
1498053958bcSkrw 	tr->tr_init = NULL;
1499053958bcSkrw 	tr->tr_stop = NULL;
15005009642bSreyk 
1501fcf4ed95Stedu 	arc4random_buf(&lb->lb_key, sizeof(lb->lb_key));
15025009642bSreyk 	tr->tr_psc = (caddr_t)lb;
15035009642bSreyk 
15045009642bSreyk 	return (0);
15055009642bSreyk }
15065009642bSreyk 
15075009642bSreyk int
trunk_lb_detach(struct trunk_softc * tr)15085009642bSreyk trunk_lb_detach(struct trunk_softc *tr)
15095009642bSreyk {
15105009642bSreyk 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
15110985b883Sderaadt 
15120985b883Sderaadt 	free(lb, M_DEVBUF, sizeof *lb);
15135009642bSreyk 	return (0);
15145009642bSreyk }
15155009642bSreyk 
15165009642bSreyk int
trunk_lb_porttable(struct trunk_softc * tr,struct trunk_port * tp)15175009642bSreyk trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
15185009642bSreyk {
15195009642bSreyk 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
15205009642bSreyk 	struct trunk_port *tp_next;
15215009642bSreyk 	int i = 0;
15225009642bSreyk 
15235009642bSreyk 	bzero(&lb->lb_ports, sizeof(lb->lb_ports));
15245009642bSreyk 	SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
15255009642bSreyk 		if (tp_next == tp)
15265009642bSreyk 			continue;
15275009642bSreyk 		if (i >= TRUNK_MAX_PORTS)
15285009642bSreyk 			return (EINVAL);
15295009642bSreyk 		if (tr->tr_ifflags & IFF_DEBUG)
15305009642bSreyk 			printf("%s: port %s at index %d\n",
15315009642bSreyk 			    tr->tr_ifname, tp_next->tp_ifname, i);
15325009642bSreyk 		lb->lb_ports[i++] = tp_next;
15335009642bSreyk 	}
15345009642bSreyk 
15355009642bSreyk 	return (0);
15365009642bSreyk }
15375009642bSreyk 
15385009642bSreyk int
trunk_lb_port_create(struct trunk_port * tp)15395009642bSreyk trunk_lb_port_create(struct trunk_port *tp)
15405009642bSreyk {
15415009642bSreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
15425009642bSreyk 	return (trunk_lb_porttable(tr, NULL));
15435009642bSreyk }
15445009642bSreyk 
15455009642bSreyk void
trunk_lb_port_destroy(struct trunk_port * tp)15465009642bSreyk trunk_lb_port_destroy(struct trunk_port *tp)
15475009642bSreyk {
15485009642bSreyk 	struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
15495009642bSreyk 	trunk_lb_porttable(tr, tp);
15505009642bSreyk }
15515009642bSreyk 
15525009642bSreyk int
trunk_lb_start(struct trunk_softc * tr,struct mbuf * m)15535009642bSreyk trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
15545009642bSreyk {
15555009642bSreyk 	struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
15565009642bSreyk 	struct trunk_port *tp = NULL;
15575009642bSreyk 	u_int32_t p = 0;
15585009642bSreyk 
1559fcf4ed95Stedu 	p = trunk_hashmbuf(m, &lb->lb_key);
1560b11fd5b9Sbrad 	p %= tr->tr_count;
1561b11fd5b9Sbrad 	tp = lb->lb_ports[p];
15625009642bSreyk 
15635009642bSreyk 	/*
15645009642bSreyk 	 * Check the port's link state. This will return the next active
15655009642bSreyk 	 * port if the link is down or the port is NULL.
15665009642bSreyk 	 */
156723c71058Scanacar 	if ((tp = trunk_link_active(tr, tp)) == NULL) {
156823c71058Scanacar 		m_freem(m);
15695009642bSreyk 		return (ENOENT);
157023c71058Scanacar 	}
15715009642bSreyk 
1572c38eb4ffSmpi 	return (if_enqueue(tp->tp_if, m));
15735009642bSreyk }
15745009642bSreyk 
15755009642bSreyk int
trunk_lb_input(struct trunk_softc * tr,struct trunk_port * tp,struct mbuf * m)15760ff5889aSmpi trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
15775009642bSreyk {
15785009642bSreyk 	/* Just pass in the packet to our trunk device */
15795009642bSreyk 	return (0);
15805009642bSreyk }
1581c0faeffaSpyr 
1582c0faeffaSpyr /*
1583c0faeffaSpyr  * Broadcast mode
1584c0faeffaSpyr  */
1585c0faeffaSpyr 
1586c0faeffaSpyr int
trunk_bcast_attach(struct trunk_softc * tr)1587c0faeffaSpyr trunk_bcast_attach(struct trunk_softc *tr)
1588c0faeffaSpyr {
1589c0faeffaSpyr 	tr->tr_detach = trunk_bcast_detach;
1590c0faeffaSpyr 	tr->tr_start = trunk_bcast_start;
1591c0faeffaSpyr 	tr->tr_input = trunk_bcast_input;
1592c0faeffaSpyr 	tr->tr_init = NULL;
1593c0faeffaSpyr 	tr->tr_stop = NULL;
1594c0faeffaSpyr 	tr->tr_port_create = NULL;
1595c0faeffaSpyr 	tr->tr_port_destroy = NULL;
1596c0faeffaSpyr 	tr->tr_linkstate = NULL;
1597a14b8f16Smpf 	tr->tr_req = NULL;
1598a14b8f16Smpf 	tr->tr_portreq = NULL;
1599c0faeffaSpyr 
1600c0faeffaSpyr 	return (0);
1601c0faeffaSpyr }
1602c0faeffaSpyr 
1603c0faeffaSpyr int
trunk_bcast_detach(struct trunk_softc * tr)1604c0faeffaSpyr trunk_bcast_detach(struct trunk_softc *tr)
1605c0faeffaSpyr {
1606c0faeffaSpyr 	return (0);
1607c0faeffaSpyr }
1608c0faeffaSpyr 
1609c0faeffaSpyr int
trunk_bcast_start(struct trunk_softc * tr,struct mbuf * m0)1610781eceb7Sdlg trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0)
1611c0faeffaSpyr {
1612c0faeffaSpyr 	int			 active_ports = 0;
1613c0faeffaSpyr 	int			 errors = 0;
1614781eceb7Sdlg 	struct trunk_port	*tp, *last = NULL;
1615781eceb7Sdlg 	struct mbuf		*m;
1616c0faeffaSpyr 
1617c0faeffaSpyr 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1618781eceb7Sdlg 		if (!TRUNK_PORTACTIVE(tp))
1619781eceb7Sdlg 			continue;
1620781eceb7Sdlg 
1621c0faeffaSpyr 		active_ports++;
1622781eceb7Sdlg 
1623781eceb7Sdlg 		if (last != NULL) {
1624781eceb7Sdlg 			m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
1625781eceb7Sdlg 			if (m == NULL) {
1626781eceb7Sdlg 				errors++;
1627781eceb7Sdlg 				break;
1628781eceb7Sdlg 			}
1629781eceb7Sdlg 
16300fd69414Sreyk 			if (if_enqueue(last->tp_if, m) != 0)
1631c0faeffaSpyr 				errors++;
1632c0faeffaSpyr 		}
1633781eceb7Sdlg 		last = tp;
1634c0faeffaSpyr 	}
1635781eceb7Sdlg 	if (last == NULL) {
1636781eceb7Sdlg 		m_freem(m0);
1637c0faeffaSpyr 		return (ENOENT);
1638b865aa54Sreyk 	}
1639781eceb7Sdlg 
16400fd69414Sreyk 	if (if_enqueue(last->tp_if, m0) != 0)
1641781eceb7Sdlg 		errors++;
1642781eceb7Sdlg 
1643c0faeffaSpyr 	if (errors == active_ports)
16440fd69414Sreyk 		return (ENOBUFS);
1645781eceb7Sdlg 
1646c0faeffaSpyr 	return (0);
1647c0faeffaSpyr }
1648c0faeffaSpyr 
1649c0faeffaSpyr int
trunk_bcast_input(struct trunk_softc * tr,struct trunk_port * tp,struct mbuf * m)16500ff5889aSmpi trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1651c0faeffaSpyr {
1652c0faeffaSpyr 	return (0);
1653c0faeffaSpyr }
1654a14b8f16Smpf 
1655a14b8f16Smpf /*
1656a14b8f16Smpf  * 802.3ad LACP
1657a14b8f16Smpf  */
1658a14b8f16Smpf 
1659a14b8f16Smpf int
trunk_lacp_attach(struct trunk_softc * tr)1660a14b8f16Smpf trunk_lacp_attach(struct trunk_softc *tr)
1661a14b8f16Smpf {
1662a14b8f16Smpf 	struct trunk_port *tp;
1663a14b8f16Smpf 	int error;
1664a14b8f16Smpf 
1665a14b8f16Smpf 	tr->tr_detach = trunk_lacp_detach;
1666a14b8f16Smpf 	tr->tr_port_create = lacp_port_create;
1667a14b8f16Smpf 	tr->tr_port_destroy = lacp_port_destroy;
1668a14b8f16Smpf 	tr->tr_linkstate = lacp_linkstate;
1669a14b8f16Smpf 	tr->tr_start = trunk_lacp_start;
1670a14b8f16Smpf 	tr->tr_input = trunk_lacp_input;
1671a14b8f16Smpf 	tr->tr_init = lacp_init;
1672a14b8f16Smpf 	tr->tr_stop = lacp_stop;
1673a14b8f16Smpf 	tr->tr_req = lacp_req;
1674a14b8f16Smpf 	tr->tr_portreq = lacp_portreq;
1675a14b8f16Smpf 
1676a14b8f16Smpf 	error = lacp_attach(tr);
1677a14b8f16Smpf 	if (error)
1678a14b8f16Smpf 		return (error);
1679a14b8f16Smpf 
1680a14b8f16Smpf 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1681a14b8f16Smpf 		lacp_port_create(tp);
1682a14b8f16Smpf 
1683a14b8f16Smpf 	return (error);
1684a14b8f16Smpf }
1685a14b8f16Smpf 
1686a14b8f16Smpf int
trunk_lacp_detach(struct trunk_softc * tr)1687a14b8f16Smpf trunk_lacp_detach(struct trunk_softc *tr)
1688a14b8f16Smpf {
1689a14b8f16Smpf 	struct trunk_port *tp;
1690a14b8f16Smpf 	int error;
1691a14b8f16Smpf 
1692a14b8f16Smpf 	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1693a14b8f16Smpf 		lacp_port_destroy(tp);
1694a14b8f16Smpf 
1695a14b8f16Smpf 	/* unlocking is safe here */
1696a14b8f16Smpf 	error = lacp_detach(tr);
1697a14b8f16Smpf 
1698a14b8f16Smpf 	return (error);
1699a14b8f16Smpf }
1700a14b8f16Smpf 
1701a14b8f16Smpf int
trunk_lacp_start(struct trunk_softc * tr,struct mbuf * m)1702a14b8f16Smpf trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
1703a14b8f16Smpf {
1704a14b8f16Smpf 	struct trunk_port *tp;
1705a14b8f16Smpf 
1706a14b8f16Smpf 	tp = lacp_select_tx_port(tr, m);
1707a14b8f16Smpf 	if (tp == NULL) {
1708a14b8f16Smpf 		m_freem(m);
1709a14b8f16Smpf 		return (EBUSY);
1710a14b8f16Smpf 	}
1711a14b8f16Smpf 
1712c38eb4ffSmpi 	return (if_enqueue(tp->tp_if, m));
1713a14b8f16Smpf }
1714a14b8f16Smpf 
1715a14b8f16Smpf int
trunk_lacp_input(struct trunk_softc * tr,struct trunk_port * tp,struct mbuf * m)17160ff5889aSmpi trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1717a14b8f16Smpf {
17180ff5889aSmpi 	return (lacp_input(tp, m));
1719a14b8f16Smpf }
1720