1168793Sthompsa/*	$OpenBSD: if_trunk.c,v 1.30 2007/01/31 06:20:19 reyk Exp $	*/
2168793Sthompsa
3168793Sthompsa/*
4168793Sthompsa * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5174721Sthompsa * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org>
6294615Saraujo * Copyright (c) 2014, 2016 Marcelo Araujo <araujo@FreeBSD.org>
7168793Sthompsa *
8168793Sthompsa * Permission to use, copy, modify, and distribute this software for any
9168793Sthompsa * purpose with or without fee is hereby granted, provided that the above
10168793Sthompsa * copyright notice and this permission notice appear in all copies.
11168793Sthompsa *
12168793Sthompsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13168793Sthompsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14168793Sthompsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15168793Sthompsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16168793Sthompsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17168793Sthompsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18168793Sthompsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19168793Sthompsa */
20168793Sthompsa
21168793Sthompsa#include <sys/cdefs.h>
22168793Sthompsa__FBSDID("$FreeBSD: stable/11/sys/net/if_lagg.c 362366 2020-06-19 06:35:57Z rpokala $");
23168793Sthompsa
24168793Sthompsa#include "opt_inet.h"
25168793Sthompsa#include "opt_inet6.h"
26168793Sthompsa
27168793Sthompsa#include <sys/param.h>
28168793Sthompsa#include <sys/kernel.h>
29168793Sthompsa#include <sys/malloc.h>
30168793Sthompsa#include <sys/mbuf.h>
31168793Sthompsa#include <sys/queue.h>
32168793Sthompsa#include <sys/socket.h>
33168793Sthompsa#include <sys/sockio.h>
34168793Sthompsa#include <sys/sysctl.h>
35168793Sthompsa#include <sys/module.h>
36168793Sthompsa#include <sys/priv.h>
37168793Sthompsa#include <sys/systm.h>
38168793Sthompsa#include <sys/proc.h>
39169569Sthompsa#include <sys/lock.h>
40255038Sadrian#include <sys/rmlock.h>
41318329Smav#include <sys/sx.h>
42169329Sthompsa#include <sys/taskqueue.h>
43203548Seri#include <sys/eventhandler.h>
44168793Sthompsa
45168793Sthompsa#include <net/ethernet.h>
46168793Sthompsa#include <net/if.h>
47168793Sthompsa#include <net/if_clone.h>
48168793Sthompsa#include <net/if_arp.h>
49168793Sthompsa#include <net/if_dl.h>
50168793Sthompsa#include <net/if_media.h>
51168793Sthompsa#include <net/if_types.h>
52168793Sthompsa#include <net/if_var.h>
53168793Sthompsa#include <net/bpf.h>
54272386Shrs#include <net/vnet.h>
55168793Sthompsa
56221130Sbz#if defined(INET) || defined(INET6)
57221130Sbz#include <netinet/in.h>
58264498Srmacklem#include <netinet/ip.h>
59221130Sbz#endif
60168793Sthompsa#ifdef INET
61168793Sthompsa#include <netinet/in_systm.h>
62168793Sthompsa#include <netinet/if_ether.h>
63168793Sthompsa#endif
64168793Sthompsa
65168793Sthompsa#ifdef INET6
66168793Sthompsa#include <netinet/ip6.h>
67252511Shrs#include <netinet6/in6_var.h>
68252511Shrs#include <netinet6/in6_ifattach.h>
69168793Sthompsa#endif
70168793Sthompsa
71168793Sthompsa#include <net/if_vlan_var.h>
72168793Sthompsa#include <net/if_lagg.h>
73168793Sthompsa#include <net/ieee8023ad_lacp.h>
74168793Sthompsa
75168793Sthompsa/* Special flags we should propagate to the lagg ports. */
76168793Sthompsastatic struct {
77168793Sthompsa	int flag;
78168793Sthompsa	int (*func)(struct ifnet *, int);
79168793Sthompsa} lagg_pflags[] = {
80168793Sthompsa	{IFF_PROMISC, ifpromisc},
81168793Sthompsa	{IFF_ALLMULTI, if_allmulti},
82168793Sthompsa	{0, NULL}
83168793Sthompsa};
84168793Sthompsa
85272386ShrsVNET_DEFINE(SLIST_HEAD(__trhead, lagg_softc), lagg_list); /* list of laggs */
86272386Shrs#define	V_lagg_list	VNET(lagg_list)
87272386Shrsstatic VNET_DEFINE(struct mtx, lagg_list_mtx);
88272386Shrs#define	V_lagg_list_mtx	VNET(lagg_list_mtx)
89272386Shrs#define	LAGG_LIST_LOCK_INIT(x)		mtx_init(&V_lagg_list_mtx, \
90272386Shrs					"if_lagg list", NULL, MTX_DEF)
91272386Shrs#define	LAGG_LIST_LOCK_DESTROY(x)	mtx_destroy(&V_lagg_list_mtx)
92272386Shrs#define	LAGG_LIST_LOCK(x)		mtx_lock(&V_lagg_list_mtx)
93272386Shrs#define	LAGG_LIST_UNLOCK(x)		mtx_unlock(&V_lagg_list_mtx)
94168793Sthompsaeventhandler_tag	lagg_detach_cookie = NULL;
95168793Sthompsa
96168793Sthompsastatic int	lagg_clone_create(struct if_clone *, int, caddr_t);
97168793Sthompsastatic void	lagg_clone_destroy(struct ifnet *);
98272386Shrsstatic VNET_DEFINE(struct if_clone *, lagg_cloner);
99272386Shrs#define	V_lagg_cloner	VNET(lagg_cloner)
100241610Sglebiusstatic const char laggname[] = "lagg";
101241610Sglebius
102171661Sthompsastatic void	lagg_capabilities(struct lagg_softc *);
103168793Sthompsastatic int	lagg_port_create(struct lagg_softc *, struct ifnet *);
104168793Sthompsastatic int	lagg_port_destroy(struct lagg_port *, int);
105168793Sthompsastatic struct mbuf *lagg_input(struct ifnet *, struct mbuf *);
106173895Sthompsastatic void	lagg_linkstate(struct lagg_softc *);
107292402Ssmhstatic void	lagg_port_state(struct ifnet *, int);
108168793Sthompsastatic int	lagg_port_ioctl(struct ifnet *, u_long, caddr_t);
109168793Sthompsastatic int	lagg_port_output(struct ifnet *, struct mbuf *,
110249925Sglebius		    const struct sockaddr *, struct route *);
111168793Sthompsastatic void	lagg_port_ifdetach(void *arg __unused, struct ifnet *);
112201803Strasz#ifdef LAGG_PORT_STACKING
113168793Sthompsastatic int	lagg_port_checkstacking(struct lagg_softc *);
114201803Strasz#endif
115168793Sthompsastatic void	lagg_port2req(struct lagg_port *, struct lagg_reqport *);
116168793Sthompsastatic void	lagg_init(void *);
117168793Sthompsastatic void	lagg_stop(struct lagg_softc *);
118168793Sthompsastatic int	lagg_ioctl(struct ifnet *, u_long, caddr_t);
119318329Smavstatic int	lagg_setmulti(struct lagg_port *);
120318329Smavstatic int	lagg_clrmulti(struct lagg_port *);
121318329Smavstatic	int	lagg_setcaps(struct lagg_port *, int cap);
122168793Sthompsastatic	int	lagg_setflag(struct lagg_port *, int, int,
123168793Sthompsa		    int (*func)(struct ifnet *, int));
124168793Sthompsastatic	int	lagg_setflags(struct lagg_port *, int status);
125272211Smelifarostatic uint64_t lagg_get_counter(struct ifnet *ifp, ift_counter cnt);
126240742Sglebiusstatic int	lagg_transmit(struct ifnet *, struct mbuf *);
127240742Sglebiusstatic void	lagg_qflush(struct ifnet *);
128168793Sthompsastatic int	lagg_media_change(struct ifnet *);
129168793Sthompsastatic void	lagg_media_status(struct ifnet *, struct ifmediareq *);
130168793Sthompsastatic struct lagg_port *lagg_link_active(struct lagg_softc *,
131168793Sthompsa	    struct lagg_port *);
132168793Sthompsa
133168793Sthompsa/* Simple round robin */
134272161Sglebiusstatic void	lagg_rr_attach(struct lagg_softc *);
135168793Sthompsastatic int	lagg_rr_start(struct lagg_softc *, struct mbuf *);
136168793Sthompsastatic struct mbuf *lagg_rr_input(struct lagg_softc *, struct lagg_port *,
137168793Sthompsa		    struct mbuf *);
138168793Sthompsa
139168793Sthompsa/* Active failover */
140168793Sthompsastatic int	lagg_fail_start(struct lagg_softc *, struct mbuf *);
141168793Sthompsastatic struct mbuf *lagg_fail_input(struct lagg_softc *, struct lagg_port *,
142168793Sthompsa		    struct mbuf *);
143168793Sthompsa
144168793Sthompsa/* Loadbalancing */
145272161Sglebiusstatic void	lagg_lb_attach(struct lagg_softc *);
146272158Sglebiusstatic void	lagg_lb_detach(struct lagg_softc *);
147168793Sthompsastatic int	lagg_lb_port_create(struct lagg_port *);
148168793Sthompsastatic void	lagg_lb_port_destroy(struct lagg_port *);
149168793Sthompsastatic int	lagg_lb_start(struct lagg_softc *, struct mbuf *);
150168793Sthompsastatic struct mbuf *lagg_lb_input(struct lagg_softc *, struct lagg_port *,
151168793Sthompsa		    struct mbuf *);
152168793Sthompsastatic int	lagg_lb_porttable(struct lagg_softc *, struct lagg_port *);
153168793Sthompsa
154271732Saraujo/* Broadcast */
155271732Saraujostatic int    lagg_bcast_start(struct lagg_softc *, struct mbuf *);
156271732Saraujostatic struct mbuf *lagg_bcast_input(struct lagg_softc *, struct lagg_port *,
157272175Sglebius		    struct mbuf *);
158271732Saraujo
159168793Sthompsa/* 802.3ad LACP */
160272161Sglebiusstatic void	lagg_lacp_attach(struct lagg_softc *);
161272158Sglebiusstatic void	lagg_lacp_detach(struct lagg_softc *);
162168793Sthompsastatic int	lagg_lacp_start(struct lagg_softc *, struct mbuf *);
163168793Sthompsastatic struct mbuf *lagg_lacp_input(struct lagg_softc *, struct lagg_port *,
164168793Sthompsa		    struct mbuf *);
165168793Sthompsastatic void	lagg_lacp_lladdr(struct lagg_softc *);
166168793Sthompsa
167168793Sthompsa/* lagg protocol table */
168272161Sglebiusstatic const struct lagg_proto {
169272170Sglebius	lagg_proto	pr_num;
170272170Sglebius	void		(*pr_attach)(struct lagg_softc *);
171272170Sglebius	void		(*pr_detach)(struct lagg_softc *);
172272178Sglebius	int		(*pr_start)(struct lagg_softc *, struct mbuf *);
173272178Sglebius	struct mbuf *	(*pr_input)(struct lagg_softc *, struct lagg_port *,
174272178Sglebius			    struct mbuf *);
175272178Sglebius	int		(*pr_addport)(struct lagg_port *);
176272178Sglebius	void		(*pr_delport)(struct lagg_port *);
177272178Sglebius	void		(*pr_linkstate)(struct lagg_port *);
178272178Sglebius	void 		(*pr_init)(struct lagg_softc *);
179272178Sglebius	void 		(*pr_stop)(struct lagg_softc *);
180272178Sglebius	void 		(*pr_lladdr)(struct lagg_softc *);
181272178Sglebius	void		(*pr_request)(struct lagg_softc *, void *);
182272178Sglebius	void		(*pr_portreq)(struct lagg_port *, void *);
183168793Sthompsa} lagg_protos[] = {
184272170Sglebius    {
185272170Sglebius	.pr_num = LAGG_PROTO_NONE
186272170Sglebius    },
187272170Sglebius    {
188272170Sglebius	.pr_num = LAGG_PROTO_ROUNDROBIN,
189272170Sglebius	.pr_attach = lagg_rr_attach,
190272178Sglebius	.pr_start = lagg_rr_start,
191272178Sglebius	.pr_input = lagg_rr_input,
192272170Sglebius    },
193272170Sglebius    {
194272170Sglebius	.pr_num = LAGG_PROTO_FAILOVER,
195272178Sglebius	.pr_start = lagg_fail_start,
196272178Sglebius	.pr_input = lagg_fail_input,
197272170Sglebius    },
198272170Sglebius    {
199272170Sglebius	.pr_num = LAGG_PROTO_LOADBALANCE,
200272170Sglebius	.pr_attach = lagg_lb_attach,
201272170Sglebius	.pr_detach = lagg_lb_detach,
202272178Sglebius	.pr_start = lagg_lb_start,
203272178Sglebius	.pr_input = lagg_lb_input,
204272178Sglebius	.pr_addport = lagg_lb_port_create,
205272178Sglebius	.pr_delport = lagg_lb_port_destroy,
206272170Sglebius    },
207272170Sglebius    {
208272170Sglebius	.pr_num = LAGG_PROTO_LACP,
209272170Sglebius	.pr_attach = lagg_lacp_attach,
210272170Sglebius	.pr_detach = lagg_lacp_detach,
211272178Sglebius	.pr_start = lagg_lacp_start,
212272178Sglebius	.pr_input = lagg_lacp_input,
213272178Sglebius	.pr_addport = lacp_port_create,
214272178Sglebius	.pr_delport = lacp_port_destroy,
215272178Sglebius	.pr_linkstate = lacp_linkstate,
216272178Sglebius	.pr_init = lacp_init,
217272178Sglebius	.pr_stop = lacp_stop,
218272178Sglebius	.pr_lladdr = lagg_lacp_lladdr,
219272178Sglebius	.pr_request = lacp_req,
220272178Sglebius	.pr_portreq = lacp_portreq,
221272170Sglebius    },
222272170Sglebius    {
223272170Sglebius	.pr_num = LAGG_PROTO_BROADCAST,
224272178Sglebius	.pr_start = lagg_bcast_start,
225272178Sglebius	.pr_input = lagg_bcast_input,
226272170Sglebius    },
227168793Sthompsa};
228168793Sthompsa
229212100SemasteSYSCTL_DECL(_net_link);
230253314SadrianSYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0,
231227309Sed    "Link Aggregation");
232212100Semaste
233272386Shrs/* Allow input on any failover links */
234272386Shrsstatic VNET_DEFINE(int, lagg_failover_rx_all);
235272386Shrs#define	V_lagg_failover_rx_all	VNET(lagg_failover_rx_all)
236272386ShrsSYSCTL_INT(_net_link_lagg, OID_AUTO, failover_rx_all, CTLFLAG_RW | CTLFLAG_VNET,
237272386Shrs    &VNET_NAME(lagg_failover_rx_all), 0,
238212100Semaste    "Accept input from any interface in a failover lagg");
239272386Shrs
240275358Shselasky/* Default value for using flowid */
241332318Ssmhstatic VNET_DEFINE(int, def_use_flowid) = 0;
242272386Shrs#define	V_def_use_flowid	VNET(def_use_flowid)
243267992ShselaskySYSCTL_INT(_net_link_lagg, OID_AUTO, default_use_flowid, CTLFLAG_RWTUN,
244272386Shrs    &VNET_NAME(def_use_flowid), 0,
245232080Sthompsa    "Default setting for using flow id for load sharing");
246272386Shrs
247275358Shselasky/* Default value for flowid shift */
248272386Shrsstatic VNET_DEFINE(int, def_flowid_shift) = 16;
249272386Shrs#define	V_def_flowid_shift	VNET(def_flowid_shift)
250267992ShselaskySYSCTL_INT(_net_link_lagg, OID_AUTO, default_flowid_shift, CTLFLAG_RWTUN,
251272386Shrs    &VNET_NAME(def_flowid_shift), 0,
252260070Sscottl    "Default setting for flowid shift for load sharing");
253212100Semaste
254272386Shrsstatic void
255272386Shrsvnet_lagg_init(const void *unused __unused)
256272386Shrs{
257272386Shrs
258272386Shrs	LAGG_LIST_LOCK_INIT();
259272386Shrs	SLIST_INIT(&V_lagg_list);
260272386Shrs	V_lagg_cloner = if_clone_simple(laggname, lagg_clone_create,
261272386Shrs	    lagg_clone_destroy, 0);
262272386Shrs}
263272386ShrsVNET_SYSINIT(vnet_lagg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
264272386Shrs    vnet_lagg_init, NULL);
265272386Shrs
266272386Shrsstatic void
267272386Shrsvnet_lagg_uninit(const void *unused __unused)
268272386Shrs{
269272386Shrs
270272386Shrs	if_clone_detach(V_lagg_cloner);
271272386Shrs	LAGG_LIST_LOCK_DESTROY();
272272386Shrs}
273302054SbzVNET_SYSUNINIT(vnet_lagg_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY,
274272386Shrs    vnet_lagg_uninit, NULL);
275272386Shrs
276168793Sthompsastatic int
277168793Sthompsalagg_modevent(module_t mod, int type, void *data)
278168793Sthompsa{
279168793Sthompsa
280168793Sthompsa	switch (type) {
281168793Sthompsa	case MOD_LOAD:
282168793Sthompsa		lagg_input_p = lagg_input;
283168793Sthompsa		lagg_linkstate_p = lagg_port_state;
284168793Sthompsa		lagg_detach_cookie = EVENTHANDLER_REGISTER(
285168793Sthompsa		    ifnet_departure_event, lagg_port_ifdetach, NULL,
286168793Sthompsa		    EVENTHANDLER_PRI_ANY);
287168793Sthompsa		break;
288168793Sthompsa	case MOD_UNLOAD:
289168793Sthompsa		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
290168793Sthompsa		    lagg_detach_cookie);
291168793Sthompsa		lagg_input_p = NULL;
292168793Sthompsa		lagg_linkstate_p = NULL;
293168793Sthompsa		break;
294168793Sthompsa	default:
295168793Sthompsa		return (EOPNOTSUPP);
296168793Sthompsa	}
297168793Sthompsa	return (0);
298168793Sthompsa}
299168793Sthompsa
300168793Sthompsastatic moduledata_t lagg_mod = {
301168793Sthompsa	"if_lagg",
302168793Sthompsa	lagg_modevent,
303241394Skevlo	0
304168793Sthompsa};
305168793Sthompsa
306168793SthompsaDECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
307224571SpluknetMODULE_VERSION(if_lagg, 1);
308168793Sthompsa
309272170Sglebiusstatic void
310272170Sglebiuslagg_proto_attach(struct lagg_softc *sc, lagg_proto pr)
311272170Sglebius{
312272170Sglebius
313318329Smav	LAGG_XLOCK_ASSERT(sc);
314272170Sglebius	KASSERT(sc->sc_proto == LAGG_PROTO_NONE, ("%s: sc %p has proto",
315272170Sglebius	    __func__, sc));
316272170Sglebius
317272170Sglebius	if (sc->sc_ifflags & IFF_DEBUG)
318272170Sglebius		if_printf(sc->sc_ifp, "using proto %u\n", pr);
319272170Sglebius
320272178Sglebius	if (lagg_protos[pr].pr_attach != NULL)
321272178Sglebius		lagg_protos[pr].pr_attach(sc);
322272170Sglebius	sc->sc_proto = pr;
323272170Sglebius}
324272170Sglebius
325272170Sglebiusstatic void
326272170Sglebiuslagg_proto_detach(struct lagg_softc *sc)
327272170Sglebius{
328272170Sglebius	lagg_proto pr;
329272170Sglebius
330318329Smav	LAGG_XLOCK_ASSERT(sc);
331272170Sglebius	LAGG_WLOCK_ASSERT(sc);
332272170Sglebius	pr = sc->sc_proto;
333272175Sglebius	sc->sc_proto = LAGG_PROTO_NONE;
334272170Sglebius
335272170Sglebius	if (lagg_protos[pr].pr_detach != NULL)
336272170Sglebius		lagg_protos[pr].pr_detach(sc);
337272170Sglebius	else
338272170Sglebius		LAGG_WUNLOCK(sc);
339272170Sglebius}
340272170Sglebius
341272178Sglebiusstatic int
342272178Sglebiuslagg_proto_start(struct lagg_softc *sc, struct mbuf *m)
343272178Sglebius{
344272178Sglebius
345272178Sglebius	return (lagg_protos[sc->sc_proto].pr_start(sc, m));
346272178Sglebius}
347272178Sglebius
348272178Sglebiusstatic struct mbuf *
349272178Sglebiuslagg_proto_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
350272178Sglebius{
351272178Sglebius
352272178Sglebius	return (lagg_protos[sc->sc_proto].pr_input(sc, lp, m));
353272178Sglebius}
354272178Sglebius
355272178Sglebiusstatic int
356272178Sglebiuslagg_proto_addport(struct lagg_softc *sc, struct lagg_port *lp)
357272178Sglebius{
358272178Sglebius
359272178Sglebius	if (lagg_protos[sc->sc_proto].pr_addport == NULL)
360272178Sglebius		return (0);
361272178Sglebius	else
362272178Sglebius		return (lagg_protos[sc->sc_proto].pr_addport(lp));
363272178Sglebius}
364272178Sglebius
365272178Sglebiusstatic void
366272178Sglebiuslagg_proto_delport(struct lagg_softc *sc, struct lagg_port *lp)
367272178Sglebius{
368272178Sglebius
369272178Sglebius	if (lagg_protos[sc->sc_proto].pr_delport != NULL)
370272178Sglebius		lagg_protos[sc->sc_proto].pr_delport(lp);
371272178Sglebius}
372272178Sglebius
373272178Sglebiusstatic void
374272178Sglebiuslagg_proto_linkstate(struct lagg_softc *sc, struct lagg_port *lp)
375272178Sglebius{
376272178Sglebius
377272178Sglebius	if (lagg_protos[sc->sc_proto].pr_linkstate != NULL)
378272178Sglebius		lagg_protos[sc->sc_proto].pr_linkstate(lp);
379272178Sglebius}
380272178Sglebius
381272178Sglebiusstatic void
382272178Sglebiuslagg_proto_init(struct lagg_softc *sc)
383272178Sglebius{
384272178Sglebius
385272178Sglebius	if (lagg_protos[sc->sc_proto].pr_init != NULL)
386272178Sglebius		lagg_protos[sc->sc_proto].pr_init(sc);
387272178Sglebius}
388272178Sglebius
389272178Sglebiusstatic void
390272178Sglebiuslagg_proto_stop(struct lagg_softc *sc)
391272178Sglebius{
392272178Sglebius
393272178Sglebius	if (lagg_protos[sc->sc_proto].pr_stop != NULL)
394272178Sglebius		lagg_protos[sc->sc_proto].pr_stop(sc);
395272178Sglebius}
396272178Sglebius
397272178Sglebiusstatic void
398272178Sglebiuslagg_proto_lladdr(struct lagg_softc *sc)
399272178Sglebius{
400272178Sglebius
401272178Sglebius	if (lagg_protos[sc->sc_proto].pr_lladdr != NULL)
402272178Sglebius		lagg_protos[sc->sc_proto].pr_lladdr(sc);
403272178Sglebius}
404272178Sglebius
405272178Sglebiusstatic void
406272178Sglebiuslagg_proto_request(struct lagg_softc *sc, void *v)
407272178Sglebius{
408272178Sglebius
409272178Sglebius	if (lagg_protos[sc->sc_proto].pr_request != NULL)
410272178Sglebius		lagg_protos[sc->sc_proto].pr_request(sc, v);
411272178Sglebius}
412272178Sglebius
413272178Sglebiusstatic void
414272178Sglebiuslagg_proto_portreq(struct lagg_softc *sc, struct lagg_port *lp, void *v)
415272178Sglebius{
416272178Sglebius
417272178Sglebius	if (lagg_protos[sc->sc_proto].pr_portreq != NULL)
418272178Sglebius		lagg_protos[sc->sc_proto].pr_portreq(lp, v);
419272178Sglebius}
420272178Sglebius
421203548Seri/*
422203548Seri * This routine is run via an vlan
423203548Seri * config EVENT
424203548Seri */
425203548Seristatic void
426203548Serilagg_register_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag)
427203548Seri{
428272175Sglebius	struct lagg_softc *sc = ifp->if_softc;
429272175Sglebius	struct lagg_port *lp;
430203548Seri
431272175Sglebius	if (ifp->if_softc !=  arg)   /* Not our event */
432272175Sglebius		return;
433203548Seri
434318329Smav	LAGG_SLOCK(sc);
435317699Smav	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
436317699Smav		EVENTHANDLER_INVOKE(vlan_config, lp->lp_ifp, vtag);
437318329Smav	LAGG_SUNLOCK(sc);
438203548Seri}
439203548Seri
440203548Seri/*
441203548Seri * This routine is run via an vlan
442203548Seri * unconfig EVENT
443203548Seri */
444203548Seristatic void
445203548Serilagg_unregister_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag)
446203548Seri{
447272175Sglebius	struct lagg_softc *sc = ifp->if_softc;
448272175Sglebius	struct lagg_port *lp;
449203548Seri
450272175Sglebius	if (ifp->if_softc !=  arg)   /* Not our event */
451272175Sglebius		return;
452203548Seri
453318329Smav	LAGG_SLOCK(sc);
454317699Smav	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
455317699Smav		EVENTHANDLER_INVOKE(vlan_unconfig, lp->lp_ifp, vtag);
456318329Smav	LAGG_SUNLOCK(sc);
457203548Seri}
458203548Seri
459168793Sthompsastatic int
460168793Sthompsalagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
461168793Sthompsa{
462168793Sthompsa	struct lagg_softc *sc;
463168793Sthompsa	struct ifnet *ifp;
464168793Sthompsa	static const u_char eaddr[6];	/* 00:00:00:00:00:00 */
465168793Sthompsa
466168793Sthompsa	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
467168793Sthompsa	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
468168793Sthompsa	if (ifp == NULL) {
469168793Sthompsa		free(sc, M_DEVBUF);
470168793Sthompsa		return (ENOSPC);
471168793Sthompsa	}
472318329Smav	LAGG_LOCK_INIT(sc);
473318329Smav	LAGG_SX_INIT(sc);
474168793Sthompsa
475318329Smav	LAGG_XLOCK(sc);
476272386Shrs	if (V_def_use_flowid)
477272386Shrs		sc->sc_opts |= LAGG_OPT_USE_FLOWID;
478272386Shrs	sc->flowid_shift = V_def_flowid_shift;
479272386Shrs
480232629Sthompsa	/* Hash all layers by default */
481279891Shselasky	sc->sc_flags = MBUF_HASHFLAG_L2|MBUF_HASHFLAG_L3|MBUF_HASHFLAG_L4;
482232008Sthompsa
483272170Sglebius	lagg_proto_attach(sc, LAGG_PROTO_DEFAULT);
484272170Sglebius
485168793Sthompsa	SLIST_INIT(&sc->sc_ports);
486168793Sthompsa
487168793Sthompsa	/* Initialise pseudo media types */
488168793Sthompsa	ifmedia_init(&sc->sc_media, 0, lagg_media_change,
489168793Sthompsa	    lagg_media_status);
490168793Sthompsa	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
491168793Sthompsa	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
492168793Sthompsa
493241610Sglebius	if_initname(ifp, laggname, unit);
494168793Sthompsa	ifp->if_softc = sc;
495240742Sglebius	ifp->if_transmit = lagg_transmit;
496240742Sglebius	ifp->if_qflush = lagg_qflush;
497168793Sthompsa	ifp->if_init = lagg_init;
498168793Sthompsa	ifp->if_ioctl = lagg_ioctl;
499272211Smelifaro	ifp->if_get_counter = lagg_get_counter;
500168793Sthompsa	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
501256218Sglebius	ifp->if_capenable = ifp->if_capabilities = IFCAP_HWSTATS;
502168793Sthompsa
503168793Sthompsa	/*
504227459Sbrooks	 * Attach as an ordinary ethernet device, children will be attached
505168793Sthompsa	 * as special device IFT_IEEE8023ADLAG.
506168793Sthompsa	 */
507168793Sthompsa	ether_ifattach(ifp, eaddr);
508168793Sthompsa
509203548Seri	sc->vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
510203548Seri		lagg_register_vlan, sc, EVENTHANDLER_PRI_FIRST);
511203548Seri	sc->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
512203548Seri		lagg_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST);
513203548Seri
514168793Sthompsa	/* Insert into the global list of laggs */
515272386Shrs	LAGG_LIST_LOCK();
516272386Shrs	SLIST_INSERT_HEAD(&V_lagg_list, sc, sc_entries);
517272386Shrs	LAGG_LIST_UNLOCK();
518318329Smav	LAGG_XUNLOCK(sc);
519168793Sthompsa
520168793Sthompsa	return (0);
521168793Sthompsa}
522168793Sthompsa
523168793Sthompsastatic void
524168793Sthompsalagg_clone_destroy(struct ifnet *ifp)
525168793Sthompsa{
526168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
527168793Sthompsa	struct lagg_port *lp;
528168793Sthompsa
529318329Smav	LAGG_XLOCK(sc);
530318329Smav	sc->sc_destroying = 1;
531168793Sthompsa	lagg_stop(sc);
532168793Sthompsa	ifp->if_flags &= ~IFF_UP;
533168793Sthompsa
534203548Seri	EVENTHANDLER_DEREGISTER(vlan_config, sc->vlan_attach);
535203548Seri	EVENTHANDLER_DEREGISTER(vlan_unconfig, sc->vlan_detach);
536203548Seri
537168793Sthompsa	/* Shutdown and remove lagg ports */
538318329Smav	while ((lp = SLIST_FIRST(&sc->sc_ports)) != NULL)
539168793Sthompsa		lagg_port_destroy(lp, 1);
540318329Smav
541168793Sthompsa	/* Unhook the aggregation protocol */
542318329Smav	LAGG_WLOCK(sc);
543272170Sglebius	lagg_proto_detach(sc);
544290239Smelifaro	LAGG_UNLOCK_ASSERT(sc);
545318329Smav	LAGG_XUNLOCK(sc);
546168793Sthompsa
547168793Sthompsa	ifmedia_removeall(&sc->sc_media);
548168793Sthompsa	ether_ifdetach(ifp);
549227459Sbrooks	if_free(ifp);
550168793Sthompsa
551272386Shrs	LAGG_LIST_LOCK();
552272386Shrs	SLIST_REMOVE(&V_lagg_list, sc, lagg_softc, sc_entries);
553272386Shrs	LAGG_LIST_UNLOCK();
554168793Sthompsa
555318329Smav	LAGG_SX_DESTROY(sc);
556168793Sthompsa	LAGG_LOCK_DESTROY(sc);
557168793Sthompsa	free(sc, M_DEVBUF);
558168793Sthompsa}
559168793Sthompsa
560171661Sthompsastatic void
561168793Sthompsalagg_capabilities(struct lagg_softc *sc)
562168793Sthompsa{
563168793Sthompsa	struct lagg_port *lp;
564319696Smav	int cap, ena, pena;
565319696Smav	uint64_t hwa;
566271946Shselasky	struct ifnet_hw_tsomax hw_tsomax;
567168793Sthompsa
568318329Smav	LAGG_XLOCK_ASSERT(sc);
569168793Sthompsa
570319696Smav	/* Get common enabled capabilities for the lagg ports */
571319696Smav	ena = ~0;
572319696Smav	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
573319696Smav		ena &= lp->lp_ifp->if_capenable;
574319696Smav	ena = (ena == ~0 ? 0 : ena);
575319696Smav
576319696Smav	/*
577319696Smav	 * Apply common enabled capabilities back to the lagg ports.
578319696Smav	 * May require several iterations if they are dependent.
579319696Smav	 */
580319696Smav	do {
581319696Smav		pena = ena;
582319696Smav		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
583319696Smav			lagg_setcaps(lp, ena);
584319696Smav			ena &= lp->lp_ifp->if_capenable;
585319696Smav		}
586319696Smav	} while (pena != ena);
587319696Smav
588319696Smav	/* Get other capabilities from the lagg ports */
589319696Smav	cap = ~0;
590319696Smav	hwa = ~(uint64_t)0;
591271946Shselasky	memset(&hw_tsomax, 0, sizeof(hw_tsomax));
592171661Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
593171661Sthompsa		cap &= lp->lp_ifp->if_capabilities;
594186195Sthompsa		hwa &= lp->lp_ifp->if_hwassist;
595271946Shselasky		if_hw_tsomax_common(lp->lp_ifp, &hw_tsomax);
596171661Sthompsa	}
597171661Sthompsa	cap = (cap == ~0 ? 0 : cap);
598319696Smav	hwa = (hwa == ~(uint64_t)0 ? 0 : hwa);
599168793Sthompsa
600171661Sthompsa	if (sc->sc_ifp->if_capabilities != cap ||
601186195Sthompsa	    sc->sc_ifp->if_capenable != ena ||
602264469Srmacklem	    sc->sc_ifp->if_hwassist != hwa ||
603271946Shselasky	    if_hw_tsomax_update(sc->sc_ifp, &hw_tsomax) != 0) {
604171661Sthompsa		sc->sc_ifp->if_capabilities = cap;
605171661Sthompsa		sc->sc_ifp->if_capenable = ena;
606186195Sthompsa		sc->sc_ifp->if_hwassist = hwa;
607171661Sthompsa		getmicrotime(&sc->sc_ifp->if_lastchange);
608171661Sthompsa
609171661Sthompsa		if (sc->sc_ifflags & IFF_DEBUG)
610171661Sthompsa			if_printf(sc->sc_ifp,
611171661Sthompsa			    "capabilities 0x%08x enabled 0x%08x\n", cap, ena);
612168793Sthompsa	}
613168793Sthompsa}
614168793Sthompsa
615168793Sthompsastatic int
616168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
617168793Sthompsa{
618168793Sthompsa	struct lagg_softc *sc_ptr;
619272176Sae	struct lagg_port *lp, *tlp;
620342206Smav	struct ifreq ifr;
621342206Smav	int error, i, oldmtu;
622272211Smelifaro	uint64_t *pval;
623168793Sthompsa
624318329Smav	LAGG_XLOCK_ASSERT(sc);
625168793Sthompsa
626342206Smav	if (sc->sc_ifp == ifp) {
627342206Smav		if_printf(sc->sc_ifp,
628342206Smav		    "cannot add a lagg to itself as a port\n");
629342206Smav		return (EINVAL);
630342206Smav	}
631342206Smav
632168793Sthompsa	/* Limit the maximal number of lagg ports */
633168793Sthompsa	if (sc->sc_count >= LAGG_MAX_PORTS)
634168793Sthompsa		return (ENOSPC);
635168793Sthompsa
636168793Sthompsa	/* Check if port has already been associated to a lagg */
637236178Srea	if (ifp->if_lagg != NULL) {
638236178Srea		/* Port is already in the current lagg? */
639236178Srea		lp = (struct lagg_port *)ifp->if_lagg;
640236178Srea		if (lp->lp_softc == sc)
641236178Srea			return (EEXIST);
642168793Sthompsa		return (EBUSY);
643236178Srea	}
644168793Sthompsa
645168793Sthompsa	/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
646290239Smelifaro	if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN)
647168793Sthompsa		return (EPROTONOSUPPORT);
648168793Sthompsa
649171661Sthompsa	/* Allow the first Ethernet member to define the MTU */
650342206Smav	oldmtu = -1;
651342206Smav	if (SLIST_EMPTY(&sc->sc_ports)) {
652171661Sthompsa		sc->sc_ifp->if_mtu = ifp->if_mtu;
653342206Smav	} else if (sc->sc_ifp->if_mtu != ifp->if_mtu) {
654342206Smav		if (ifp->if_ioctl == NULL) {
655342206Smav			if_printf(sc->sc_ifp, "cannot change MTU for %s\n",
656342206Smav			    ifp->if_xname);
657342206Smav			return (EINVAL);
658342206Smav		}
659342206Smav		oldmtu = ifp->if_mtu;
660342206Smav		strlcpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name));
661342206Smav		ifr.ifr_mtu = sc->sc_ifp->if_mtu;
662342206Smav		error = (*ifp->if_ioctl)(ifp, SIOCSIFMTU, (caddr_t)&ifr);
663342206Smav		if (error != 0) {
664342206Smav			if_printf(sc->sc_ifp, "invalid MTU for %s\n",
665342206Smav			    ifp->if_xname);
666342206Smav			return (error);
667342206Smav		}
668342206Smav		ifr.ifr_mtu = oldmtu;
669171661Sthompsa	}
670171661Sthompsa
671318329Smav	lp = malloc(sizeof(struct lagg_port), M_DEVBUF, M_WAITOK|M_ZERO);
672318329Smav	lp->lp_softc = sc;
673168793Sthompsa
674168793Sthompsa	/* Check if port is a stacked lagg */
675272386Shrs	LAGG_LIST_LOCK();
676272386Shrs	SLIST_FOREACH(sc_ptr, &V_lagg_list, sc_entries) {
677168793Sthompsa		if (ifp == sc_ptr->sc_ifp) {
678272386Shrs			LAGG_LIST_UNLOCK();
679168793Sthompsa			free(lp, M_DEVBUF);
680342206Smav			if (oldmtu != -1)
681342206Smav				(*ifp->if_ioctl)(ifp, SIOCSIFMTU,
682342206Smav				    (caddr_t)&ifr);
683168793Sthompsa			return (EINVAL);
684201803Strasz			/* XXX disable stacking for the moment, its untested */
685201803Strasz#ifdef LAGG_PORT_STACKING
686168793Sthompsa			lp->lp_flags |= LAGG_PORT_STACK;
687168793Sthompsa			if (lagg_port_checkstacking(sc_ptr) >=
688168793Sthompsa			    LAGG_MAX_STACKING) {
689272386Shrs				LAGG_LIST_UNLOCK();
690168793Sthompsa				free(lp, M_DEVBUF);
691342206Smav				if (oldmtu != -1)
692342206Smav					(*ifp->if_ioctl)(ifp, SIOCSIFMTU,
693342206Smav					    (caddr_t)&ifr);
694168793Sthompsa				return (E2BIG);
695168793Sthompsa			}
696201803Strasz#endif
697168793Sthompsa		}
698168793Sthompsa	}
699272386Shrs	LAGG_LIST_UNLOCK();
700168793Sthompsa
701318329Smav	if_ref(ifp);
702318329Smav	lp->lp_ifp = ifp;
703318329Smav
704318329Smav	bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN);
705318329Smav	lp->lp_ifcapenable = ifp->if_capenable;
706318329Smav	if (SLIST_EMPTY(&sc->sc_ports)) {
707318329Smav		LAGG_WLOCK(sc);
708318329Smav		bcopy(IF_LLADDR(ifp), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
709318329Smav		lagg_proto_lladdr(sc);
710318329Smav		LAGG_WUNLOCK(sc);
711318329Smav		EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp);
712318329Smav	} else {
713318329Smav		if_setlladdr(ifp, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
714318329Smav	}
715318329Smav	lagg_setflags(lp, 1);
716318329Smav
717318329Smav	LAGG_WLOCK(sc);
718318329Smav	if (SLIST_EMPTY(&sc->sc_ports))
719318329Smav		sc->sc_primary = lp;
720318329Smav
721168793Sthompsa	/* Change the interface type */
722168793Sthompsa	lp->lp_iftype = ifp->if_type;
723168793Sthompsa	ifp->if_type = IFT_IEEE8023ADLAG;
724168793Sthompsa	ifp->if_lagg = lp;
725168793Sthompsa	lp->lp_ioctl = ifp->if_ioctl;
726168793Sthompsa	ifp->if_ioctl = lagg_port_ioctl;
727168793Sthompsa	lp->lp_output = ifp->if_output;
728168793Sthompsa	ifp->if_output = lagg_port_output;
729168793Sthompsa
730318329Smav	/* Read port counters */
731318329Smav	pval = lp->port_counters.val;
732318329Smav	for (i = 0; i < IFCOUNTERS; i++, pval++)
733318329Smav		*pval = ifp->if_get_counter(ifp, i);
734168793Sthompsa
735277295Sae	/*
736277295Sae	 * Insert into the list of ports.
737277295Sae	 * Keep ports sorted by if_index. It is handy, when configuration
738277295Sae	 * is predictable and `ifconfig laggN create ...` command
739277295Sae	 * will lead to the same result each time.
740277295Sae	 */
741272176Sae	SLIST_FOREACH(tlp, &sc->sc_ports, lp_entries) {
742272176Sae		if (tlp->lp_ifp->if_index < ifp->if_index && (
743272176Sae		    SLIST_NEXT(tlp, lp_entries) == NULL ||
744277295Sae		    SLIST_NEXT(tlp, lp_entries)->lp_ifp->if_index >
745272176Sae		    ifp->if_index))
746272176Sae			break;
747272176Sae	}
748272176Sae	if (tlp != NULL)
749272176Sae		SLIST_INSERT_AFTER(tlp, lp, lp_entries);
750272176Sae	else
751272176Sae		SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries);
752168793Sthompsa	sc->sc_count++;
753168793Sthompsa
754318329Smav	lagg_setmulti(lp);
755168793Sthompsa
756272178Sglebius	if ((error = lagg_proto_addport(sc, lp)) != 0) {
757272178Sglebius		/* Remove the port, without calling pr_delport. */
758168793Sthompsa		lagg_port_destroy(lp, 0);
759342206Smav		if (oldmtu != -1)
760342206Smav			(*ifp->if_ioctl)(ifp, SIOCSIFMTU, (caddr_t)&ifr);
761318329Smav		LAGG_UNLOCK_ASSERT(sc);
762168793Sthompsa		return (error);
763168793Sthompsa	}
764168793Sthompsa
765318329Smav	LAGG_WUNLOCK(sc);
766318329Smav
767318329Smav	/* Update lagg capabilities */
768318329Smav	lagg_capabilities(sc);
769318329Smav	lagg_linkstate(sc);
770318329Smav
771272178Sglebius	return (0);
772168793Sthompsa}
773168793Sthompsa
774201803Strasz#ifdef LAGG_PORT_STACKING
775168793Sthompsastatic int
776168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc)
777168793Sthompsa{
778168793Sthompsa	struct lagg_softc *sc_ptr;
779168793Sthompsa	struct lagg_port *lp;
780168793Sthompsa	int m = 0;
781168793Sthompsa
782318329Smav	LAGG_SXLOCK_ASSERT(sc);
783168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
784168793Sthompsa		if (lp->lp_flags & LAGG_PORT_STACK) {
785168793Sthompsa			sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc;
786168793Sthompsa			m = MAX(m, lagg_port_checkstacking(sc_ptr));
787168793Sthompsa		}
788168793Sthompsa	}
789168793Sthompsa
790168793Sthompsa	return (m + 1);
791168793Sthompsa}
792201803Strasz#endif
793168793Sthompsa
794168793Sthompsastatic int
795272178Sglebiuslagg_port_destroy(struct lagg_port *lp, int rundelport)
796168793Sthompsa{
797170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
798288980Shrs	struct lagg_port *lp_ptr, *lp0;
799168793Sthompsa	struct ifnet *ifp = lp->lp_ifp;
800272211Smelifaro	uint64_t *pval, vdiff;
801272211Smelifaro	int i;
802168793Sthompsa
803318329Smav	LAGG_XLOCK_ASSERT(sc);
804168793Sthompsa
805318329Smav	if (rundelport) {
806318329Smav		LAGG_WLOCK(sc);
807272178Sglebius		lagg_proto_delport(sc, lp);
808318329Smav	} else
809318329Smav		LAGG_WLOCK_ASSERT(sc);
810168793Sthompsa
811318329Smav	if (lp->lp_detaching == 0)
812318329Smav		lagg_clrmulti(lp);
813168793Sthompsa
814168793Sthompsa	/* Restore interface */
815168793Sthompsa	ifp->if_type = lp->lp_iftype;
816168793Sthompsa	ifp->if_ioctl = lp->lp_ioctl;
817168793Sthompsa	ifp->if_output = lp->lp_output;
818168793Sthompsa	ifp->if_lagg = NULL;
819168793Sthompsa
820272211Smelifaro	/* Update detached port counters */
821272211Smelifaro	pval = lp->port_counters.val;
822272354Sglebius	for (i = 0; i < IFCOUNTERS; i++, pval++) {
823272211Smelifaro		vdiff = ifp->if_get_counter(ifp, i) - *pval;
824272244Sglebius		sc->detached_counters.val[i] += vdiff;
825272211Smelifaro	}
826272211Smelifaro
827168793Sthompsa	/* Finally, remove the port from the lagg */
828168793Sthompsa	SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries);
829168793Sthompsa	sc->sc_count--;
830168793Sthompsa
831168793Sthompsa	/* Update the primary interface */
832168793Sthompsa	if (lp == sc->sc_primary) {
833168793Sthompsa		uint8_t lladdr[ETHER_ADDR_LEN];
834168793Sthompsa
835318329Smav		if ((lp0 = SLIST_FIRST(&sc->sc_ports)) == NULL)
836168793Sthompsa			bzero(&lladdr, ETHER_ADDR_LEN);
837318329Smav		else
838318329Smav			bcopy(lp0->lp_lladdr, lladdr, ETHER_ADDR_LEN);
839290239Smelifaro		sc->sc_primary = lp0;
840318329Smav		if (sc->sc_destroying == 0) {
841318329Smav			bcopy(lladdr, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
842318329Smav			lagg_proto_lladdr(sc);
843318329Smav			LAGG_WUNLOCK(sc);
844318329Smav			EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp);
845318329Smav		} else
846318329Smav			LAGG_WUNLOCK(sc);
847290239Smelifaro
848288980Shrs		/*
849318329Smav		 * Update lladdr for each port (new primary needs update
850318329Smav		 * as well, to switch from old lladdr to its 'real' one)
851288980Shrs		 */
852168793Sthompsa		SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries)
853318329Smav			if_setlladdr(lp_ptr->lp_ifp, lladdr, ETHER_ADDR_LEN);
854318329Smav	} else
855318329Smav		LAGG_WUNLOCK(sc);
856168793Sthompsa
857168793Sthompsa	if (lp->lp_ifflags)
858168793Sthompsa		if_printf(ifp, "%s: lp_ifflags unclean\n", __func__);
859168793Sthompsa
860318329Smav	if (lp->lp_detaching == 0) {
861318329Smav		lagg_setflags(lp, 0);
862318329Smav		lagg_setcaps(lp, lp->lp_ifcapenable);
863318329Smav		if_setlladdr(ifp, lp->lp_lladdr, ETHER_ADDR_LEN);
864318329Smav	}
865318329Smav
866317698Smav	if_rele(ifp);
867168793Sthompsa	free(lp, M_DEVBUF);
868168793Sthompsa
869168793Sthompsa	/* Update lagg capabilities */
870171661Sthompsa	lagg_capabilities(sc);
871173895Sthompsa	lagg_linkstate(sc);
872168793Sthompsa
873168793Sthompsa	return (0);
874168793Sthompsa}
875168793Sthompsa
876168793Sthompsastatic int
877168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
878168793Sthompsa{
879168793Sthompsa	struct lagg_reqport *rp = (struct lagg_reqport *)data;
880168793Sthompsa	struct lagg_softc *sc;
881168793Sthompsa	struct lagg_port *lp = NULL;
882168793Sthompsa	int error = 0;
883168793Sthompsa
884168793Sthompsa	/* Should be checked by the caller */
885168793Sthompsa	if (ifp->if_type != IFT_IEEE8023ADLAG ||
886170599Sthompsa	    (lp = ifp->if_lagg) == NULL || (sc = lp->lp_softc) == NULL)
887168793Sthompsa		goto fallback;
888168793Sthompsa
889168793Sthompsa	switch (cmd) {
890168793Sthompsa	case SIOCGLAGGPORT:
891168793Sthompsa		if (rp->rp_portname[0] == '\0' ||
892168793Sthompsa		    ifunit(rp->rp_portname) != ifp) {
893168793Sthompsa			error = EINVAL;
894168793Sthompsa			break;
895168793Sthompsa		}
896168793Sthompsa
897318329Smav		LAGG_SLOCK(sc);
898171603Sthompsa		if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) {
899168793Sthompsa			error = ENOENT;
900318329Smav			LAGG_SUNLOCK(sc);
901168793Sthompsa			break;
902168793Sthompsa		}
903168793Sthompsa
904168793Sthompsa		lagg_port2req(lp, rp);
905318329Smav		LAGG_SUNLOCK(sc);
906168793Sthompsa		break;
907171661Sthompsa
908171661Sthompsa	case SIOCSIFCAP:
909171661Sthompsa		if (lp->lp_ioctl == NULL) {
910171661Sthompsa			error = EINVAL;
911171661Sthompsa			break;
912171661Sthompsa		}
913171661Sthompsa		error = (*lp->lp_ioctl)(ifp, cmd, data);
914171661Sthompsa		if (error)
915171661Sthompsa			break;
916171661Sthompsa
917171661Sthompsa		/* Update lagg interface capabilities */
918318329Smav		LAGG_XLOCK(sc);
919171661Sthompsa		lagg_capabilities(sc);
920318329Smav		LAGG_XUNLOCK(sc);
921319697Smav		VLAN_CAPABILITIES(sc->sc_ifp);
922171661Sthompsa		break;
923171661Sthompsa
924171661Sthompsa	case SIOCSIFMTU:
925171661Sthompsa		/* Do not allow the MTU to be changed once joined */
926171661Sthompsa		error = EINVAL;
927171661Sthompsa		break;
928171661Sthompsa
929168793Sthompsa	default:
930168793Sthompsa		goto fallback;
931168793Sthompsa	}
932168793Sthompsa
933168793Sthompsa	return (error);
934168793Sthompsa
935168793Sthompsafallback:
936313108Sasomers	if (lp != NULL && lp->lp_ioctl != NULL)
937168793Sthompsa		return ((*lp->lp_ioctl)(ifp, cmd, data));
938168793Sthompsa
939168793Sthompsa	return (EINVAL);
940168793Sthompsa}
941168793Sthompsa
942234936Semaste/*
943272211Smelifaro * Requests counter @cnt data.
944272211Smelifaro *
945272211Smelifaro * Counter value is calculated the following way:
946272211Smelifaro * 1) for each port, sum  difference between current and "initial" measurements.
947272211Smelifaro * 2) add lagg logical interface counters.
948272211Smelifaro * 3) add data from detached_counters array.
949272211Smelifaro *
950272211Smelifaro * We also do the following things on ports attach/detach:
951272211Smelifaro * 1) On port attach we store all counters it has into port_counter array.
952272211Smelifaro * 2) On port detach we add the different between "initial" and
953272211Smelifaro *   current counters data to detached_counters array.
954272211Smelifaro */
955272211Smelifarostatic uint64_t
956272211Smelifarolagg_get_counter(struct ifnet *ifp, ift_counter cnt)
957272211Smelifaro{
958272211Smelifaro	struct lagg_softc *sc;
959272211Smelifaro	struct lagg_port *lp;
960272211Smelifaro	struct ifnet *lpifp;
961272211Smelifaro	struct rm_priotracker tracker;
962272211Smelifaro	uint64_t newval, oldval, vsum;
963272211Smelifaro
964272244Sglebius	/* Revise this when we've got non-generic counters. */
965272244Sglebius	KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt));
966272211Smelifaro
967272211Smelifaro	sc = (struct lagg_softc *)ifp->if_softc;
968272211Smelifaro	LAGG_RLOCK(sc, &tracker);
969272211Smelifaro
970272211Smelifaro	vsum = 0;
971272211Smelifaro	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
972272211Smelifaro		/* Saved attached value */
973272244Sglebius		oldval = lp->port_counters.val[cnt];
974272211Smelifaro		/* current value */
975272211Smelifaro		lpifp = lp->lp_ifp;
976272211Smelifaro		newval = lpifp->if_get_counter(lpifp, cnt);
977272211Smelifaro		/* Calculate diff and save new */
978272211Smelifaro		vsum += newval - oldval;
979272211Smelifaro	}
980272211Smelifaro
981272211Smelifaro	/*
982272211Smelifaro	 * Add counter data which might be added by upper
983272211Smelifaro	 * layer protocols operating on logical interface.
984272211Smelifaro	 */
985272211Smelifaro	vsum += if_get_counter_default(ifp, cnt);
986272211Smelifaro
987272211Smelifaro	/*
988272211Smelifaro	 * Add counter data from detached ports counters
989272211Smelifaro	 */
990272244Sglebius	vsum += sc->detached_counters.val[cnt];
991272211Smelifaro
992272211Smelifaro	LAGG_RUNLOCK(sc, &tracker);
993272211Smelifaro
994272211Smelifaro	return (vsum);
995272211Smelifaro}
996272211Smelifaro
997272211Smelifaro/*
998234936Semaste * For direct output to child ports.
999234936Semaste */
1000168793Sthompsastatic int
1001168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m,
1002249925Sglebius	const struct sockaddr *dst, struct route *ro)
1003168793Sthompsa{
1004168793Sthompsa	struct lagg_port *lp = ifp->if_lagg;
1005168793Sthompsa
1006168793Sthompsa	switch (dst->sa_family) {
1007168793Sthompsa		case pseudo_AF_HDRCMPLT:
1008168793Sthompsa		case AF_UNSPEC:
1009191148Skmacy			return ((*lp->lp_output)(ifp, m, dst, ro));
1010168793Sthompsa	}
1011168793Sthompsa
1012168793Sthompsa	/* drop any other frames */
1013168793Sthompsa	m_freem(m);
1014245741Sglebius	return (ENETDOWN);
1015168793Sthompsa}
1016168793Sthompsa
1017168793Sthompsastatic void
1018168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp)
1019168793Sthompsa{
1020168793Sthompsa	struct lagg_port *lp;
1021168793Sthompsa	struct lagg_softc *sc;
1022168793Sthompsa
1023168793Sthompsa	if ((lp = ifp->if_lagg) == NULL)
1024168793Sthompsa		return;
1025237852Sthompsa	/* If the ifnet is just being renamed, don't do anything. */
1026237852Sthompsa	if (ifp->if_flags & IFF_RENAMING)
1027237852Sthompsa		return;
1028168793Sthompsa
1029170599Sthompsa	sc = lp->lp_softc;
1030168793Sthompsa
1031318329Smav	LAGG_XLOCK(sc);
1032318329Smav	lp->lp_detaching = 1;
1033168793Sthompsa	lagg_port_destroy(lp, 1);
1034318329Smav	LAGG_XUNLOCK(sc);
1035319697Smav	VLAN_CAPABILITIES(sc->sc_ifp);
1036168793Sthompsa}
1037168793Sthompsa
1038168793Sthompsastatic void
1039168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp)
1040168793Sthompsa{
1041170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
1042172020Sthompsa
1043168793Sthompsa	strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname));
1044168793Sthompsa	strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname));
1045168793Sthompsa	rp->rp_prio = lp->lp_prio;
1046168793Sthompsa	rp->rp_flags = lp->lp_flags;
1047272178Sglebius	lagg_proto_portreq(sc, lp, &rp->rp_psc);
1048168793Sthompsa
1049168793Sthompsa	/* Add protocol specific flags */
1050168793Sthompsa	switch (sc->sc_proto) {
1051168793Sthompsa		case LAGG_PROTO_FAILOVER:
1052168793Sthompsa			if (lp == sc->sc_primary)
1053169204Sthompsa				rp->rp_flags |= LAGG_PORT_MASTER;
1054172020Sthompsa			if (lp == lagg_link_active(sc, sc->sc_primary))
1055172020Sthompsa				rp->rp_flags |= LAGG_PORT_ACTIVE;
1056172020Sthompsa			break;
1057172020Sthompsa
1058168793Sthompsa		case LAGG_PROTO_ROUNDROBIN:
1059168793Sthompsa		case LAGG_PROTO_LOADBALANCE:
1060272175Sglebius		case LAGG_PROTO_BROADCAST:
1061168793Sthompsa			if (LAGG_PORTACTIVE(lp))
1062168793Sthompsa				rp->rp_flags |= LAGG_PORT_ACTIVE;
1063168793Sthompsa			break;
1064168793Sthompsa
1065168793Sthompsa		case LAGG_PROTO_LACP:
1066168793Sthompsa			/* LACP has a different definition of active */
1067177274Sthompsa			if (lacp_isactive(lp))
1068168793Sthompsa				rp->rp_flags |= LAGG_PORT_ACTIVE;
1069177274Sthompsa			if (lacp_iscollecting(lp))
1070177274Sthompsa				rp->rp_flags |= LAGG_PORT_COLLECTING;
1071177274Sthompsa			if (lacp_isdistributing(lp))
1072177274Sthompsa				rp->rp_flags |= LAGG_PORT_DISTRIBUTING;
1073168793Sthompsa			break;
1074168793Sthompsa	}
1075168793Sthompsa
1076168793Sthompsa}
1077168793Sthompsa
1078168793Sthompsastatic void
1079168793Sthompsalagg_init(void *xsc)
1080168793Sthompsa{
1081168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)xsc;
1082290239Smelifaro	struct ifnet *ifp = sc->sc_ifp;
1083168793Sthompsa	struct lagg_port *lp;
1084168793Sthompsa
1085318329Smav	LAGG_XLOCK(sc);
1086318329Smav	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1087318329Smav		LAGG_XUNLOCK(sc);
1088168793Sthompsa		return;
1089318329Smav	}
1090168793Sthompsa
1091168793Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1092290239Smelifaro
1093290239Smelifaro	/*
1094290239Smelifaro	 * Update the port lladdrs if needed.
1095290239Smelifaro	 * This might be if_setlladdr() notification
1096290239Smelifaro	 * that lladdr has been changed.
1097290239Smelifaro	 */
1098318329Smav	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1099318329Smav		if (memcmp(IF_LLADDR(ifp), IF_LLADDR(lp->lp_ifp),
1100318329Smav		    ETHER_ADDR_LEN) != 0)
1101318329Smav			if_setlladdr(lp->lp_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN);
1102318329Smav	}
1103168793Sthompsa
1104272178Sglebius	lagg_proto_init(sc);
1105168793Sthompsa
1106318329Smav	LAGG_XUNLOCK(sc);
1107168793Sthompsa}
1108168793Sthompsa
1109168793Sthompsastatic void
1110168793Sthompsalagg_stop(struct lagg_softc *sc)
1111168793Sthompsa{
1112168793Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1113168793Sthompsa
1114318329Smav	LAGG_XLOCK_ASSERT(sc);
1115168793Sthompsa
1116168793Sthompsa	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
1117168793Sthompsa		return;
1118168793Sthompsa
1119168793Sthompsa	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1120168793Sthompsa
1121272178Sglebius	lagg_proto_stop(sc);
1122168793Sthompsa}
1123168793Sthompsa
1124168793Sthompsastatic int
1125168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1126168793Sthompsa{
1127168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
1128168793Sthompsa	struct lagg_reqall *ra = (struct lagg_reqall *)data;
1129272446Shrs	struct lagg_reqopts *ro = (struct lagg_reqopts *)data;
1130168793Sthompsa	struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf;
1131232629Sthompsa	struct lagg_reqflags *rf = (struct lagg_reqflags *)data;
1132168793Sthompsa	struct ifreq *ifr = (struct ifreq *)data;
1133168793Sthompsa	struct lagg_port *lp;
1134168793Sthompsa	struct ifnet *tpif;
1135168793Sthompsa	struct thread *td = curthread;
1136171603Sthompsa	char *buf, *outbuf;
1137171603Sthompsa	int count, buflen, len, error = 0;
1138168793Sthompsa
1139168793Sthompsa	bzero(&rpbuf, sizeof(rpbuf));
1140168793Sthompsa
1141168793Sthompsa	switch (cmd) {
1142168793Sthompsa	case SIOCGLAGG:
1143318329Smav		LAGG_SLOCK(sc);
1144318329Smav		buflen = sc->sc_count * sizeof(struct lagg_reqport);
1145171603Sthompsa		outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO);
1146168793Sthompsa		ra->ra_proto = sc->sc_proto;
1147272178Sglebius		lagg_proto_request(sc, &ra->ra_psc);
1148171603Sthompsa		count = 0;
1149171603Sthompsa		buf = outbuf;
1150171603Sthompsa		len = min(ra->ra_size, buflen);
1151171603Sthompsa		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1152171603Sthompsa			if (len < sizeof(rpbuf))
1153171603Sthompsa				break;
1154171603Sthompsa
1155168793Sthompsa			lagg_port2req(lp, &rpbuf);
1156171603Sthompsa			memcpy(buf, &rpbuf, sizeof(rpbuf));
1157171603Sthompsa			count++;
1158171603Sthompsa			buf += sizeof(rpbuf);
1159171603Sthompsa			len -= sizeof(rpbuf);
1160168793Sthompsa		}
1161318329Smav		LAGG_SUNLOCK(sc);
1162171603Sthompsa		ra->ra_ports = count;
1163171603Sthompsa		ra->ra_size = count * sizeof(rpbuf);
1164171603Sthompsa		error = copyout(outbuf, ra->ra_port, ra->ra_size);
1165171603Sthompsa		free(outbuf, M_TEMP);
1166168793Sthompsa		break;
1167168793Sthompsa	case SIOCSLAGG:
1168168793Sthompsa		error = priv_check(td, PRIV_NET_LAGG);
1169168793Sthompsa		if (error)
1170168793Sthompsa			break;
1171295796Saraujo		if (ra->ra_proto >= LAGG_PROTO_MAX) {
1172272446Shrs			error = EPROTONOSUPPORT;
1173272446Shrs			break;
1174272446Shrs		}
1175272386Shrs
1176318329Smav		LAGG_XLOCK(sc);
1177272446Shrs		LAGG_WLOCK(sc);
1178272446Shrs		lagg_proto_detach(sc);
1179290239Smelifaro		LAGG_UNLOCK_ASSERT(sc);
1180272446Shrs		lagg_proto_attach(sc, ra->ra_proto);
1181318329Smav		LAGG_XUNLOCK(sc);
1182272446Shrs		break;
1183272446Shrs	case SIOCGLAGGOPTS:
1184318329Smav		LAGG_SLOCK(sc);
1185272446Shrs		ro->ro_opts = sc->sc_opts;
1186272446Shrs		if (sc->sc_proto == LAGG_PROTO_LACP) {
1187272446Shrs			struct lacp_softc *lsc;
1188272446Shrs
1189272446Shrs			lsc = (struct lacp_softc *)sc->sc_psc;
1190272446Shrs			if (lsc->lsc_debug.lsc_tx_test != 0)
1191272446Shrs				ro->ro_opts |= LAGG_OPT_LACP_TXTEST;
1192272446Shrs			if (lsc->lsc_debug.lsc_rx_test != 0)
1193272446Shrs				ro->ro_opts |= LAGG_OPT_LACP_RXTEST;
1194272446Shrs			if (lsc->lsc_strict_mode != 0)
1195272446Shrs				ro->ro_opts |= LAGG_OPT_LACP_STRICT;
1196286700Shiren			if (lsc->lsc_fast_timeout != 0)
1197362366Srpokala				ro->ro_opts |= LAGG_OPT_LACP_FAST_TIMO;
1198272446Shrs
1199272446Shrs			ro->ro_active = sc->sc_active;
1200272446Shrs		} else {
1201272446Shrs			ro->ro_active = 0;
1202272446Shrs			SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
1203272446Shrs				ro->ro_active += LAGG_PORTACTIVE(lp);
1204272446Shrs		}
1205294615Saraujo		ro->ro_bkt = sc->sc_bkt;
1206272446Shrs		ro->ro_flapping = sc->sc_flapping;
1207272446Shrs		ro->ro_flowid_shift = sc->flowid_shift;
1208318329Smav		LAGG_SUNLOCK(sc);
1209272446Shrs		break;
1210272446Shrs	case SIOCSLAGGOPTS:
1211294615Saraujo		if (sc->sc_proto == LAGG_PROTO_ROUNDROBIN) {
1212294615Saraujo			if (ro->ro_bkt == 0)
1213294615Saraujo				sc->sc_bkt = 1; // Minimum 1 packet per iface.
1214294615Saraujo			else
1215294615Saraujo				sc->sc_bkt = ro->ro_bkt;
1216294615Saraujo		}
1217272446Shrs		error = priv_check(td, PRIV_NET_LAGG);
1218272446Shrs		if (error)
1219272446Shrs			break;
1220272446Shrs		if (ro->ro_opts == 0)
1221272446Shrs			break;
1222272446Shrs		/*
1223272446Shrs		 * Set options.  LACP options are stored in sc->sc_psc,
1224272446Shrs		 * not in sc_opts.
1225272446Shrs		 */
1226272446Shrs		int valid, lacp;
1227272446Shrs
1228272446Shrs		switch (ro->ro_opts) {
1229272446Shrs		case LAGG_OPT_USE_FLOWID:
1230272446Shrs		case -LAGG_OPT_USE_FLOWID:
1231272446Shrs		case LAGG_OPT_FLOWIDSHIFT:
1232272446Shrs			valid = 1;
1233272446Shrs			lacp = 0;
1234272446Shrs			break;
1235272446Shrs		case LAGG_OPT_LACP_TXTEST:
1236272446Shrs		case -LAGG_OPT_LACP_TXTEST:
1237272446Shrs		case LAGG_OPT_LACP_RXTEST:
1238272446Shrs		case -LAGG_OPT_LACP_RXTEST:
1239272446Shrs		case LAGG_OPT_LACP_STRICT:
1240272446Shrs		case -LAGG_OPT_LACP_STRICT:
1241362366Srpokala		case LAGG_OPT_LACP_FAST_TIMO:
1242362366Srpokala		case -LAGG_OPT_LACP_FAST_TIMO:
1243272446Shrs			valid = lacp = 1;
1244272446Shrs			break;
1245272446Shrs		default:
1246272446Shrs			valid = lacp = 0;
1247272446Shrs			break;
1248272446Shrs		}
1249272446Shrs
1250318329Smav		LAGG_XLOCK(sc);
1251294615Saraujo
1252272446Shrs		if (valid == 0 ||
1253272446Shrs		    (lacp == 1 && sc->sc_proto != LAGG_PROTO_LACP)) {
1254272446Shrs			/* Invalid combination of options specified. */
1255272446Shrs			error = EINVAL;
1256318329Smav			LAGG_XUNLOCK(sc);
1257272446Shrs			break;	/* Return from SIOCSLAGGOPTS. */
1258272446Shrs		}
1259272446Shrs		/*
1260272446Shrs		 * Store new options into sc->sc_opts except for
1261272446Shrs		 * FLOWIDSHIFT and LACP options.
1262272446Shrs		 */
1263272446Shrs		if (lacp == 0) {
1264272446Shrs			if (ro->ro_opts == LAGG_OPT_FLOWIDSHIFT)
1265272446Shrs				sc->flowid_shift = ro->ro_flowid_shift;
1266272446Shrs			else if (ro->ro_opts > 0)
1267272446Shrs				sc->sc_opts |= ro->ro_opts;
1268272446Shrs			else
1269272446Shrs				sc->sc_opts &= ~ro->ro_opts;
1270272446Shrs		} else {
1271272446Shrs			struct lacp_softc *lsc;
1272286700Shiren			struct lacp_port *lp;
1273272446Shrs
1274272446Shrs			lsc = (struct lacp_softc *)sc->sc_psc;
1275272446Shrs
1276272446Shrs			switch (ro->ro_opts) {
1277272446Shrs			case LAGG_OPT_LACP_TXTEST:
1278272446Shrs				lsc->lsc_debug.lsc_tx_test = 1;
1279272386Shrs				break;
1280272386Shrs			case -LAGG_OPT_LACP_TXTEST:
1281272446Shrs				lsc->lsc_debug.lsc_tx_test = 0;
1282272446Shrs				break;
1283272386Shrs			case LAGG_OPT_LACP_RXTEST:
1284272446Shrs				lsc->lsc_debug.lsc_rx_test = 1;
1285272446Shrs				break;
1286272386Shrs			case -LAGG_OPT_LACP_RXTEST:
1287272446Shrs				lsc->lsc_debug.lsc_rx_test = 0;
1288272446Shrs				break;
1289272386Shrs			case LAGG_OPT_LACP_STRICT:
1290272446Shrs				lsc->lsc_strict_mode = 1;
1291272446Shrs				break;
1292272386Shrs			case -LAGG_OPT_LACP_STRICT:
1293272446Shrs				lsc->lsc_strict_mode = 0;
1294272386Shrs				break;
1295362366Srpokala			case LAGG_OPT_LACP_FAST_TIMO:
1296286700Shiren				LACP_LOCK(lsc);
1297286700Shiren        			LIST_FOREACH(lp, &lsc->lsc_ports, lp_next)
1298286700Shiren                        		lp->lp_state |= LACP_STATE_TIMEOUT;
1299286700Shiren				LACP_UNLOCK(lsc);
1300286700Shiren				lsc->lsc_fast_timeout = 1;
1301286700Shiren				break;
1302362366Srpokala			case -LAGG_OPT_LACP_FAST_TIMO:
1303286700Shiren				LACP_LOCK(lsc);
1304286700Shiren        			LIST_FOREACH(lp, &lsc->lsc_ports, lp_next)
1305286700Shiren                        		lp->lp_state &= ~LACP_STATE_TIMEOUT;
1306286700Shiren				LACP_UNLOCK(lsc);
1307286700Shiren				lsc->lsc_fast_timeout = 0;
1308286700Shiren				break;
1309272386Shrs			}
1310272386Shrs		}
1311318329Smav		LAGG_XUNLOCK(sc);
1312168793Sthompsa		break;
1313232629Sthompsa	case SIOCGLAGGFLAGS:
1314279891Shselasky		rf->rf_flags = 0;
1315318329Smav		LAGG_SLOCK(sc);
1316279891Shselasky		if (sc->sc_flags & MBUF_HASHFLAG_L2)
1317279891Shselasky			rf->rf_flags |= LAGG_F_HASHL2;
1318279891Shselasky		if (sc->sc_flags & MBUF_HASHFLAG_L3)
1319279891Shselasky			rf->rf_flags |= LAGG_F_HASHL3;
1320279891Shselasky		if (sc->sc_flags & MBUF_HASHFLAG_L4)
1321279891Shselasky			rf->rf_flags |= LAGG_F_HASHL4;
1322318329Smav		LAGG_SUNLOCK(sc);
1323232629Sthompsa		break;
1324232629Sthompsa	case SIOCSLAGGHASH:
1325232629Sthompsa		error = priv_check(td, PRIV_NET_LAGG);
1326232629Sthompsa		if (error)
1327232629Sthompsa			break;
1328232629Sthompsa		if ((rf->rf_flags & LAGG_F_HASHMASK) == 0) {
1329232629Sthompsa			error = EINVAL;
1330232629Sthompsa			break;
1331232629Sthompsa		}
1332318329Smav		LAGG_XLOCK(sc);
1333279891Shselasky		sc->sc_flags = 0;
1334279891Shselasky		if (rf->rf_flags & LAGG_F_HASHL2)
1335279891Shselasky			sc->sc_flags |= MBUF_HASHFLAG_L2;
1336279891Shselasky		if (rf->rf_flags & LAGG_F_HASHL3)
1337279891Shselasky			sc->sc_flags |= MBUF_HASHFLAG_L3;
1338279891Shselasky		if (rf->rf_flags & LAGG_F_HASHL4)
1339279891Shselasky			sc->sc_flags |= MBUF_HASHFLAG_L4;
1340318329Smav		LAGG_XUNLOCK(sc);
1341232629Sthompsa		break;
1342168793Sthompsa	case SIOCGLAGGPORT:
1343168793Sthompsa		if (rp->rp_portname[0] == '\0' ||
1344317698Smav		    (tpif = ifunit_ref(rp->rp_portname)) == NULL) {
1345168793Sthompsa			error = EINVAL;
1346168793Sthompsa			break;
1347168793Sthompsa		}
1348168793Sthompsa
1349318329Smav		LAGG_SLOCK(sc);
1350168793Sthompsa		if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL ||
1351170599Sthompsa		    lp->lp_softc != sc) {
1352168793Sthompsa			error = ENOENT;
1353318329Smav			LAGG_SUNLOCK(sc);
1354317698Smav			if_rele(tpif);
1355168793Sthompsa			break;
1356168793Sthompsa		}
1357168793Sthompsa
1358168793Sthompsa		lagg_port2req(lp, rp);
1359318329Smav		LAGG_SUNLOCK(sc);
1360317698Smav		if_rele(tpif);
1361168793Sthompsa		break;
1362168793Sthompsa	case SIOCSLAGGPORT:
1363168793Sthompsa		error = priv_check(td, PRIV_NET_LAGG);
1364168793Sthompsa		if (error)
1365168793Sthompsa			break;
1366168793Sthompsa		if (rp->rp_portname[0] == '\0' ||
1367317698Smav		    (tpif = ifunit_ref(rp->rp_portname)) == NULL) {
1368168793Sthompsa			error = EINVAL;
1369168793Sthompsa			break;
1370168793Sthompsa		}
1371273210Shrs#ifdef INET6
1372273210Shrs		/*
1373273210Shrs		 * A laggport interface should not have inet6 address
1374273210Shrs		 * because two interfaces with a valid link-local
1375273210Shrs		 * scope zone must not be merged in any form.  This
1376273210Shrs		 * restriction is needed to prevent violation of
1377273210Shrs		 * link-local scope zone.  Attempts to add a laggport
1378273210Shrs		 * interface which has inet6 addresses triggers
1379273210Shrs		 * removal of all inet6 addresses on the member
1380273210Shrs		 * interface.
1381273210Shrs		 */
1382273210Shrs		if (in6ifa_llaonifp(tpif)) {
1383273210Shrs			in6_ifdetach(tpif);
1384273210Shrs				if_printf(sc->sc_ifp,
1385273210Shrs				    "IPv6 addresses on %s have been removed "
1386273210Shrs				    "before adding it as a member to prevent "
1387273210Shrs				    "IPv6 address scope violation.\n",
1388273210Shrs				    tpif->if_xname);
1389273210Shrs		}
1390273210Shrs#endif
1391318329Smav		LAGG_XLOCK(sc);
1392168793Sthompsa		error = lagg_port_create(sc, tpif);
1393318329Smav		LAGG_XUNLOCK(sc);
1394317698Smav		if_rele(tpif);
1395319697Smav		VLAN_CAPABILITIES(ifp);
1396168793Sthompsa		break;
1397168793Sthompsa	case SIOCSLAGGDELPORT:
1398168793Sthompsa		error = priv_check(td, PRIV_NET_LAGG);
1399168793Sthompsa		if (error)
1400168793Sthompsa			break;
1401168793Sthompsa		if (rp->rp_portname[0] == '\0' ||
1402317698Smav		    (tpif = ifunit_ref(rp->rp_portname)) == NULL) {
1403168793Sthompsa			error = EINVAL;
1404168793Sthompsa			break;
1405168793Sthompsa		}
1406168793Sthompsa
1407318329Smav		LAGG_XLOCK(sc);
1408168793Sthompsa		if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL ||
1409170599Sthompsa		    lp->lp_softc != sc) {
1410168793Sthompsa			error = ENOENT;
1411318329Smav			LAGG_XUNLOCK(sc);
1412317698Smav			if_rele(tpif);
1413168793Sthompsa			break;
1414168793Sthompsa		}
1415168793Sthompsa
1416168793Sthompsa		error = lagg_port_destroy(lp, 1);
1417318329Smav		LAGG_XUNLOCK(sc);
1418317698Smav		if_rele(tpif);
1419319697Smav		VLAN_CAPABILITIES(ifp);
1420168793Sthompsa		break;
1421168793Sthompsa	case SIOCSIFFLAGS:
1422168793Sthompsa		/* Set flags on ports too */
1423318329Smav		LAGG_XLOCK(sc);
1424168793Sthompsa		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1425168793Sthompsa			lagg_setflags(lp, 1);
1426168793Sthompsa		}
1427168793Sthompsa
1428168793Sthompsa		if (!(ifp->if_flags & IFF_UP) &&
1429168793Sthompsa		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1430168793Sthompsa			/*
1431168793Sthompsa			 * If interface is marked down and it is running,
1432168793Sthompsa			 * then stop and disable it.
1433168793Sthompsa			 */
1434168793Sthompsa			lagg_stop(sc);
1435318329Smav			LAGG_XUNLOCK(sc);
1436168793Sthompsa		} else if ((ifp->if_flags & IFF_UP) &&
1437168793Sthompsa		    !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1438168793Sthompsa			/*
1439168793Sthompsa			 * If interface is marked up and it is stopped, then
1440168793Sthompsa			 * start it.
1441168793Sthompsa			 */
1442318329Smav			LAGG_XUNLOCK(sc);
1443168793Sthompsa			(*ifp->if_init)(sc);
1444318329Smav		} else
1445318329Smav			LAGG_XUNLOCK(sc);
1446168793Sthompsa		break;
1447168793Sthompsa	case SIOCADDMULTI:
1448168793Sthompsa	case SIOCDELMULTI:
1449171603Sthompsa		LAGG_WLOCK(sc);
1450318329Smav		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1451318329Smav			lagg_clrmulti(lp);
1452318329Smav			lagg_setmulti(lp);
1453318329Smav		}
1454171603Sthompsa		LAGG_WUNLOCK(sc);
1455318329Smav		error = 0;
1456168793Sthompsa		break;
1457168793Sthompsa	case SIOCSIFMEDIA:
1458168793Sthompsa	case SIOCGIFMEDIA:
1459168793Sthompsa		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
1460168793Sthompsa		break;
1461171661Sthompsa
1462171661Sthompsa	case SIOCSIFCAP:
1463318329Smav		LAGG_XLOCK(sc);
1464318329Smav		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1465318329Smav			if (lp->lp_ioctl != NULL)
1466318329Smav				(*lp->lp_ioctl)(lp->lp_ifp, cmd, data);
1467318329Smav		}
1468318329Smav		lagg_capabilities(sc);
1469318329Smav		LAGG_XUNLOCK(sc);
1470319697Smav		VLAN_CAPABILITIES(ifp);
1471318329Smav		error = 0;
1472318329Smav		break;
1473318329Smav
1474297610Srpokala	case SIOCSIFMTU:
1475342206Smav		LAGG_XLOCK(sc);
1476342206Smav		SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1477342206Smav			if (lp->lp_ioctl != NULL)
1478342206Smav				error = (*lp->lp_ioctl)(lp->lp_ifp, cmd, data);
1479342206Smav			else
1480342206Smav				error = EINVAL;
1481342206Smav			if (error != 0) {
1482342206Smav				if_printf(ifp,
1483342206Smav				    "failed to change MTU to %d on port %s, "
1484342206Smav				    "reverting all ports to original MTU (%d)\n",
1485342206Smav				    ifr->ifr_mtu, lp->lp_ifp->if_xname, ifp->if_mtu);
1486342206Smav				break;
1487342206Smav			}
1488342206Smav		}
1489342206Smav		if (error == 0) {
1490342206Smav			ifp->if_mtu = ifr->ifr_mtu;
1491342206Smav		} else {
1492342206Smav			/* set every port back to the original MTU */
1493342206Smav			ifr->ifr_mtu = ifp->if_mtu;
1494342206Smav			SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1495342206Smav				if (lp->lp_ioctl != NULL)
1496342206Smav					(*lp->lp_ioctl)(lp->lp_ifp, cmd, data);
1497342206Smav			}
1498342206Smav		}
1499342206Smav		LAGG_XUNLOCK(sc);
1500171661Sthompsa		break;
1501171661Sthompsa
1502168793Sthompsa	default:
1503168793Sthompsa		error = ether_ioctl(ifp, cmd, data);
1504168793Sthompsa		break;
1505168793Sthompsa	}
1506168793Sthompsa	return (error);
1507168793Sthompsa}
1508168793Sthompsa
1509168793Sthompsastatic int
1510318329Smavlagg_setmulti(struct lagg_port *lp)
1511168793Sthompsa{
1512318329Smav	struct lagg_softc *sc = lp->lp_softc;
1513318329Smav	struct ifnet *ifp = lp->lp_ifp;
1514318329Smav	struct ifnet *scifp = sc->sc_ifp;
1515318329Smav	struct lagg_mc *mc;
1516318329Smav	struct ifmultiaddr *ifma;
1517318329Smav	int error;
1518168793Sthompsa
1519169569Sthompsa	LAGG_WLOCK_ASSERT(sc);
1520318329Smav	IF_ADDR_WLOCK(scifp);
1521318329Smav	TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) {
1522318329Smav		if (ifma->ifma_addr->sa_family != AF_LINK)
1523318329Smav			continue;
1524318329Smav		mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT);
1525318329Smav		if (mc == NULL) {
1526318329Smav			IF_ADDR_WUNLOCK(scifp);
1527318329Smav			return (ENOMEM);
1528318329Smav		}
1529318329Smav		bcopy(ifma->ifma_addr, &mc->mc_addr,
1530318329Smav		    ifma->ifma_addr->sa_len);
1531318329Smav		mc->mc_addr.sdl_index = ifp->if_index;
1532318329Smav		mc->mc_ifma = NULL;
1533318329Smav		SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries);
1534169340Sthompsa	}
1535318329Smav	IF_ADDR_WUNLOCK(scifp);
1536318329Smav	SLIST_FOREACH (mc, &lp->lp_mc_head, mc_entries) {
1537318329Smav		error = if_addmulti(ifp,
1538318329Smav		    (struct sockaddr *)&mc->mc_addr, &mc->mc_ifma);
1539318329Smav		if (error)
1540318329Smav			return (error);
1541318329Smav	}
1542168793Sthompsa	return (0);
1543168793Sthompsa}
1544168793Sthompsa
1545168793Sthompsastatic int
1546318329Smavlagg_clrmulti(struct lagg_port *lp)
1547168793Sthompsa{
1548169327Sthompsa	struct lagg_mc *mc;
1549168793Sthompsa
1550318329Smav	LAGG_WLOCK_ASSERT(lp->lp_softc);
1551318329Smav	while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) {
1552318329Smav		SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries);
1553318329Smav		if (mc->mc_ifma && lp->lp_detaching == 0)
1554318329Smav			if_delmulti_ifma(mc->mc_ifma);
1555318329Smav		free(mc, M_DEVBUF);
1556168793Sthompsa	}
1557168793Sthompsa	return (0);
1558168793Sthompsa}
1559168793Sthompsa
1560318329Smavstatic int
1561318329Smavlagg_setcaps(struct lagg_port *lp, int cap)
1562318329Smav{
1563318329Smav	struct ifreq ifr;
1564318329Smav
1565318329Smav	if (lp->lp_ifp->if_capenable == cap)
1566318329Smav		return (0);
1567318329Smav	if (lp->lp_ioctl == NULL)
1568318329Smav		return (ENXIO);
1569318329Smav	ifr.ifr_reqcap = cap;
1570318329Smav	return ((*lp->lp_ioctl)(lp->lp_ifp, SIOCSIFCAP, (caddr_t)&ifr));
1571318329Smav}
1572318329Smav
1573168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */
1574168793Sthompsastatic int
1575168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status,
1576272175Sglebius    int (*func)(struct ifnet *, int))
1577168793Sthompsa{
1578170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
1579170599Sthompsa	struct ifnet *scifp = sc->sc_ifp;
1580168793Sthompsa	struct ifnet *ifp = lp->lp_ifp;
1581168793Sthompsa	int error;
1582168793Sthompsa
1583318329Smav	LAGG_XLOCK_ASSERT(sc);
1584168793Sthompsa
1585170599Sthompsa	status = status ? (scifp->if_flags & flag) : 0;
1586168793Sthompsa	/* Now "status" contains the flag value or 0 */
1587168793Sthompsa
1588168793Sthompsa	/*
1589168793Sthompsa	 * See if recorded ports status is different from what
1590168793Sthompsa	 * we want it to be.  If it is, flip it.  We record ports
1591168793Sthompsa	 * status in lp_ifflags so that we won't clear ports flag
1592168793Sthompsa	 * we haven't set.  In fact, we don't clear or set ports
1593168793Sthompsa	 * flags directly, but get or release references to them.
1594168793Sthompsa	 * That's why we can be sure that recorded flags still are
1595168793Sthompsa	 * in accord with actual ports flags.
1596168793Sthompsa	 */
1597168793Sthompsa	if (status != (lp->lp_ifflags & flag)) {
1598168793Sthompsa		error = (*func)(ifp, status);
1599168793Sthompsa		if (error)
1600168793Sthompsa			return (error);
1601168793Sthompsa		lp->lp_ifflags &= ~flag;
1602168793Sthompsa		lp->lp_ifflags |= status;
1603168793Sthompsa	}
1604168793Sthompsa	return (0);
1605168793Sthompsa}
1606168793Sthompsa
1607168793Sthompsa/*
1608168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port
1609168793Sthompsa * if "status" is true, update ports flags respective to the lagg
1610168793Sthompsa * if "status" is false, forcedly clear the flags set on port.
1611168793Sthompsa */
1612168793Sthompsastatic int
1613168793Sthompsalagg_setflags(struct lagg_port *lp, int status)
1614168793Sthompsa{
1615168793Sthompsa	int error, i;
1616170599Sthompsa
1617168793Sthompsa	for (i = 0; lagg_pflags[i].flag; i++) {
1618168793Sthompsa		error = lagg_setflag(lp, lagg_pflags[i].flag,
1619168793Sthompsa		    status, lagg_pflags[i].func);
1620168793Sthompsa		if (error)
1621168793Sthompsa			return (error);
1622168793Sthompsa	}
1623168793Sthompsa	return (0);
1624168793Sthompsa}
1625168793Sthompsa
1626240742Sglebiusstatic int
1627240742Sglebiuslagg_transmit(struct ifnet *ifp, struct mbuf *m)
1628168793Sthompsa{
1629168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
1630240742Sglebius	int error, len, mcast;
1631255038Sadrian	struct rm_priotracker tracker;
1632168793Sthompsa
1633240742Sglebius	len = m->m_pkthdr.len;
1634240742Sglebius	mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0;
1635240742Sglebius
1636255038Sadrian	LAGG_RLOCK(sc, &tracker);
1637183160Sthompsa	/* We need a Tx algorithm and at least one port */
1638183160Sthompsa	if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) {
1639255038Sadrian		LAGG_RUNLOCK(sc, &tracker);
1640240742Sglebius		m_freem(m);
1641272242Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1642240742Sglebius		return (ENXIO);
1643183160Sthompsa	}
1644183160Sthompsa
1645240742Sglebius	ETHER_BPF_MTAP(ifp, m);
1646168793Sthompsa
1647272178Sglebius	error = lagg_proto_start(sc, m);
1648255038Sadrian	LAGG_RUNLOCK(sc, &tracker);
1649168793Sthompsa
1650272211Smelifaro	if (error != 0)
1651272242Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1652240742Sglebius
1653240742Sglebius	return (error);
1654168793Sthompsa}
1655168793Sthompsa
1656240742Sglebius/*
1657240742Sglebius * The ifp->if_qflush entry point for lagg(4) is no-op.
1658240742Sglebius */
1659240742Sglebiusstatic void
1660240742Sglebiuslagg_qflush(struct ifnet *ifp __unused)
1661240742Sglebius{
1662240742Sglebius}
1663240742Sglebius
1664168793Sthompsastatic struct mbuf *
1665168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m)
1666168793Sthompsa{
1667168793Sthompsa	struct lagg_port *lp = ifp->if_lagg;
1668170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
1669170599Sthompsa	struct ifnet *scifp = sc->sc_ifp;
1670255038Sadrian	struct rm_priotracker tracker;
1671168793Sthompsa
1672255038Sadrian	LAGG_RLOCK(sc, &tracker);
1673170599Sthompsa	if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
1674169227Sthompsa	    (lp->lp_flags & LAGG_PORT_DISABLED) ||
1675168793Sthompsa	    sc->sc_proto == LAGG_PROTO_NONE) {
1676255038Sadrian		LAGG_RUNLOCK(sc, &tracker);
1677168793Sthompsa		m_freem(m);
1678168793Sthompsa		return (NULL);
1679168793Sthompsa	}
1680168793Sthompsa
1681172825Sthompsa	ETHER_BPF_MTAP(scifp, m);
1682168793Sthompsa
1683280720Sae	if (lp->lp_detaching != 0) {
1684280720Sae		m_freem(m);
1685280720Sae		m = NULL;
1686280720Sae	} else
1687280720Sae		m = lagg_proto_input(sc, lp, m);
1688168793Sthompsa
1689168793Sthompsa	if (m != NULL) {
1690174278Sthompsa		if (scifp->if_flags & IFF_MONITOR) {
1691174278Sthompsa			m_freem(m);
1692174278Sthompsa			m = NULL;
1693174278Sthompsa		}
1694168793Sthompsa	}
1695168793Sthompsa
1696255038Sadrian	LAGG_RUNLOCK(sc, &tracker);
1697168793Sthompsa	return (m);
1698168793Sthompsa}
1699168793Sthompsa
1700168793Sthompsastatic int
1701168793Sthompsalagg_media_change(struct ifnet *ifp)
1702168793Sthompsa{
1703168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
1704168793Sthompsa
1705168793Sthompsa	if (sc->sc_ifflags & IFF_DEBUG)
1706168793Sthompsa		printf("%s\n", __func__);
1707168793Sthompsa
1708168793Sthompsa	/* Ignore */
1709168793Sthompsa	return (0);
1710168793Sthompsa}
1711168793Sthompsa
1712168793Sthompsastatic void
1713168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1714168793Sthompsa{
1715168793Sthompsa	struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
1716168793Sthompsa	struct lagg_port *lp;
1717168793Sthompsa
1718168793Sthompsa	imr->ifm_status = IFM_AVALID;
1719168793Sthompsa	imr->ifm_active = IFM_ETHER | IFM_AUTO;
1720168793Sthompsa
1721318329Smav	LAGG_SLOCK(sc);
1722169340Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1723169340Sthompsa		if (LAGG_PORTACTIVE(lp))
1724169340Sthompsa			imr->ifm_status |= IFM_ACTIVE;
1725169340Sthompsa	}
1726318329Smav	LAGG_SUNLOCK(sc);
1727168793Sthompsa}
1728168793Sthompsa
1729168793Sthompsastatic void
1730173895Sthompsalagg_linkstate(struct lagg_softc *sc)
1731173895Sthompsa{
1732173895Sthompsa	struct lagg_port *lp;
1733173895Sthompsa	int new_link = LINK_STATE_DOWN;
1734186255Sthompsa	uint64_t speed;
1735173895Sthompsa
1736318329Smav	LAGG_XLOCK_ASSERT(sc);
1737318329Smav
1738173895Sthompsa	/* Our link is considered up if at least one of our ports is active */
1739173895Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1740272179Sglebius		if (lp->lp_ifp->if_link_state == LINK_STATE_UP) {
1741173895Sthompsa			new_link = LINK_STATE_UP;
1742173895Sthompsa			break;
1743173895Sthompsa		}
1744173895Sthompsa	}
1745292402Ssmh	if_link_state_change(sc->sc_ifp, new_link);
1746186254Sthompsa
1747186254Sthompsa	/* Update if_baudrate to reflect the max possible speed */
1748186254Sthompsa	switch (sc->sc_proto) {
1749186254Sthompsa		case LAGG_PROTO_FAILOVER:
1750186255Sthompsa			sc->sc_ifp->if_baudrate = sc->sc_primary != NULL ?
1751186255Sthompsa			    sc->sc_primary->lp_ifp->if_baudrate : 0;
1752186254Sthompsa			break;
1753186254Sthompsa		case LAGG_PROTO_ROUNDROBIN:
1754186254Sthompsa		case LAGG_PROTO_LOADBALANCE:
1755272175Sglebius		case LAGG_PROTO_BROADCAST:
1756186255Sthompsa			speed = 0;
1757186254Sthompsa			SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
1758186254Sthompsa				speed += lp->lp_ifp->if_baudrate;
1759186254Sthompsa			sc->sc_ifp->if_baudrate = speed;
1760186254Sthompsa			break;
1761186254Sthompsa		case LAGG_PROTO_LACP:
1762186254Sthompsa			/* LACP updates if_baudrate itself */
1763186254Sthompsa			break;
1764186254Sthompsa	}
1765173895Sthompsa}
1766173895Sthompsa
1767173895Sthompsastatic void
1768292402Ssmhlagg_port_state(struct ifnet *ifp, int state)
1769168793Sthompsa{
1770168793Sthompsa	struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg;
1771168793Sthompsa	struct lagg_softc *sc = NULL;
1772168793Sthompsa
1773168793Sthompsa	if (lp != NULL)
1774170599Sthompsa		sc = lp->lp_softc;
1775168793Sthompsa	if (sc == NULL)
1776168793Sthompsa		return;
1777168793Sthompsa
1778318329Smav	LAGG_XLOCK(sc);
1779173895Sthompsa	lagg_linkstate(sc);
1780272178Sglebius	lagg_proto_linkstate(sc, lp);
1781318329Smav	LAGG_XUNLOCK(sc);
1782168793Sthompsa}
1783168793Sthompsa
1784292402Ssmhstruct lagg_port *
1785168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp)
1786168793Sthompsa{
1787168793Sthompsa	struct lagg_port *lp_next, *rval = NULL;
1788168793Sthompsa
1789168793Sthompsa	/*
1790168793Sthompsa	 * Search a port which reports an active link state.
1791168793Sthompsa	 */
1792168793Sthompsa
1793168793Sthompsa	if (lp == NULL)
1794168793Sthompsa		goto search;
1795168793Sthompsa	if (LAGG_PORTACTIVE(lp)) {
1796168793Sthompsa		rval = lp;
1797168793Sthompsa		goto found;
1798168793Sthompsa	}
1799168793Sthompsa	if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL &&
1800168793Sthompsa	    LAGG_PORTACTIVE(lp_next)) {
1801168793Sthompsa		rval = lp_next;
1802168793Sthompsa		goto found;
1803168793Sthompsa	}
1804168793Sthompsa
1805168793Sthompsasearch:
1806168793Sthompsa	SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) {
1807168793Sthompsa		if (LAGG_PORTACTIVE(lp_next)) {
1808168793Sthompsa			rval = lp_next;
1809168793Sthompsa			goto found;
1810168793Sthompsa		}
1811168793Sthompsa	}
1812168793Sthompsa
1813168793Sthompsafound:
1814168793Sthompsa	return (rval);
1815168793Sthompsa}
1816168793Sthompsa
1817168793Sthompsaint
1818168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m)
1819168793Sthompsa{
1820168793Sthompsa
1821185164Skmacy	return (ifp->if_transmit)(ifp, m);
1822168793Sthompsa}
1823168793Sthompsa
1824168793Sthompsa/*
1825168793Sthompsa * Simple round robin aggregation
1826168793Sthompsa */
1827272161Sglebiusstatic void
1828168793Sthompsalagg_rr_attach(struct lagg_softc *sc)
1829168793Sthompsa{
1830172554Sthompsa	sc->sc_seq = 0;
1831294615Saraujo	sc->sc_bkt_count = sc->sc_bkt;
1832168793Sthompsa}
1833168793Sthompsa
1834168793Sthompsastatic int
1835168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m)
1836168793Sthompsa{
1837172554Sthompsa	struct lagg_port *lp;
1838172554Sthompsa	uint32_t p;
1839168793Sthompsa
1840294615Saraujo	if (sc->sc_bkt_count == 0 && sc->sc_bkt > 0)
1841294615Saraujo		sc->sc_bkt_count = sc->sc_bkt;
1842294615Saraujo
1843294615Saraujo	if (sc->sc_bkt > 0) {
1844294615Saraujo		atomic_subtract_int(&sc->sc_bkt_count, 1);
1845294615Saraujo	if (atomic_cmpset_int(&sc->sc_bkt_count, 0, sc->sc_bkt))
1846294615Saraujo		p = atomic_fetchadd_32(&sc->sc_seq, 1);
1847294615Saraujo	else
1848294615Saraujo		p = sc->sc_seq;
1849294615Saraujo	} else
1850294615Saraujo		p = atomic_fetchadd_32(&sc->sc_seq, 1);
1851294615Saraujo
1852172554Sthompsa	p %= sc->sc_count;
1853172554Sthompsa	lp = SLIST_FIRST(&sc->sc_ports);
1854294615Saraujo
1855172554Sthompsa	while (p--)
1856172554Sthompsa		lp = SLIST_NEXT(lp, lp_entries);
1857172554Sthompsa
1858172554Sthompsa	/*
1859172554Sthompsa	 * Check the port's link state. This will return the next active
1860172554Sthompsa	 * port if the link is down or the port is NULL.
1861172554Sthompsa	 */
1862172554Sthompsa	if ((lp = lagg_link_active(sc, lp)) == NULL) {
1863172554Sthompsa		m_freem(m);
1864251859Sdelphij		return (ENETDOWN);
1865172554Sthompsa	}
1866168793Sthompsa
1867168793Sthompsa	/* Send mbuf */
1868172554Sthompsa	return (lagg_enqueue(lp->lp_ifp, m));
1869168793Sthompsa}
1870168793Sthompsa
1871168793Sthompsastatic struct mbuf *
1872168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
1873168793Sthompsa{
1874168793Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1875168793Sthompsa
1876168793Sthompsa	/* Just pass in the packet to our lagg device */
1877168793Sthompsa	m->m_pkthdr.rcvif = ifp;
1878168793Sthompsa
1879168793Sthompsa	return (m);
1880168793Sthompsa}
1881168793Sthompsa
1882168793Sthompsa/*
1883271732Saraujo * Broadcast mode
1884271732Saraujo */
1885271732Saraujostatic int
1886271732Saraujolagg_bcast_start(struct lagg_softc *sc, struct mbuf *m)
1887271732Saraujo{
1888272175Sglebius	int active_ports = 0;
1889272175Sglebius	int errors = 0;
1890272175Sglebius	int ret;
1891272175Sglebius	struct lagg_port *lp, *last = NULL;
1892272175Sglebius	struct mbuf *m0;
1893271732Saraujo
1894272175Sglebius	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
1895272175Sglebius		if (!LAGG_PORTACTIVE(lp))
1896272175Sglebius			continue;
1897271732Saraujo
1898272175Sglebius		active_ports++;
1899271732Saraujo
1900272175Sglebius		if (last != NULL) {
1901272175Sglebius			m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT);
1902272175Sglebius			if (m0 == NULL) {
1903272175Sglebius				ret = ENOBUFS;
1904272175Sglebius				errors++;
1905272175Sglebius				break;
1906272175Sglebius			}
1907271732Saraujo
1908272175Sglebius			ret = lagg_enqueue(last->lp_ifp, m0);
1909272175Sglebius			if (ret != 0)
1910272175Sglebius				errors++;
1911272175Sglebius		}
1912272175Sglebius		last = lp;
1913272175Sglebius	}
1914272175Sglebius	if (last == NULL) {
1915272175Sglebius		m_freem(m);
1916272175Sglebius		return (ENOENT);
1917272175Sglebius	}
1918272175Sglebius	if ((last = lagg_link_active(sc, last)) == NULL) {
1919272175Sglebius		m_freem(m);
1920272175Sglebius		return (ENETDOWN);
1921272175Sglebius	}
1922271732Saraujo
1923272175Sglebius	ret = lagg_enqueue(last->lp_ifp, m);
1924272175Sglebius	if (ret != 0)
1925272175Sglebius		errors++;
1926271732Saraujo
1927272175Sglebius	if (errors == 0)
1928272175Sglebius		return (ret);
1929271732Saraujo
1930272175Sglebius	return (0);
1931271732Saraujo}
1932271732Saraujo
1933271732Saraujostatic struct mbuf*
1934272175Sglebiuslagg_bcast_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
1935271732Saraujo{
1936272175Sglebius	struct ifnet *ifp = sc->sc_ifp;
1937271732Saraujo
1938272175Sglebius	/* Just pass in the packet to our lagg device */
1939272175Sglebius	m->m_pkthdr.rcvif = ifp;
1940272175Sglebius	return (m);
1941271732Saraujo}
1942271732Saraujo
1943271732Saraujo/*
1944168793Sthompsa * Active failover
1945168793Sthompsa */
1946168793Sthompsastatic int
1947168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m)
1948168793Sthompsa{
1949168793Sthompsa	struct lagg_port *lp;
1950168793Sthompsa
1951168793Sthompsa	/* Use the master port if active or the next available port */
1952172554Sthompsa	if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) {
1953172554Sthompsa		m_freem(m);
1954251859Sdelphij		return (ENETDOWN);
1955172554Sthompsa	}
1956168793Sthompsa
1957168793Sthompsa	/* Send mbuf */
1958168793Sthompsa	return (lagg_enqueue(lp->lp_ifp, m));
1959168793Sthompsa}
1960168793Sthompsa
1961168793Sthompsastatic struct mbuf *
1962168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
1963168793Sthompsa{
1964168793Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1965168793Sthompsa	struct lagg_port *tmp_tp;
1966168793Sthompsa
1967272386Shrs	if (lp == sc->sc_primary || V_lagg_failover_rx_all) {
1968168793Sthompsa		m->m_pkthdr.rcvif = ifp;
1969168793Sthompsa		return (m);
1970168793Sthompsa	}
1971168793Sthompsa
1972174742Sthompsa	if (!LAGG_PORTACTIVE(sc->sc_primary)) {
1973174742Sthompsa		tmp_tp = lagg_link_active(sc, sc->sc_primary);
1974168793Sthompsa		/*
1975298995Spfg		 * If tmp_tp is null, we've received a packet when all
1976168793Sthompsa		 * our links are down. Weird, but process it anyways.
1977168793Sthompsa		 */
1978168793Sthompsa		if ((tmp_tp == NULL || tmp_tp == lp)) {
1979168793Sthompsa			m->m_pkthdr.rcvif = ifp;
1980168793Sthompsa			return (m);
1981168793Sthompsa		}
1982168793Sthompsa	}
1983168793Sthompsa
1984168793Sthompsa	m_freem(m);
1985168793Sthompsa	return (NULL);
1986168793Sthompsa}
1987168793Sthompsa
1988168793Sthompsa/*
1989168793Sthompsa * Loadbalancing
1990168793Sthompsa */
1991272161Sglebiusstatic void
1992168793Sthompsalagg_lb_attach(struct lagg_softc *sc)
1993168793Sthompsa{
1994168793Sthompsa	struct lagg_port *lp;
1995168793Sthompsa	struct lagg_lb *lb;
1996168793Sthompsa
1997272161Sglebius	lb = malloc(sizeof(struct lagg_lb), M_DEVBUF, M_WAITOK | M_ZERO);
1998279891Shselasky	lb->lb_key = m_ether_tcpip_hash_init();
1999272175Sglebius	sc->sc_psc = lb;
2000168793Sthompsa
2001168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
2002168793Sthompsa		lagg_lb_port_create(lp);
2003168793Sthompsa}
2004168793Sthompsa
2005272158Sglebiusstatic void
2006168793Sthompsalagg_lb_detach(struct lagg_softc *sc)
2007168793Sthompsa{
2008272158Sglebius	struct lagg_lb *lb;
2009272158Sglebius
2010272158Sglebius	lb = (struct lagg_lb *)sc->sc_psc;
2011272161Sglebius	LAGG_WUNLOCK(sc);
2012168793Sthompsa	if (lb != NULL)
2013168793Sthompsa		free(lb, M_DEVBUF);
2014168793Sthompsa}
2015168793Sthompsa
2016168793Sthompsastatic int
2017168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp)
2018168793Sthompsa{
2019168793Sthompsa	struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc;
2020168793Sthompsa	struct lagg_port *lp_next;
2021168793Sthompsa	int i = 0;
2022168793Sthompsa
2023168793Sthompsa	bzero(&lb->lb_ports, sizeof(lb->lb_ports));
2024168793Sthompsa	SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) {
2025168793Sthompsa		if (lp_next == lp)
2026168793Sthompsa			continue;
2027168793Sthompsa		if (i >= LAGG_MAX_PORTS)
2028168793Sthompsa			return (EINVAL);
2029168793Sthompsa		if (sc->sc_ifflags & IFF_DEBUG)
2030168793Sthompsa			printf("%s: port %s at index %d\n",
2031272179Sglebius			    sc->sc_ifname, lp_next->lp_ifp->if_xname, i);
2032168793Sthompsa		lb->lb_ports[i++] = lp_next;
2033168793Sthompsa	}
2034168793Sthompsa
2035168793Sthompsa	return (0);
2036168793Sthompsa}
2037168793Sthompsa
2038168793Sthompsastatic int
2039168793Sthompsalagg_lb_port_create(struct lagg_port *lp)
2040168793Sthompsa{
2041170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
2042168793Sthompsa	return (lagg_lb_porttable(sc, NULL));
2043168793Sthompsa}
2044168793Sthompsa
2045168793Sthompsastatic void
2046168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp)
2047168793Sthompsa{
2048170599Sthompsa	struct lagg_softc *sc = lp->lp_softc;
2049168793Sthompsa	lagg_lb_porttable(sc, lp);
2050168793Sthompsa}
2051168793Sthompsa
2052168793Sthompsastatic int
2053168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m)
2054168793Sthompsa{
2055168793Sthompsa	struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc;
2056168793Sthompsa	struct lagg_port *lp = NULL;
2057168793Sthompsa	uint32_t p = 0;
2058168793Sthompsa
2059275358Shselasky	if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) &&
2060275358Shselasky	    M_HASHTYPE_GET(m) != M_HASHTYPE_NONE)
2061260070Sscottl		p = m->m_pkthdr.flowid >> sc->flowid_shift;
2062191692Sthompsa	else
2063279891Shselasky		p = m_ether_tcpip_hash(sc->sc_flags, m, lb->lb_key);
2064180249Sthompsa	p %= sc->sc_count;
2065180249Sthompsa	lp = lb->lb_ports[p];
2066168793Sthompsa
2067168793Sthompsa	/*
2068168793Sthompsa	 * Check the port's link state. This will return the next active
2069168793Sthompsa	 * port if the link is down or the port is NULL.
2070168793Sthompsa	 */
2071172554Sthompsa	if ((lp = lagg_link_active(sc, lp)) == NULL) {
2072172554Sthompsa		m_freem(m);
2073251859Sdelphij		return (ENETDOWN);
2074172554Sthompsa	}
2075168793Sthompsa
2076168793Sthompsa	/* Send mbuf */
2077168793Sthompsa	return (lagg_enqueue(lp->lp_ifp, m));
2078168793Sthompsa}
2079168793Sthompsa
2080168793Sthompsastatic struct mbuf *
2081168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
2082168793Sthompsa{
2083168793Sthompsa	struct ifnet *ifp = sc->sc_ifp;
2084168793Sthompsa
2085168793Sthompsa	/* Just pass in the packet to our lagg device */
2086168793Sthompsa	m->m_pkthdr.rcvif = ifp;
2087168793Sthompsa
2088168793Sthompsa	return (m);
2089168793Sthompsa}
2090168793Sthompsa
2091168793Sthompsa/*
2092168793Sthompsa * 802.3ad LACP
2093168793Sthompsa */
2094272161Sglebiusstatic void
2095168793Sthompsalagg_lacp_attach(struct lagg_softc *sc)
2096168793Sthompsa{
2097168793Sthompsa	struct lagg_port *lp;
2098168793Sthompsa
2099272161Sglebius	lacp_attach(sc);
2100168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
2101168793Sthompsa		lacp_port_create(lp);
2102168793Sthompsa}
2103168793Sthompsa
2104272158Sglebiusstatic void
2105168793Sthompsalagg_lacp_detach(struct lagg_softc *sc)
2106168793Sthompsa{
2107168793Sthompsa	struct lagg_port *lp;
2108272161Sglebius	void *psc;
2109168793Sthompsa
2110168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
2111168793Sthompsa		lacp_port_destroy(lp);
2112168793Sthompsa
2113272161Sglebius	psc = sc->sc_psc;
2114272161Sglebius	sc->sc_psc = NULL;
2115169569Sthompsa	LAGG_WUNLOCK(sc);
2116272161Sglebius
2117272161Sglebius	lacp_detach(psc);
2118168793Sthompsa}
2119168793Sthompsa
2120168793Sthompsastatic void
2121168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc)
2122168793Sthompsa{
2123168793Sthompsa	struct lagg_port *lp;
2124168793Sthompsa
2125318329Smav	LAGG_SXLOCK_ASSERT(sc);
2126318329Smav
2127168793Sthompsa	/* purge all the lacp ports */
2128168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
2129168793Sthompsa		lacp_port_destroy(lp);
2130168793Sthompsa
2131168793Sthompsa	/* add them back in */
2132168793Sthompsa	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
2133168793Sthompsa		lacp_port_create(lp);
2134168793Sthompsa}
2135168793Sthompsa
2136168793Sthompsastatic int
2137168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m)
2138168793Sthompsa{
2139168793Sthompsa	struct lagg_port *lp;
2140168793Sthompsa
2141168793Sthompsa	lp = lacp_select_tx_port(sc, m);
2142172554Sthompsa	if (lp == NULL) {
2143172554Sthompsa		m_freem(m);
2144245741Sglebius		return (ENETDOWN);
2145172554Sthompsa	}
2146168793Sthompsa
2147168793Sthompsa	/* Send mbuf */
2148168793Sthompsa	return (lagg_enqueue(lp->lp_ifp, m));
2149168793Sthompsa}
2150168793Sthompsa
2151168793Sthompsastatic struct mbuf *
2152168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
2153168793Sthompsa{
2154168793Sthompsa	struct ifnet *ifp = sc->sc_ifp;
2155168793Sthompsa	struct ether_header *eh;
2156168793Sthompsa	u_short etype;
2157168793Sthompsa
2158168793Sthompsa	eh = mtod(m, struct ether_header *);
2159168793Sthompsa	etype = ntohs(eh->ether_type);
2160168793Sthompsa
2161168793Sthompsa	/* Tap off LACP control messages */
2162221270Sthompsa	if ((m->m_flags & M_VLANTAG) == 0 && etype == ETHERTYPE_SLOW) {
2163175005Sthompsa		m = lacp_input(lp, m);
2164175005Sthompsa		if (m == NULL)
2165175005Sthompsa			return (NULL);
2166168793Sthompsa	}
2167168793Sthompsa
2168168793Sthompsa	/*
2169168793Sthompsa	 * If the port is not collecting or not in the active aggregator then
2170168793Sthompsa	 * free and return.
2171168793Sthompsa	 */
2172177274Sthompsa	if (lacp_iscollecting(lp) == 0 || lacp_isactive(lp) == 0) {
2173168793Sthompsa		m_freem(m);
2174168793Sthompsa		return (NULL);
2175168793Sthompsa	}
2176168793Sthompsa
2177168793Sthompsa	m->m_pkthdr.rcvif = ifp;
2178168793Sthompsa	return (m);
2179168793Sthompsa}
2180249506Sglebius
2181