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, ðer_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, ðer_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