ieee8023ad_lacp.c revision 168793
1168561Sthompsa/*	$NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $	*/
2168561Sthompsa
3168561Sthompsa/*-
4168561Sthompsa * Copyright (c)2005 YAMAMOTO Takashi,
5168561Sthompsa * All rights reserved.
6168561Sthompsa *
7168561Sthompsa * Redistribution and use in source and binary forms, with or without
8168561Sthompsa * modification, are permitted provided that the following conditions
9168561Sthompsa * are met:
10168561Sthompsa * 1. Redistributions of source code must retain the above copyright
11168561Sthompsa *    notice, this list of conditions and the following disclaimer.
12168561Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
13168561Sthompsa *    notice, this list of conditions and the following disclaimer in the
14168561Sthompsa *    documentation and/or other materials provided with the distribution.
15168561Sthompsa *
16168561Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17168561Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18168561Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19168561Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20168561Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21168561Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22168561Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23168561Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24168561Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25168561Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26168561Sthompsa * SUCH DAMAGE.
27168561Sthompsa */
28168561Sthompsa
29168561Sthompsa#include <sys/cdefs.h>
30168561Sthompsa__FBSDID("$FreeBSD: head/sys/net/ieee8023ad_lacp.c 168793 2007-04-17 00:35:11Z thompsa $");
31168561Sthompsa
32168561Sthompsa#include <sys/param.h>
33168561Sthompsa#include <sys/callout.h>
34168561Sthompsa#include <sys/mbuf.h>
35168561Sthompsa#include <sys/systm.h>
36168561Sthompsa#include <sys/malloc.h>
37168561Sthompsa#include <sys/kernel.h> /* hz */
38168561Sthompsa#include <sys/socket.h> /* for net/if.h */
39168561Sthompsa#include <sys/sockio.h>
40168561Sthompsa#include <machine/stdarg.h>
41168561Sthompsa
42168561Sthompsa#include <net/if.h>
43168561Sthompsa#include <net/if_dl.h>
44168561Sthompsa#include <net/ethernet.h>
45168561Sthompsa#include <net/if_media.h>
46168561Sthompsa#include <net/if_types.h>
47168561Sthompsa
48168793Sthompsa#include <net/if_lagg.h>
49168561Sthompsa#include <net/ieee8023ad_lacp.h>
50168561Sthompsa
51168561Sthompsa/*
52168561Sthompsa * actor system priority and port priority.
53168561Sthompsa * XXX should be configurable.
54168561Sthompsa */
55168561Sthompsa
56168561Sthompsa#define	LACP_SYSTEM_PRIO	0x8000
57168561Sthompsa#define	LACP_PORT_PRIO		0x8000
58168561Sthompsa
59168561Sthompsaconst uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] =
60168561Sthompsa    { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
61168561Sthompsa
62168561Sthompsastatic const struct tlv_template lacp_info_tlv_template[] = {
63168561Sthompsa	{ LACP_TYPE_ACTORINFO,
64168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) },
65168561Sthompsa	{ LACP_TYPE_PARTNERINFO,
66168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) },
67168561Sthompsa	{ LACP_TYPE_COLLECTORINFO,
68168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) },
69168561Sthompsa	{ 0, 0 },
70168561Sthompsa};
71168561Sthompsa
72168561Sthompsatypedef void (*lacp_timer_func_t)(struct lacp_port *);
73168561Sthompsa
74168561Sthompsastatic const struct tlv_template marker_info_tlv_template[] = {
75168561Sthompsa	{ MARKER_TYPE_INFO, 16 },
76168561Sthompsa	{ 0, 0 },
77168561Sthompsa};
78168561Sthompsa
79168561Sthompsastatic const struct tlv_template marker_response_tlv_template[] = {
80168561Sthompsa	{ MARKER_TYPE_RESPONSE, 16 },
81168561Sthompsa	{ 0, 0 },
82168561Sthompsa};
83168561Sthompsa
84168561Sthompsastatic void	lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *);
85168561Sthompsa
86168561Sthompsastatic uint64_t	lacp_aggregator_bandwidth(struct lacp_aggregator *);
87168561Sthompsastatic void	lacp_suppress_distributing(struct lacp_softc *,
88168561Sthompsa		    struct lacp_aggregator *);
89168561Sthompsastatic void	lacp_transit_expire(void *);
90168561Sthompsastatic void	lacp_select_active_aggregator(struct lacp_softc *);
91168561Sthompsastatic uint16_t	lacp_compose_key(struct lacp_port *);
92168561Sthompsastatic int	tlv_check(const void *, size_t, const struct tlvhdr *,
93168561Sthompsa		    const struct tlv_template *, boolean_t);
94168561Sthompsastatic void	lacp_tick(void *);
95168561Sthompsa
96168561Sthompsastatic void	lacp_fill_aggregator_id(struct lacp_aggregator *,
97168561Sthompsa		    const struct lacp_port *);
98168561Sthompsastatic void	lacp_fill_aggregator_id_peer(struct lacp_peerinfo *,
99168561Sthompsa		    const struct lacp_peerinfo *);
100168561Sthompsastatic int	lacp_aggregator_is_compatible(const struct lacp_aggregator *,
101168561Sthompsa		    const struct lacp_port *);
102168561Sthompsastatic int	lacp_peerinfo_is_compatible(const struct lacp_peerinfo *,
103168561Sthompsa		    const struct lacp_peerinfo *);
104168561Sthompsa
105168561Sthompsastatic struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *,
106168561Sthompsa		    struct lacp_port *);
107168561Sthompsastatic void	lacp_aggregator_addref(struct lacp_softc *,
108168561Sthompsa		    struct lacp_aggregator *);
109168561Sthompsastatic void	lacp_aggregator_delref(struct lacp_softc *,
110168561Sthompsa		    struct lacp_aggregator *);
111168561Sthompsa
112168561Sthompsa/* receive machine */
113168561Sthompsa
114168561Sthompsastatic void	lacp_sm_rx(struct lacp_port *, const struct lacpdu *);
115168561Sthompsastatic void	lacp_sm_rx_timer(struct lacp_port *);
116168561Sthompsastatic void	lacp_sm_rx_set_expired(struct lacp_port *);
117168561Sthompsastatic void	lacp_sm_rx_update_ntt(struct lacp_port *,
118168561Sthompsa		    const struct lacpdu *);
119168561Sthompsastatic void	lacp_sm_rx_record_pdu(struct lacp_port *,
120168561Sthompsa		    const struct lacpdu *);
121168561Sthompsastatic void	lacp_sm_rx_update_selected(struct lacp_port *,
122168561Sthompsa		    const struct lacpdu *);
123168561Sthompsastatic void	lacp_sm_rx_record_default(struct lacp_port *);
124168561Sthompsastatic void	lacp_sm_rx_update_default_selected(struct lacp_port *);
125168561Sthompsastatic void	lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *,
126168561Sthompsa		    const struct lacp_peerinfo *);
127168561Sthompsa
128168561Sthompsa/* mux machine */
129168561Sthompsa
130168561Sthompsastatic void	lacp_sm_mux(struct lacp_port *);
131168561Sthompsastatic void	lacp_set_mux(struct lacp_port *, enum lacp_mux_state);
132168561Sthompsastatic void	lacp_sm_mux_timer(struct lacp_port *);
133168561Sthompsa
134168561Sthompsa/* periodic transmit machine */
135168561Sthompsa
136168561Sthompsastatic void	lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t);
137168561Sthompsastatic void	lacp_sm_ptx_tx_schedule(struct lacp_port *);
138168561Sthompsastatic void	lacp_sm_ptx_timer(struct lacp_port *);
139168561Sthompsa
140168561Sthompsa/* transmit machine */
141168561Sthompsa
142168561Sthompsastatic void	lacp_sm_tx(struct lacp_port *);
143168561Sthompsastatic void	lacp_sm_assert_ntt(struct lacp_port *);
144168561Sthompsa
145168561Sthompsastatic void	lacp_run_timers(struct lacp_port *);
146168561Sthompsastatic int	lacp_compare_peerinfo(const struct lacp_peerinfo *,
147168561Sthompsa		    const struct lacp_peerinfo *);
148168561Sthompsastatic int	lacp_compare_systemid(const struct lacp_systemid *,
149168561Sthompsa		    const struct lacp_systemid *);
150168561Sthompsastatic void	lacp_port_enable(struct lacp_port *);
151168561Sthompsastatic void	lacp_port_disable(struct lacp_port *);
152168561Sthompsastatic void	lacp_select(struct lacp_port *);
153168561Sthompsastatic void	lacp_unselect(struct lacp_port *);
154168561Sthompsastatic void	lacp_disable_collecting(struct lacp_port *);
155168561Sthompsastatic void	lacp_enable_collecting(struct lacp_port *);
156168561Sthompsastatic void	lacp_disable_distributing(struct lacp_port *);
157168561Sthompsastatic void	lacp_enable_distributing(struct lacp_port *);
158168561Sthompsastatic int	lacp_xmit_lacpdu(struct lacp_port *);
159168561Sthompsa
160168561Sthompsa#if defined(LACP_DEBUG)
161168561Sthompsastatic void	lacp_dump_lacpdu(const struct lacpdu *);
162168561Sthompsastatic const char *lacp_format_partner(const struct lacp_peerinfo *, char *,
163168561Sthompsa		    size_t);
164168561Sthompsastatic const char *lacp_format_lagid(const struct lacp_peerinfo *,
165168561Sthompsa		    const struct lacp_peerinfo *, char *, size_t);
166168561Sthompsastatic const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *,
167168561Sthompsa		    char *, size_t);
168168561Sthompsastatic const char *lacp_format_state(uint8_t, char *, size_t);
169168561Sthompsastatic const char *lacp_format_mac(const uint8_t *, char *, size_t);
170168561Sthompsastatic const char *lacp_format_systemid(const struct lacp_systemid *, char *,
171168561Sthompsa		    size_t);
172168561Sthompsastatic const char *lacp_format_portid(const struct lacp_portid *, char *,
173168561Sthompsa		    size_t);
174168561Sthompsastatic void	lacp_dprintf(const struct lacp_port *, const char *, ...)
175168561Sthompsa		    __attribute__((__format__(__printf__, 2, 3)));
176168561Sthompsa#define	LACP_DPRINTF(a)	lacp_dprintf a
177168561Sthompsa#else
178168561Sthompsa#define LACP_DPRINTF(a) /* nothing */
179168561Sthompsa#endif
180168561Sthompsa
181168561Sthompsa/*
182168561Sthompsa * partner administration variables.
183168561Sthompsa * XXX should be configurable.
184168561Sthompsa */
185168561Sthompsa
186168561Sthompsastatic const struct lacp_peerinfo lacp_partner_admin = {
187168561Sthompsa	.lip_systemid = { .lsi_prio = 0xffff },
188168561Sthompsa	.lip_portid = { .lpi_prio = 0xffff },
189168561Sthompsa#if 1
190168561Sthompsa	/* optimistic */
191168561Sthompsa	.lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION |
192168561Sthompsa	    LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING,
193168561Sthompsa#else
194168561Sthompsa	/* pessimistic */
195168561Sthompsa	.lip_state = 0,
196168561Sthompsa#endif
197168561Sthompsa};
198168561Sthompsa
199168561Sthompsastatic const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = {
200168561Sthompsa	[LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer,
201168561Sthompsa	[LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer,
202168561Sthompsa	[LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer,
203168561Sthompsa};
204168561Sthompsa
205168561Sthompsa/*
206168561Sthompsa * lacp_input: process lacpdu
207168561Sthompsa */
208168561Sthompsaint
209168793Sthompsalacp_input(struct lagg_port *lgp, struct mbuf *m)
210168561Sthompsa{
211168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
212168561Sthompsa	struct lacpdu *du;
213168561Sthompsa	int error = 0;
214168561Sthompsa
215168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
216168561Sthompsa
217168561Sthompsa	if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) {
218168561Sthompsa		goto bad;
219168561Sthompsa	}
220168561Sthompsa
221168561Sthompsa	if (m->m_pkthdr.len != sizeof(*du)) {
222168561Sthompsa		goto bad;
223168561Sthompsa	}
224168561Sthompsa
225168561Sthompsa	if ((m->m_flags & M_MCAST) == 0) {
226168561Sthompsa		goto bad;
227168561Sthompsa	}
228168561Sthompsa
229168561Sthompsa	if (m->m_len < sizeof(*du)) {
230168561Sthompsa		m = m_pullup(m, sizeof(*du));
231168561Sthompsa		if (m == NULL) {
232168561Sthompsa			return (ENOMEM);
233168561Sthompsa		}
234168561Sthompsa	}
235168561Sthompsa
236168561Sthompsa	du = mtod(m, struct lacpdu *);
237168561Sthompsa
238168561Sthompsa	if (memcmp(&du->ldu_eh.ether_dhost,
239168561Sthompsa	    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) {
240168561Sthompsa		goto bad;
241168561Sthompsa	}
242168561Sthompsa
243168561Sthompsa	/* XXX
244168561Sthompsa	KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP,
245168561Sthompsa	    ("a very bad kassert!"));
246168561Sthompsa	*/
247168561Sthompsa
248168561Sthompsa	/*
249168561Sthompsa	 * ignore the version for compatibility with
250168561Sthompsa	 * the future protocol revisions.
251168561Sthompsa	 */
252168561Sthompsa
253168561Sthompsa#if 0
254168561Sthompsa	if (du->ldu_sph.sph_version != 1) {
255168561Sthompsa		goto bad;
256168561Sthompsa	}
257168561Sthompsa#endif
258168561Sthompsa
259168561Sthompsa	/*
260168561Sthompsa	 * ignore tlv types for compatibility with
261168561Sthompsa	 * the future protocol revisions.
262168561Sthompsa	 */
263168561Sthompsa
264168561Sthompsa	if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor,
265168561Sthompsa	    lacp_info_tlv_template, FALSE)) {
266168561Sthompsa		goto bad;
267168561Sthompsa	}
268168561Sthompsa
269168561Sthompsa#if defined(LACP_DEBUG)
270168561Sthompsa	LACP_DPRINTF((lp, "lacpdu receive\n"));
271168561Sthompsa	lacp_dump_lacpdu(du);
272168561Sthompsa#endif /* defined(LACP_DEBUG) */
273168561Sthompsa	lacp_sm_rx(lp, du);
274168561Sthompsa
275168561Sthompsa	m_freem(m);
276168561Sthompsa
277168561Sthompsa	return (error);
278168561Sthompsa
279168561Sthompsabad:
280168561Sthompsa	m_freem(m);
281168561Sthompsa	return (EINVAL);
282168561Sthompsa}
283168561Sthompsa
284168561Sthompsastatic void
285168561Sthompsalacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info)
286168561Sthompsa{
287168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
288168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
289168561Sthompsa
290168561Sthompsa	info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO);
291168561Sthompsa	memcpy(&info->lip_systemid.lsi_mac,
292168793Sthompsa	    IF_LLADDR(lgs->sc_ifp), ETHER_ADDR_LEN);
293168561Sthompsa	info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO);
294168561Sthompsa	info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index);
295168561Sthompsa	info->lip_state = lp->lp_state;
296168561Sthompsa}
297168561Sthompsa
298168561Sthompsastatic int
299168561Sthompsalacp_xmit_lacpdu(struct lacp_port *lp)
300168561Sthompsa{
301168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
302168561Sthompsa	struct mbuf *m;
303168561Sthompsa	struct lacpdu *du;
304168561Sthompsa	int error;
305168561Sthompsa
306168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
307168561Sthompsa
308168561Sthompsa	m = m_gethdr(M_DONTWAIT, MT_DATA);
309168561Sthompsa	if (m == NULL) {
310168561Sthompsa		return (ENOMEM);
311168561Sthompsa	}
312168561Sthompsa	m->m_len = m->m_pkthdr.len = sizeof(*du);
313168561Sthompsa
314168561Sthompsa	du = mtod(m, struct lacpdu *);
315168561Sthompsa	memset(du, 0, sizeof(*du));
316168561Sthompsa
317168561Sthompsa	memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols,
318168561Sthompsa	    ETHER_ADDR_LEN);
319168793Sthompsa	memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN);
320168561Sthompsa	du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW);
321168561Sthompsa
322168561Sthompsa	du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP;
323168561Sthompsa	du->ldu_sph.sph_version = 1;
324168561Sthompsa
325168561Sthompsa	TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor));
326168561Sthompsa	du->ldu_actor = lp->lp_actor;
327168561Sthompsa
328168561Sthompsa	TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO,
329168561Sthompsa	    sizeof(du->ldu_partner));
330168561Sthompsa	du->ldu_partner = lp->lp_partner;
331168561Sthompsa
332168561Sthompsa	TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO,
333168561Sthompsa	    sizeof(du->ldu_collector));
334168561Sthompsa	du->ldu_collector.lci_maxdelay = 0;
335168561Sthompsa
336168561Sthompsa#if defined(LACP_DEBUG)
337168561Sthompsa	LACP_DPRINTF((lp, "lacpdu transmit\n"));
338168561Sthompsa	lacp_dump_lacpdu(du);
339168561Sthompsa#endif /* defined(LACP_DEBUG) */
340168561Sthompsa
341168561Sthompsa	m->m_flags |= M_MCAST;
342168561Sthompsa
343168561Sthompsa	/*
344168561Sthompsa	 * XXX should use higher priority queue.
345168561Sthompsa	 * otherwise network congestion can break aggregation.
346168561Sthompsa	 */
347168561Sthompsa
348168793Sthompsa	error = lagg_enqueue(lp->lp_ifp, m);
349168561Sthompsa	return (error);
350168561Sthompsa}
351168561Sthompsa
352168561Sthompsavoid
353168793Sthompsalacp_linkstate(struct lagg_port *lgp)
354168561Sthompsa{
355168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
356168793Sthompsa	struct ifnet *ifp = lgp->lp_ifp;
357168561Sthompsa	struct ifmediareq ifmr;
358168561Sthompsa	int error = 0;
359168561Sthompsa	u_int media;
360168561Sthompsa	uint8_t old_state;
361168561Sthompsa	uint16_t old_key;
362168561Sthompsa
363168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
364168561Sthompsa
365168561Sthompsa	bzero((char *)&ifmr, sizeof(ifmr));
366168561Sthompsa	error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
367168561Sthompsa	if (error != 0)
368168561Sthompsa		return;
369168561Sthompsa
370168561Sthompsa	media = ifmr.ifm_active;
371168561Sthompsa	LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x\n", lp->lp_media, media));
372168561Sthompsa	old_state = lp->lp_state;
373168561Sthompsa	old_key = lp->lp_key;
374168561Sthompsa
375168561Sthompsa	lp->lp_media = media;
376168561Sthompsa	if ((media & IFM_HDX) != 0 || ifp->if_link_state == LINK_STATE_DOWN) {
377168561Sthompsa		lacp_port_disable(lp);
378168561Sthompsa	} else {
379168561Sthompsa		lacp_port_enable(lp);
380168561Sthompsa	}
381168561Sthompsa	lp->lp_key = lacp_compose_key(lp);
382168561Sthompsa
383168561Sthompsa	if (old_state != lp->lp_state || old_key != lp->lp_key) {
384168561Sthompsa		LACP_DPRINTF((lp, "-> UNSELECTED\n"));
385168561Sthompsa		lp->lp_selected = LACP_UNSELECTED;
386168561Sthompsa	}
387168561Sthompsa}
388168561Sthompsa
389168561Sthompsastatic void
390168561Sthompsalacp_tick(void *arg)
391168561Sthompsa{
392168561Sthompsa	struct lacp_softc *lsc = arg;
393168561Sthompsa	struct lacp_port *lp;
394168561Sthompsa
395168561Sthompsa	LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) {
396168561Sthompsa		if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0)
397168561Sthompsa			continue;
398168561Sthompsa
399168561Sthompsa		lacp_run_timers(lp);
400168561Sthompsa
401168561Sthompsa		lacp_select(lp);
402168561Sthompsa		lacp_sm_mux(lp);
403168561Sthompsa		lacp_sm_tx(lp);
404168561Sthompsa		lacp_sm_ptx_tx_schedule(lp);
405168561Sthompsa	}
406168561Sthompsa	callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc);
407168561Sthompsa}
408168561Sthompsa
409168561Sthompsaint
410168793Sthompsalacp_port_create(struct lagg_port *lgp)
411168561Sthompsa{
412168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
413168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
414168561Sthompsa	struct lacp_port *lp;
415168793Sthompsa	struct ifnet *ifp = lgp->lp_ifp;
416168561Sthompsa	struct sockaddr_dl sdl;
417168561Sthompsa	struct ifmultiaddr *rifma = NULL;
418168561Sthompsa	int error;
419168561Sthompsa
420168561Sthompsa	boolean_t active = TRUE; /* XXX should be configurable */
421168561Sthompsa	boolean_t fast = FALSE; /* XXX should be configurable */
422168561Sthompsa
423168793Sthompsa	LAGG_LOCK_ASSERT(lgs);
424168561Sthompsa
425168561Sthompsa	bzero((char *)&sdl, sizeof(sdl));
426168561Sthompsa	sdl.sdl_len = sizeof(sdl);
427168561Sthompsa	sdl.sdl_family = AF_LINK;
428168561Sthompsa	sdl.sdl_index = ifp->if_index;
429168561Sthompsa	sdl.sdl_type = IFT_ETHER;
430168561Sthompsa	sdl.sdl_alen = ETHER_ADDR_LEN;
431168561Sthompsa
432168561Sthompsa	bcopy(&ethermulticastaddr_slowprotocols,
433168561Sthompsa	    LLADDR(&sdl), ETHER_ADDR_LEN);
434168561Sthompsa	error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma);
435168561Sthompsa	if (error) {
436168793Sthompsa		printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname);
437168561Sthompsa		return (error);
438168561Sthompsa	}
439168561Sthompsa
440168561Sthompsa	lp = malloc(sizeof(struct lacp_port),
441168561Sthompsa	    M_DEVBUF, M_NOWAIT|M_ZERO);
442168561Sthompsa	if (lp == NULL)
443168561Sthompsa		return (ENOMEM);
444168561Sthompsa
445168793Sthompsa	lgp->lp_psc = (caddr_t)lp;
446168561Sthompsa	lp->lp_ifp = ifp;
447168793Sthompsa	lp->lp_lagg = lgp;
448168561Sthompsa	lp->lp_lsc = lsc;
449168561Sthompsa
450168561Sthompsa	LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next);
451168561Sthompsa
452168561Sthompsa	lacp_fill_actorinfo(lp, &lp->lp_actor);
453168561Sthompsa	lp->lp_state =
454168561Sthompsa	    (active ? LACP_STATE_ACTIVITY : 0) |
455168561Sthompsa	    (fast ? LACP_STATE_TIMEOUT : 0);
456168561Sthompsa	lp->lp_aggregator = NULL;
457168793Sthompsa	lacp_linkstate(lgp);
458168561Sthompsa	lacp_sm_rx_set_expired(lp);
459168561Sthompsa
460168561Sthompsa	return (0);
461168561Sthompsa}
462168561Sthompsa
463168561Sthompsavoid
464168793Sthompsalacp_port_destroy(struct lagg_port *lgp)
465168561Sthompsa{
466168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
467168793Sthompsa	struct ifnet *ifp = lgp->lp_ifp;
468168561Sthompsa	struct sockaddr_dl sdl;
469168561Sthompsa	int i, error;
470168561Sthompsa
471168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
472168561Sthompsa
473168561Sthompsa	for (i = 0; i < LACP_NTIMER; i++) {
474168561Sthompsa		LACP_TIMER_DISARM(lp, i);
475168561Sthompsa	}
476168561Sthompsa
477168561Sthompsa	lacp_disable_collecting(lp);
478168561Sthompsa	lacp_disable_distributing(lp);
479168561Sthompsa	lacp_unselect(lp);
480168561Sthompsa
481168561Sthompsa	bzero((char *)&sdl, sizeof(sdl));
482168561Sthompsa	sdl.sdl_len = sizeof(sdl);
483168561Sthompsa	sdl.sdl_family = AF_LINK;
484168561Sthompsa	sdl.sdl_index = ifp->if_index;
485168561Sthompsa	sdl.sdl_type = IFT_ETHER;
486168561Sthompsa	sdl.sdl_alen = ETHER_ADDR_LEN;
487168561Sthompsa
488168561Sthompsa	bcopy(&ethermulticastaddr_slowprotocols,
489168561Sthompsa	    LLADDR(&sdl), ETHER_ADDR_LEN);
490168561Sthompsa	error = if_delmulti(ifp, (struct sockaddr *)&sdl);
491168561Sthompsa	if (error)
492168793Sthompsa		printf("%s: DELMULTI failed on %s\n", __func__, lgp->lp_ifname);
493168561Sthompsa
494168561Sthompsa	LIST_REMOVE(lp, lp_next);
495168561Sthompsa	free(lp, M_DEVBUF);
496168561Sthompsa}
497168561Sthompsa
498168561Sthompsaint
499168793Sthompsalacp_port_isactive(struct lagg_port *lgp)
500168561Sthompsa{
501168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
502168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
503168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
504168561Sthompsa
505168561Sthompsa	/* This port is joined to the active aggregator */
506168561Sthompsa	if (la != NULL && la == lsc->lsc_active_aggregator)
507168561Sthompsa		return (1);
508168561Sthompsa
509168561Sthompsa	return (0);
510168561Sthompsa}
511168561Sthompsa
512168561Sthompsastatic void
513168561Sthompsalacp_disable_collecting(struct lacp_port *lp)
514168561Sthompsa{
515168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
516168561Sthompsa
517168561Sthompsa	LACP_DPRINTF((lp, "collecting disabled\n"));
518168561Sthompsa
519168561Sthompsa	lp->lp_state &= ~LACP_STATE_COLLECTING;
520168793Sthompsa	lgp->lp_flags &= ~LAGG_PORT_COLLECTING;
521168561Sthompsa}
522168561Sthompsa
523168561Sthompsastatic void
524168561Sthompsalacp_enable_collecting(struct lacp_port *lp)
525168561Sthompsa{
526168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
527168561Sthompsa
528168561Sthompsa	LACP_DPRINTF((lp, "collecting enabled\n"));
529168561Sthompsa
530168561Sthompsa	lp->lp_state |= LACP_STATE_COLLECTING;
531168793Sthompsa	lgp->lp_flags |= LAGG_PORT_COLLECTING;
532168561Sthompsa}
533168561Sthompsa
534168561Sthompsastatic void
535168561Sthompsalacp_disable_distributing(struct lacp_port *lp)
536168561Sthompsa{
537168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
538168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
539168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
540168561Sthompsa#if defined(LACP_DEBUG)
541168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
542168561Sthompsa#endif /* defined(LACP_DEBUG) */
543168561Sthompsa
544168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
545168561Sthompsa
546168561Sthompsa	if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) {
547168561Sthompsa		return;
548168561Sthompsa	}
549168561Sthompsa
550168561Sthompsa	KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports"));
551168561Sthompsa	KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports));
552168561Sthompsa	KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid"));
553168561Sthompsa
554168561Sthompsa	LACP_DPRINTF((lp, "disable distributing on aggregator %s, "
555168561Sthompsa	    "nports %d -> %d\n",
556168561Sthompsa	    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
557168561Sthompsa	    la->la_nports, la->la_nports - 1));
558168561Sthompsa
559168561Sthompsa	TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q);
560168561Sthompsa	la->la_nports--;
561168561Sthompsa
562168561Sthompsa	lacp_suppress_distributing(lsc, la);
563168561Sthompsa
564168561Sthompsa	lp->lp_state &= ~LACP_STATE_DISTRIBUTING;
565168793Sthompsa	lgp->lp_flags &= ~LAGG_PORT_DISTRIBUTING;
566168561Sthompsa
567168561Sthompsa	if (lsc->lsc_active_aggregator == la) {
568168561Sthompsa		lacp_select_active_aggregator(lsc);
569168561Sthompsa	}
570168561Sthompsa}
571168561Sthompsa
572168561Sthompsastatic void
573168561Sthompsalacp_enable_distributing(struct lacp_port *lp)
574168561Sthompsa{
575168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
576168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
577168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
578168561Sthompsa#if defined(LACP_DEBUG)
579168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
580168561Sthompsa#endif /* defined(LACP_DEBUG) */
581168561Sthompsa
582168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
583168561Sthompsa
584168561Sthompsa	if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) {
585168561Sthompsa		return;
586168561Sthompsa	}
587168561Sthompsa
588168561Sthompsa	LACP_DPRINTF((lp, "enable distributing on aggregator %s, "
589168561Sthompsa	    "nports %d -> %d\n",
590168561Sthompsa	    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
591168561Sthompsa	    la->la_nports, la->la_nports + 1));
592168561Sthompsa
593168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid"));
594168561Sthompsa	TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q);
595168561Sthompsa	la->la_nports++;
596168561Sthompsa
597168561Sthompsa	lacp_suppress_distributing(lsc, la);
598168561Sthompsa
599168561Sthompsa	lp->lp_state |= LACP_STATE_DISTRIBUTING;
600168793Sthompsa	lgp->lp_flags |= LAGG_PORT_DISTRIBUTING;
601168561Sthompsa
602168561Sthompsa	if (lsc->lsc_active_aggregator != la) {
603168561Sthompsa		lacp_select_active_aggregator(lsc);
604168561Sthompsa	}
605168561Sthompsa}
606168561Sthompsa
607168561Sthompsastatic void
608168561Sthompsalacp_transit_expire(void *vp)
609168561Sthompsa{
610168561Sthompsa	struct lacp_softc *lsc = vp;
611168561Sthompsa
612168561Sthompsa	LACP_DPRINTF((NULL, "%s\n", __func__));
613168561Sthompsa	lsc->lsc_suppress_distributing = FALSE;
614168561Sthompsa}
615168561Sthompsa
616168561Sthompsaint
617168793Sthompsalacp_attach(struct lagg_softc *lgs)
618168561Sthompsa{
619168561Sthompsa	struct lacp_softc *lsc;
620168561Sthompsa
621168793Sthompsa	LAGG_LOCK_ASSERT(lgs);
622168561Sthompsa
623168561Sthompsa	lsc = malloc(sizeof(struct lacp_softc),
624168561Sthompsa	    M_DEVBUF, M_NOWAIT|M_ZERO);
625168561Sthompsa	if (lsc == NULL)
626168561Sthompsa		return (ENOMEM);
627168561Sthompsa
628168793Sthompsa	lgs->sc_psc = (caddr_t)lsc;
629168793Sthompsa	lsc->lsc_lagg = lgs;
630168561Sthompsa
631168561Sthompsa	lsc->lsc_hashkey = arc4random();
632168561Sthompsa	lsc->lsc_active_aggregator = NULL;
633168561Sthompsa	TAILQ_INIT(&lsc->lsc_aggregators);
634168561Sthompsa	LIST_INIT(&lsc->lsc_ports);
635168561Sthompsa
636168793Sthompsa	callout_init_mtx(&lsc->lsc_transit_callout, &lgs->sc_mtx, 0);
637168793Sthompsa	callout_init_mtx(&lsc->lsc_callout, &lgs->sc_mtx, 0);
638168561Sthompsa
639168793Sthompsa	/* if the lagg is already up then do the same */
640168793Sthompsa	if (lgs->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
641168793Sthompsa		lacp_init(lgs);
642168561Sthompsa
643168561Sthompsa	return (0);
644168561Sthompsa}
645168561Sthompsa
646168561Sthompsaint
647168793Sthompsalacp_detach(struct lagg_softc *lgs)
648168561Sthompsa{
649168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
650168561Sthompsa
651168561Sthompsa	KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators),
652168561Sthompsa	    ("aggregators still active"));
653168561Sthompsa	KASSERT(lsc->lsc_active_aggregator == NULL,
654168561Sthompsa	    ("aggregator still attached"));
655168561Sthompsa
656168793Sthompsa	lgs->sc_psc = NULL;
657168561Sthompsa	callout_drain(&lsc->lsc_transit_callout);
658168561Sthompsa	callout_drain(&lsc->lsc_callout);
659168561Sthompsa
660168561Sthompsa	free(lsc, M_DEVBUF);
661168561Sthompsa	return (0);
662168561Sthompsa}
663168561Sthompsa
664168561Sthompsavoid
665168793Sthompsalacp_init(struct lagg_softc *lgs)
666168561Sthompsa{
667168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
668168561Sthompsa
669168561Sthompsa	callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc);
670168561Sthompsa}
671168561Sthompsa
672168561Sthompsavoid
673168793Sthompsalacp_stop(struct lagg_softc *lgs)
674168561Sthompsa{
675168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
676168561Sthompsa
677168561Sthompsa	callout_stop(&lsc->lsc_transit_callout);
678168561Sthompsa	callout_stop(&lsc->lsc_callout);
679168561Sthompsa}
680168561Sthompsa
681168793Sthompsastruct lagg_port *
682168793Sthompsalacp_select_tx_port(struct lagg_softc *lgs, struct mbuf *m)
683168561Sthompsa{
684168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
685168561Sthompsa	struct lacp_aggregator *la;
686168561Sthompsa	struct lacp_port *lp;
687168561Sthompsa	uint32_t hash;
688168561Sthompsa	int nports;
689168561Sthompsa
690168793Sthompsa	LAGG_LOCK_ASSERT(lgs);
691168561Sthompsa
692168561Sthompsa	if (__predict_false(lsc->lsc_suppress_distributing)) {
693168561Sthompsa		LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__));
694168561Sthompsa		return (NULL);
695168561Sthompsa	}
696168561Sthompsa
697168561Sthompsa	la = lsc->lsc_active_aggregator;
698168561Sthompsa	if (__predict_false(la == NULL)) {
699168561Sthompsa		LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__));
700168561Sthompsa		return (NULL);
701168561Sthompsa	}
702168561Sthompsa
703168561Sthompsa	nports = la->la_nports;
704168561Sthompsa	KASSERT(nports > 0, ("no ports available"));
705168561Sthompsa
706168793Sthompsa	hash = lagg_hashmbuf(m, lsc->lsc_hashkey);
707168561Sthompsa	hash %= nports;
708168561Sthompsa	lp = TAILQ_FIRST(&la->la_ports);
709168561Sthompsa	while (hash--) {
710168561Sthompsa		lp = TAILQ_NEXT(lp, lp_dist_q);
711168561Sthompsa	}
712168561Sthompsa
713168561Sthompsa	KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0,
714168561Sthompsa	    ("aggregated port is not distributing"));
715168561Sthompsa
716168793Sthompsa	return (lp->lp_lagg);
717168561Sthompsa}
718168561Sthompsa/*
719168561Sthompsa * lacp_suppress_distributing: drop transmit packets for a while
720168561Sthompsa * to preserve packet ordering.
721168561Sthompsa */
722168561Sthompsa
723168561Sthompsastatic void
724168561Sthompsalacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la)
725168561Sthompsa{
726168561Sthompsa	if (lsc->lsc_active_aggregator != la) {
727168561Sthompsa		return;
728168561Sthompsa	}
729168561Sthompsa
730168561Sthompsa	LACP_DPRINTF((NULL, "%s\n", __func__));
731168561Sthompsa	lsc->lsc_suppress_distributing = TRUE;
732168561Sthompsa	/* XXX should consider collector max delay */
733168561Sthompsa	callout_reset(&lsc->lsc_transit_callout,
734168561Sthompsa	    LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc);
735168561Sthompsa}
736168561Sthompsa
737168561Sthompsastatic int
738168561Sthompsalacp_compare_peerinfo(const struct lacp_peerinfo *a,
739168561Sthompsa    const struct lacp_peerinfo *b)
740168561Sthompsa{
741168561Sthompsa	return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state)));
742168561Sthompsa}
743168561Sthompsa
744168561Sthompsastatic int
745168561Sthompsalacp_compare_systemid(const struct lacp_systemid *a,
746168561Sthompsa    const struct lacp_systemid *b)
747168561Sthompsa{
748168561Sthompsa	return (memcmp(a, b, sizeof(*a)));
749168561Sthompsa}
750168561Sthompsa
751168561Sthompsa#if 0	/* unused */
752168561Sthompsastatic int
753168561Sthompsalacp_compare_portid(const struct lacp_portid *a,
754168561Sthompsa    const struct lacp_portid *b)
755168561Sthompsa{
756168561Sthompsa	return (memcmp(a, b, sizeof(*a)));
757168561Sthompsa}
758168561Sthompsa#endif
759168561Sthompsa
760168561Sthompsastatic uint64_t
761168561Sthompsalacp_aggregator_bandwidth(struct lacp_aggregator *la)
762168561Sthompsa{
763168561Sthompsa	struct lacp_port *lp;
764168561Sthompsa	uint64_t speed;
765168561Sthompsa
766168561Sthompsa	lp = TAILQ_FIRST(&la->la_ports);
767168561Sthompsa	if (lp == NULL) {
768168561Sthompsa		return (0);
769168561Sthompsa	}
770168561Sthompsa
771168561Sthompsa	speed = ifmedia_baudrate(lp->lp_media);
772168561Sthompsa	speed *= la->la_nports;
773168561Sthompsa	if (speed == 0) {
774168561Sthompsa		LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n",
775168561Sthompsa		    lp->lp_media, la->la_nports));
776168561Sthompsa	}
777168561Sthompsa
778168561Sthompsa	return (speed);
779168561Sthompsa}
780168561Sthompsa
781168561Sthompsa/*
782168561Sthompsa * lacp_select_active_aggregator: select an aggregator to be used to transmit
783168793Sthompsa * packets from lagg(4) interface.
784168561Sthompsa */
785168561Sthompsa
786168561Sthompsastatic void
787168561Sthompsalacp_select_active_aggregator(struct lacp_softc *lsc)
788168561Sthompsa{
789168561Sthompsa	struct lacp_aggregator *la;
790168561Sthompsa	struct lacp_aggregator *best_la = NULL;
791168561Sthompsa	uint64_t best_speed = 0;
792168561Sthompsa#if defined(LACP_DEBUG)
793168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
794168561Sthompsa#endif /* defined(LACP_DEBUG) */
795168561Sthompsa
796168561Sthompsa	LACP_DPRINTF((NULL, "%s:\n", __func__));
797168561Sthompsa
798168561Sthompsa	TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) {
799168561Sthompsa		uint64_t speed;
800168561Sthompsa
801168561Sthompsa		if (la->la_nports == 0) {
802168561Sthompsa			continue;
803168561Sthompsa		}
804168561Sthompsa
805168561Sthompsa		speed = lacp_aggregator_bandwidth(la);
806168561Sthompsa		LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n",
807168561Sthompsa		    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
808168561Sthompsa		    speed, la->la_nports));
809168561Sthompsa		if (speed > best_speed ||
810168561Sthompsa		    (speed == best_speed &&
811168561Sthompsa		    la == lsc->lsc_active_aggregator)) {
812168561Sthompsa			best_la = la;
813168561Sthompsa			best_speed = speed;
814168561Sthompsa		}
815168561Sthompsa	}
816168561Sthompsa
817168561Sthompsa	KASSERT(best_la == NULL || best_la->la_nports > 0,
818168561Sthompsa	    ("invalid aggregator refcnt"));
819168561Sthompsa	KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports),
820168561Sthompsa	    ("invalid aggregator list"));
821168561Sthompsa
822168561Sthompsa#if defined(LACP_DEBUG)
823168561Sthompsa	if (lsc->lsc_active_aggregator != best_la) {
824168561Sthompsa		LACP_DPRINTF((NULL, "active aggregator changed\n"));
825168561Sthompsa		LACP_DPRINTF((NULL, "old %s\n",
826168561Sthompsa		    lacp_format_lagid_aggregator(lsc->lsc_active_aggregator,
827168561Sthompsa		    buf, sizeof(buf))));
828168561Sthompsa	} else {
829168561Sthompsa		LACP_DPRINTF((NULL, "active aggregator not changed\n"));
830168561Sthompsa	}
831168561Sthompsa	LACP_DPRINTF((NULL, "new %s\n",
832168561Sthompsa	    lacp_format_lagid_aggregator(best_la, buf, sizeof(buf))));
833168561Sthompsa#endif /* defined(LACP_DEBUG) */
834168561Sthompsa
835168561Sthompsa	if (lsc->lsc_active_aggregator != best_la) {
836168561Sthompsa		lsc->lsc_active_aggregator = best_la;
837168561Sthompsa		if (best_la) {
838168561Sthompsa			lacp_suppress_distributing(lsc, best_la);
839168561Sthompsa		}
840168561Sthompsa	}
841168561Sthompsa}
842168561Sthompsa
843168561Sthompsastatic uint16_t
844168561Sthompsalacp_compose_key(struct lacp_port *lp)
845168561Sthompsa{
846168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
847168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
848168561Sthompsa	u_int media = lp->lp_media;
849168561Sthompsa	uint16_t key;
850168561Sthompsa
851168561Sthompsa	KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid interface type"));
852168561Sthompsa
853168561Sthompsa	if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) {
854168561Sthompsa
855168561Sthompsa		/*
856168561Sthompsa		 * non-aggregatable links should have unique keys.
857168561Sthompsa		 *
858168561Sthompsa		 * XXX this isn't really unique as if_index is 16 bit.
859168561Sthompsa		 */
860168561Sthompsa
861168561Sthompsa		/* bit 0..14:	(some bits of) if_index of this port */
862168561Sthompsa		key = lp->lp_ifp->if_index;
863168561Sthompsa		/* bit 15:	1 */
864168561Sthompsa		key |= 0x8000;
865168561Sthompsa	} else {
866168561Sthompsa		u_int subtype = IFM_SUBTYPE(media);
867168561Sthompsa
868168561Sthompsa		KASSERT((media & IFM_HDX) == 0, ("aggregating HDX interface"));
869168561Sthompsa
870168561Sthompsa		/* bit 0..4:	IFM_SUBTYPE */
871168561Sthompsa		key = subtype;
872168793Sthompsa		/* bit 5..14:	(some bits of) if_index of lagg device */
873168793Sthompsa		key |= 0x7fe0 & ((lgs->sc_ifp->if_index) << 5);
874168561Sthompsa		/* bit 15:	0 */
875168561Sthompsa	}
876168561Sthompsa	return (htons(key));
877168561Sthompsa}
878168561Sthompsa
879168561Sthompsastatic void
880168561Sthompsalacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la)
881168561Sthompsa{
882168561Sthompsa#if defined(LACP_DEBUG)
883168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
884168561Sthompsa#endif
885168561Sthompsa
886168561Sthompsa	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
887168561Sthompsa	    __func__,
888168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
889168561Sthompsa	    buf, sizeof(buf)),
890168561Sthompsa	    la->la_refcnt, la->la_refcnt + 1));
891168561Sthompsa
892168561Sthompsa	KASSERT(la->la_refcnt > 0, ("refcount <= 0"));
893168561Sthompsa	la->la_refcnt++;
894168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount"));
895168561Sthompsa}
896168561Sthompsa
897168561Sthompsastatic void
898168561Sthompsalacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la)
899168561Sthompsa{
900168561Sthompsa#if defined(LACP_DEBUG)
901168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
902168561Sthompsa#endif
903168561Sthompsa
904168561Sthompsa	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
905168561Sthompsa	    __func__,
906168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
907168561Sthompsa	    buf, sizeof(buf)),
908168561Sthompsa	    la->la_refcnt, la->la_refcnt - 1));
909168561Sthompsa
910168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt"));
911168561Sthompsa	la->la_refcnt--;
912168561Sthompsa	if (la->la_refcnt > 0) {
913168561Sthompsa		return;
914168561Sthompsa	}
915168561Sthompsa
916168561Sthompsa	KASSERT(la->la_refcnt == 0, ("refcount not zero"));
917168561Sthompsa	KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active"));
918168561Sthompsa
919168561Sthompsa	TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q);
920168561Sthompsa
921168561Sthompsa	free(la, M_DEVBUF);
922168561Sthompsa}
923168561Sthompsa
924168561Sthompsa/*
925168561Sthompsa * lacp_aggregator_get: allocate an aggregator.
926168561Sthompsa */
927168561Sthompsa
928168561Sthompsastatic struct lacp_aggregator *
929168561Sthompsalacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp)
930168561Sthompsa{
931168561Sthompsa	struct lacp_aggregator *la;
932168561Sthompsa
933168561Sthompsa	la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT);
934168561Sthompsa	if (la) {
935168561Sthompsa		la->la_refcnt = 1;
936168561Sthompsa		la->la_nports = 0;
937168561Sthompsa		TAILQ_INIT(&la->la_ports);
938168561Sthompsa		la->la_pending = 0;
939168561Sthompsa		TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q);
940168561Sthompsa	}
941168561Sthompsa
942168561Sthompsa	return (la);
943168561Sthompsa}
944168561Sthompsa
945168561Sthompsa/*
946168561Sthompsa * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port.
947168561Sthompsa */
948168561Sthompsa
949168561Sthompsastatic void
950168561Sthompsalacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp)
951168561Sthompsa{
952168561Sthompsa	lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner);
953168561Sthompsa	lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor);
954168561Sthompsa
955168561Sthompsa	la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION;
956168561Sthompsa}
957168561Sthompsa
958168561Sthompsastatic void
959168561Sthompsalacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr,
960168561Sthompsa    const struct lacp_peerinfo *lpi_port)
961168561Sthompsa{
962168561Sthompsa	memset(lpi_aggr, 0, sizeof(*lpi_aggr));
963168561Sthompsa	lpi_aggr->lip_systemid = lpi_port->lip_systemid;
964168561Sthompsa	lpi_aggr->lip_key = lpi_port->lip_key;
965168561Sthompsa}
966168561Sthompsa
967168561Sthompsa/*
968168561Sthompsa * lacp_aggregator_is_compatible: check if a port can join to an aggregator.
969168561Sthompsa */
970168561Sthompsa
971168561Sthompsastatic int
972168561Sthompsalacp_aggregator_is_compatible(const struct lacp_aggregator *la,
973168561Sthompsa    const struct lacp_port *lp)
974168561Sthompsa{
975168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION) ||
976168561Sthompsa	    !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) {
977168561Sthompsa		return (0);
978168561Sthompsa	}
979168561Sthompsa
980168561Sthompsa	if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) {
981168561Sthompsa		return (0);
982168561Sthompsa	}
983168561Sthompsa
984168561Sthompsa	if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) {
985168561Sthompsa		return (0);
986168561Sthompsa	}
987168561Sthompsa
988168561Sthompsa	if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) {
989168561Sthompsa		return (0);
990168561Sthompsa	}
991168561Sthompsa
992168561Sthompsa	return (1);
993168561Sthompsa}
994168561Sthompsa
995168561Sthompsastatic int
996168561Sthompsalacp_peerinfo_is_compatible(const struct lacp_peerinfo *a,
997168561Sthompsa    const struct lacp_peerinfo *b)
998168561Sthompsa{
999168561Sthompsa	if (memcmp(&a->lip_systemid, &b->lip_systemid,
1000168561Sthompsa	    sizeof(a->lip_systemid))) {
1001168561Sthompsa		return (0);
1002168561Sthompsa	}
1003168561Sthompsa
1004168561Sthompsa	if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) {
1005168561Sthompsa		return (0);
1006168561Sthompsa	}
1007168561Sthompsa
1008168561Sthompsa	return (1);
1009168561Sthompsa}
1010168561Sthompsa
1011168561Sthompsastatic void
1012168561Sthompsalacp_port_enable(struct lacp_port *lp)
1013168561Sthompsa{
1014168561Sthompsa	lp->lp_state |= LACP_STATE_AGGREGATION;
1015168561Sthompsa}
1016168561Sthompsa
1017168561Sthompsastatic void
1018168561Sthompsalacp_port_disable(struct lacp_port *lp)
1019168561Sthompsa{
1020168561Sthompsa	lacp_set_mux(lp, LACP_MUX_DETACHED);
1021168561Sthompsa
1022168561Sthompsa	lp->lp_state &= ~LACP_STATE_AGGREGATION;
1023168561Sthompsa	lp->lp_selected = LACP_UNSELECTED;
1024168561Sthompsa	lacp_sm_rx_record_default(lp);
1025168561Sthompsa	lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION;
1026168561Sthompsa	lp->lp_state &= ~LACP_STATE_EXPIRED;
1027168561Sthompsa}
1028168561Sthompsa
1029168561Sthompsa/*
1030168561Sthompsa * lacp_select: select an aggregator.  create one if necessary.
1031168561Sthompsa */
1032168561Sthompsastatic void
1033168561Sthompsalacp_select(struct lacp_port *lp)
1034168561Sthompsa{
1035168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
1036168561Sthompsa	struct lacp_aggregator *la;
1037168561Sthompsa#if defined(LACP_DEBUG)
1038168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
1039168561Sthompsa#endif
1040168561Sthompsa
1041168561Sthompsa	if (lp->lp_aggregator) {
1042168561Sthompsa		return;
1043168561Sthompsa	}
1044168561Sthompsa
1045168561Sthompsa	KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1046168561Sthompsa	    ("timer_wait_while still active"));
1047168561Sthompsa
1048168561Sthompsa	LACP_DPRINTF((lp, "port lagid=%s\n",
1049168561Sthompsa	    lacp_format_lagid(&lp->lp_actor, &lp->lp_partner,
1050168561Sthompsa	    buf, sizeof(buf))));
1051168561Sthompsa
1052168561Sthompsa	TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) {
1053168561Sthompsa		if (lacp_aggregator_is_compatible(la, lp)) {
1054168561Sthompsa			break;
1055168561Sthompsa		}
1056168561Sthompsa	}
1057168561Sthompsa
1058168561Sthompsa	if (la == NULL) {
1059168561Sthompsa		la = lacp_aggregator_get(lsc, lp);
1060168561Sthompsa		if (la == NULL) {
1061168561Sthompsa			LACP_DPRINTF((lp, "aggregator creation failed\n"));
1062168561Sthompsa
1063168561Sthompsa			/*
1064168561Sthompsa			 * will retry on the next tick.
1065168561Sthompsa			 */
1066168561Sthompsa
1067168561Sthompsa			return;
1068168561Sthompsa		}
1069168561Sthompsa		lacp_fill_aggregator_id(la, lp);
1070168561Sthompsa		LACP_DPRINTF((lp, "aggregator created\n"));
1071168561Sthompsa	} else {
1072168561Sthompsa		LACP_DPRINTF((lp, "compatible aggregator found\n"));
1073168561Sthompsa		lacp_aggregator_addref(lsc, la);
1074168561Sthompsa	}
1075168561Sthompsa
1076168561Sthompsa	LACP_DPRINTF((lp, "aggregator lagid=%s\n",
1077168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
1078168561Sthompsa	    buf, sizeof(buf))));
1079168561Sthompsa
1080168561Sthompsa	lp->lp_aggregator = la;
1081168561Sthompsa	lp->lp_selected = LACP_SELECTED;
1082168561Sthompsa}
1083168561Sthompsa
1084168561Sthompsa/*
1085168561Sthompsa * lacp_unselect: finish unselect/detach process.
1086168561Sthompsa */
1087168561Sthompsa
1088168561Sthompsastatic void
1089168561Sthompsalacp_unselect(struct lacp_port *lp)
1090168561Sthompsa{
1091168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
1092168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1093168561Sthompsa
1094168561Sthompsa	KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1095168561Sthompsa	    ("timer_wait_while still active"));
1096168561Sthompsa
1097168561Sthompsa	if (la == NULL) {
1098168561Sthompsa		return;
1099168561Sthompsa	}
1100168561Sthompsa
1101168561Sthompsa	lp->lp_aggregator = NULL;
1102168561Sthompsa	lacp_aggregator_delref(lsc, la);
1103168561Sthompsa}
1104168561Sthompsa
1105168561Sthompsa/* mux machine */
1106168561Sthompsa
1107168561Sthompsastatic void
1108168561Sthompsalacp_sm_mux(struct lacp_port *lp)
1109168561Sthompsa{
1110168561Sthompsa	enum lacp_mux_state new_state;
1111168561Sthompsa	boolean_t p_sync =
1112168561Sthompsa		    (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0;
1113168561Sthompsa	boolean_t p_collecting =
1114168561Sthompsa	    (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0;
1115168561Sthompsa	enum lacp_selected selected = lp->lp_selected;
1116168561Sthompsa	struct lacp_aggregator *la;
1117168561Sthompsa
1118168561Sthompsa	/* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */
1119168561Sthompsa
1120168561Sthompsare_eval:
1121168561Sthompsa	la = lp->lp_aggregator;
1122168561Sthompsa	KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL,
1123168561Sthompsa	    ("MUX not detached"));
1124168561Sthompsa	new_state = lp->lp_mux_state;
1125168561Sthompsa	switch (lp->lp_mux_state) {
1126168561Sthompsa	case LACP_MUX_DETACHED:
1127168561Sthompsa		if (selected != LACP_UNSELECTED) {
1128168561Sthompsa			new_state = LACP_MUX_WAITING;
1129168561Sthompsa		}
1130168561Sthompsa		break;
1131168561Sthompsa	case LACP_MUX_WAITING:
1132168561Sthompsa		KASSERT(la->la_pending > 0 ||
1133168561Sthompsa		    !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1134168561Sthompsa		    ("timer_wait_while still active"));
1135168561Sthompsa		if (selected == LACP_SELECTED && la->la_pending == 0) {
1136168561Sthompsa			new_state = LACP_MUX_ATTACHED;
1137168561Sthompsa		} else if (selected == LACP_UNSELECTED) {
1138168561Sthompsa			new_state = LACP_MUX_DETACHED;
1139168561Sthompsa		}
1140168561Sthompsa		break;
1141168561Sthompsa	case LACP_MUX_ATTACHED:
1142168561Sthompsa		if (selected == LACP_SELECTED && p_sync) {
1143168561Sthompsa			new_state = LACP_MUX_COLLECTING;
1144168561Sthompsa		} else if (selected != LACP_SELECTED) {
1145168561Sthompsa			new_state = LACP_MUX_DETACHED;
1146168561Sthompsa		}
1147168561Sthompsa		break;
1148168561Sthompsa	case LACP_MUX_COLLECTING:
1149168561Sthompsa		if (selected == LACP_SELECTED && p_sync && p_collecting) {
1150168561Sthompsa			new_state = LACP_MUX_DISTRIBUTING;
1151168561Sthompsa		} else if (selected != LACP_SELECTED || !p_sync) {
1152168561Sthompsa			new_state = LACP_MUX_ATTACHED;
1153168561Sthompsa		}
1154168561Sthompsa		break;
1155168561Sthompsa	case LACP_MUX_DISTRIBUTING:
1156168561Sthompsa		if (selected != LACP_SELECTED || !p_sync || !p_collecting) {
1157168561Sthompsa			new_state = LACP_MUX_COLLECTING;
1158168561Sthompsa		}
1159168561Sthompsa		break;
1160168561Sthompsa	default:
1161168561Sthompsa		panic("%s: unknown state", __func__);
1162168561Sthompsa	}
1163168561Sthompsa
1164168561Sthompsa	if (lp->lp_mux_state == new_state) {
1165168561Sthompsa		return;
1166168561Sthompsa	}
1167168561Sthompsa
1168168561Sthompsa	lacp_set_mux(lp, new_state);
1169168561Sthompsa	goto re_eval;
1170168561Sthompsa}
1171168561Sthompsa
1172168561Sthompsastatic void
1173168561Sthompsalacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state)
1174168561Sthompsa{
1175168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1176168561Sthompsa
1177168561Sthompsa	if (lp->lp_mux_state == new_state) {
1178168561Sthompsa		return;
1179168561Sthompsa	}
1180168561Sthompsa
1181168561Sthompsa	switch (new_state) {
1182168561Sthompsa	case LACP_MUX_DETACHED:
1183168561Sthompsa		lp->lp_state &= ~LACP_STATE_SYNC;
1184168561Sthompsa		lacp_disable_distributing(lp);
1185168561Sthompsa		lacp_disable_collecting(lp);
1186168561Sthompsa		lacp_sm_assert_ntt(lp);
1187168561Sthompsa		/* cancel timer */
1188168561Sthompsa		if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) {
1189168561Sthompsa			KASSERT(la->la_pending > 0,
1190168561Sthompsa			    ("timer_wait_while not active"));
1191168561Sthompsa			la->la_pending--;
1192168561Sthompsa		}
1193168561Sthompsa		LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE);
1194168561Sthompsa		lacp_unselect(lp);
1195168561Sthompsa		break;
1196168561Sthompsa	case LACP_MUX_WAITING:
1197168561Sthompsa		LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE,
1198168561Sthompsa		    LACP_AGGREGATE_WAIT_TIME);
1199168561Sthompsa		la->la_pending++;
1200168561Sthompsa		break;
1201168561Sthompsa	case LACP_MUX_ATTACHED:
1202168561Sthompsa		lp->lp_state |= LACP_STATE_SYNC;
1203168561Sthompsa		lacp_disable_collecting(lp);
1204168561Sthompsa		lacp_sm_assert_ntt(lp);
1205168561Sthompsa		break;
1206168561Sthompsa	case LACP_MUX_COLLECTING:
1207168561Sthompsa		lacp_enable_collecting(lp);
1208168561Sthompsa		lacp_disable_distributing(lp);
1209168561Sthompsa		lacp_sm_assert_ntt(lp);
1210168561Sthompsa		break;
1211168561Sthompsa	case LACP_MUX_DISTRIBUTING:
1212168561Sthompsa		lacp_enable_distributing(lp);
1213168561Sthompsa		break;
1214168561Sthompsa	default:
1215168561Sthompsa		panic("%s: unknown state", __func__);
1216168561Sthompsa	}
1217168561Sthompsa
1218168561Sthompsa	LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state));
1219168561Sthompsa
1220168561Sthompsa	lp->lp_mux_state = new_state;
1221168561Sthompsa}
1222168561Sthompsa
1223168561Sthompsastatic void
1224168561Sthompsalacp_sm_mux_timer(struct lacp_port *lp)
1225168561Sthompsa{
1226168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1227168561Sthompsa#if defined(LACP_DEBUG)
1228168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
1229168561Sthompsa#endif
1230168561Sthompsa
1231168561Sthompsa	KASSERT(la->la_pending > 0, ("no pending event"));
1232168561Sthompsa
1233168561Sthompsa	LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__,
1234168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
1235168561Sthompsa	    buf, sizeof(buf)),
1236168561Sthompsa	    la->la_pending, la->la_pending - 1));
1237168561Sthompsa
1238168561Sthompsa	la->la_pending--;
1239168561Sthompsa}
1240168561Sthompsa
1241168561Sthompsa/* periodic transmit machine */
1242168561Sthompsa
1243168561Sthompsastatic void
1244168561Sthompsalacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate)
1245168561Sthompsa{
1246168561Sthompsa	if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state,
1247168561Sthompsa	    LACP_STATE_TIMEOUT)) {
1248168561Sthompsa		return;
1249168561Sthompsa	}
1250168561Sthompsa
1251168561Sthompsa	LACP_DPRINTF((lp, "partner timeout changed\n"));
1252168561Sthompsa
1253168561Sthompsa	/*
1254168561Sthompsa	 * FAST_PERIODIC -> SLOW_PERIODIC
1255168561Sthompsa	 * or
1256168561Sthompsa	 * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC
1257168561Sthompsa	 *
1258168561Sthompsa	 * let lacp_sm_ptx_tx_schedule to update timeout.
1259168561Sthompsa	 */
1260168561Sthompsa
1261168561Sthompsa	LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC);
1262168561Sthompsa
1263168561Sthompsa	/*
1264168561Sthompsa	 * if timeout has been shortened, assert NTT.
1265168561Sthompsa	 */
1266168561Sthompsa
1267168561Sthompsa	if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) {
1268168561Sthompsa		lacp_sm_assert_ntt(lp);
1269168561Sthompsa	}
1270168561Sthompsa}
1271168561Sthompsa
1272168561Sthompsastatic void
1273168561Sthompsalacp_sm_ptx_tx_schedule(struct lacp_port *lp)
1274168561Sthompsa{
1275168561Sthompsa	int timeout;
1276168561Sthompsa
1277168561Sthompsa	if (!(lp->lp_state & LACP_STATE_ACTIVITY) &&
1278168561Sthompsa	    !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) {
1279168561Sthompsa
1280168561Sthompsa		/*
1281168561Sthompsa		 * NO_PERIODIC
1282168561Sthompsa		 */
1283168561Sthompsa
1284168561Sthompsa		LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC);
1285168561Sthompsa		return;
1286168561Sthompsa	}
1287168561Sthompsa
1288168561Sthompsa	if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) {
1289168561Sthompsa		return;
1290168561Sthompsa	}
1291168561Sthompsa
1292168561Sthompsa	timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ?
1293168561Sthompsa	    LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME;
1294168561Sthompsa
1295168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout);
1296168561Sthompsa}
1297168561Sthompsa
1298168561Sthompsastatic void
1299168561Sthompsalacp_sm_ptx_timer(struct lacp_port *lp)
1300168561Sthompsa{
1301168561Sthompsa	lacp_sm_assert_ntt(lp);
1302168561Sthompsa}
1303168561Sthompsa
1304168561Sthompsastatic void
1305168561Sthompsalacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du)
1306168561Sthompsa{
1307168561Sthompsa	int timeout;
1308168561Sthompsa
1309168561Sthompsa	/*
1310168561Sthompsa	 * check LACP_DISABLED first
1311168561Sthompsa	 */
1312168561Sthompsa
1313168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION)) {
1314168561Sthompsa		return;
1315168561Sthompsa	}
1316168561Sthompsa
1317168561Sthompsa	/*
1318168561Sthompsa	 * check loopback condition.
1319168561Sthompsa	 */
1320168561Sthompsa
1321168561Sthompsa	if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid,
1322168561Sthompsa	    &lp->lp_actor.lip_systemid)) {
1323168561Sthompsa		return;
1324168561Sthompsa	}
1325168561Sthompsa
1326168561Sthompsa	/*
1327168561Sthompsa	 * EXPIRED, DEFAULTED, CURRENT -> CURRENT
1328168561Sthompsa	 */
1329168561Sthompsa
1330168561Sthompsa	lacp_sm_rx_update_selected(lp, du);
1331168561Sthompsa	lacp_sm_rx_update_ntt(lp, du);
1332168561Sthompsa	lacp_sm_rx_record_pdu(lp, du);
1333168561Sthompsa
1334168561Sthompsa	timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ?
1335168561Sthompsa	    LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME;
1336168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout);
1337168561Sthompsa
1338168561Sthompsa	lp->lp_state &= ~LACP_STATE_EXPIRED;
1339168561Sthompsa
1340168561Sthompsa	/*
1341168561Sthompsa	 * kick transmit machine without waiting the next tick.
1342168561Sthompsa	 */
1343168561Sthompsa
1344168561Sthompsa	lacp_sm_tx(lp);
1345168561Sthompsa}
1346168561Sthompsa
1347168561Sthompsastatic void
1348168561Sthompsalacp_sm_rx_set_expired(struct lacp_port *lp)
1349168561Sthompsa{
1350168561Sthompsa	lp->lp_partner.lip_state &= ~LACP_STATE_SYNC;
1351168561Sthompsa	lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT;
1352168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME);
1353168561Sthompsa	lp->lp_state |= LACP_STATE_EXPIRED;
1354168561Sthompsa}
1355168561Sthompsa
1356168561Sthompsastatic void
1357168561Sthompsalacp_sm_rx_timer(struct lacp_port *lp)
1358168561Sthompsa{
1359168561Sthompsa	if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) {
1360168561Sthompsa		/* CURRENT -> EXPIRED */
1361168561Sthompsa		LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__));
1362168561Sthompsa		lacp_sm_rx_set_expired(lp);
1363168561Sthompsa	} else {
1364168561Sthompsa		/* EXPIRED -> DEFAULTED */
1365168561Sthompsa		LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__));
1366168561Sthompsa		lacp_sm_rx_update_default_selected(lp);
1367168561Sthompsa		lacp_sm_rx_record_default(lp);
1368168561Sthompsa		lp->lp_state &= ~LACP_STATE_EXPIRED;
1369168561Sthompsa	}
1370168561Sthompsa}
1371168561Sthompsa
1372168561Sthompsastatic void
1373168561Sthompsalacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du)
1374168561Sthompsa{
1375168561Sthompsa	boolean_t active;
1376168561Sthompsa	uint8_t oldpstate;
1377168561Sthompsa#if defined(LACP_DEBUG)
1378168561Sthompsa	char buf[LACP_STATESTR_MAX+1];
1379168561Sthompsa#endif
1380168561Sthompsa
1381168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1382168561Sthompsa
1383168561Sthompsa	oldpstate = lp->lp_partner.lip_state;
1384168561Sthompsa
1385168561Sthompsa	active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY)
1386168561Sthompsa	    || ((lp->lp_state & LACP_STATE_ACTIVITY) &&
1387168561Sthompsa	    (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY));
1388168561Sthompsa
1389168561Sthompsa	lp->lp_partner = du->ldu_actor;
1390168561Sthompsa	if (active &&
1391168561Sthompsa	    ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state,
1392168561Sthompsa	    LACP_STATE_AGGREGATION) &&
1393168561Sthompsa	    !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner))
1394168561Sthompsa	    || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) {
1395168561Sthompsa		/* XXX nothing? */
1396168561Sthompsa	} else {
1397168561Sthompsa		lp->lp_partner.lip_state &= ~LACP_STATE_SYNC;
1398168561Sthompsa	}
1399168561Sthompsa
1400168561Sthompsa	lp->lp_state &= ~LACP_STATE_DEFAULTED;
1401168561Sthompsa
1402168561Sthompsa	if (oldpstate != lp->lp_partner.lip_state) {
1403168561Sthompsa		LACP_DPRINTF((lp, "old pstate %s\n",
1404168561Sthompsa		    lacp_format_state(oldpstate, buf, sizeof(buf))));
1405168561Sthompsa		LACP_DPRINTF((lp, "new pstate %s\n",
1406168561Sthompsa		    lacp_format_state(lp->lp_partner.lip_state, buf,
1407168561Sthompsa		    sizeof(buf))));
1408168561Sthompsa	}
1409168561Sthompsa
1410168561Sthompsa	lacp_sm_ptx_update_timeout(lp, oldpstate);
1411168561Sthompsa}
1412168561Sthompsa
1413168561Sthompsastatic void
1414168561Sthompsalacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du)
1415168561Sthompsa{
1416168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1417168561Sthompsa
1418168561Sthompsa	if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) ||
1419168561Sthompsa	    !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state,
1420168561Sthompsa	    LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) {
1421168561Sthompsa		LACP_DPRINTF((lp, "%s: assert ntt\n", __func__));
1422168561Sthompsa		lacp_sm_assert_ntt(lp);
1423168561Sthompsa	}
1424168561Sthompsa}
1425168561Sthompsa
1426168561Sthompsastatic void
1427168561Sthompsalacp_sm_rx_record_default(struct lacp_port *lp)
1428168561Sthompsa{
1429168561Sthompsa	uint8_t oldpstate;
1430168561Sthompsa
1431168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1432168561Sthompsa
1433168561Sthompsa	oldpstate = lp->lp_partner.lip_state;
1434168561Sthompsa	lp->lp_partner = lacp_partner_admin;
1435168561Sthompsa	lp->lp_state |= LACP_STATE_DEFAULTED;
1436168561Sthompsa	lacp_sm_ptx_update_timeout(lp, oldpstate);
1437168561Sthompsa}
1438168561Sthompsa
1439168561Sthompsastatic void
1440168561Sthompsalacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp,
1441168561Sthompsa    const struct lacp_peerinfo *info)
1442168561Sthompsa{
1443168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1444168561Sthompsa
1445168561Sthompsa	if (lacp_compare_peerinfo(&lp->lp_partner, info) ||
1446168561Sthompsa	    !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state,
1447168561Sthompsa	    LACP_STATE_AGGREGATION)) {
1448168561Sthompsa		lp->lp_selected = LACP_UNSELECTED;
1449168561Sthompsa		/* mux machine will clean up lp->lp_aggregator */
1450168561Sthompsa	}
1451168561Sthompsa}
1452168561Sthompsa
1453168561Sthompsastatic void
1454168561Sthompsalacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du)
1455168561Sthompsa{
1456168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1457168561Sthompsa
1458168561Sthompsa	lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor);
1459168561Sthompsa}
1460168561Sthompsa
1461168561Sthompsastatic void
1462168561Sthompsalacp_sm_rx_update_default_selected(struct lacp_port *lp)
1463168561Sthompsa{
1464168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1465168561Sthompsa
1466168561Sthompsa	lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin);
1467168561Sthompsa}
1468168561Sthompsa
1469168561Sthompsa/* transmit machine */
1470168561Sthompsa
1471168561Sthompsastatic void
1472168561Sthompsalacp_sm_tx(struct lacp_port *lp)
1473168561Sthompsa{
1474168561Sthompsa	int error;
1475168561Sthompsa
1476168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION)
1477168561Sthompsa#if 1
1478168561Sthompsa	    || (!(lp->lp_state & LACP_STATE_ACTIVITY)
1479168561Sthompsa	    && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY))
1480168561Sthompsa#endif
1481168561Sthompsa	    ) {
1482168561Sthompsa		lp->lp_flags &= ~LACP_PORT_NTT;
1483168561Sthompsa	}
1484168561Sthompsa
1485168561Sthompsa	if (!(lp->lp_flags & LACP_PORT_NTT)) {
1486168561Sthompsa		return;
1487168561Sthompsa	}
1488168561Sthompsa
1489168561Sthompsa	/* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */
1490168561Sthompsa	if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent,
1491168561Sthompsa		    (3 / LACP_FAST_PERIODIC_TIME)) == 0) {
1492168561Sthompsa		LACP_DPRINTF((lp, "rate limited pdu\n"));
1493168561Sthompsa		return;
1494168561Sthompsa	}
1495168561Sthompsa
1496168561Sthompsa	error = lacp_xmit_lacpdu(lp);
1497168561Sthompsa
1498168561Sthompsa	if (error == 0) {
1499168561Sthompsa		lp->lp_flags &= ~LACP_PORT_NTT;
1500168561Sthompsa	} else {
1501168561Sthompsa		LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n",
1502168561Sthompsa		    error));
1503168561Sthompsa	}
1504168561Sthompsa}
1505168561Sthompsa
1506168561Sthompsastatic void
1507168561Sthompsalacp_sm_assert_ntt(struct lacp_port *lp)
1508168561Sthompsa{
1509168561Sthompsa
1510168561Sthompsa	lp->lp_flags |= LACP_PORT_NTT;
1511168561Sthompsa}
1512168561Sthompsa
1513168561Sthompsastatic void
1514168561Sthompsalacp_run_timers(struct lacp_port *lp)
1515168561Sthompsa{
1516168561Sthompsa	int i;
1517168561Sthompsa
1518168561Sthompsa	for (i = 0; i < LACP_NTIMER; i++) {
1519168561Sthompsa		KASSERT(lp->lp_timer[i] >= 0,
1520168561Sthompsa		    ("invalid timer value %d", lp->lp_timer[i]));
1521168561Sthompsa		if (lp->lp_timer[i] == 0) {
1522168561Sthompsa			continue;
1523168561Sthompsa		} else if (--lp->lp_timer[i] <= 0) {
1524168561Sthompsa			if (lacp_timer_funcs[i]) {
1525168561Sthompsa				(*lacp_timer_funcs[i])(lp);
1526168561Sthompsa			}
1527168561Sthompsa		}
1528168561Sthompsa	}
1529168561Sthompsa}
1530168561Sthompsa
1531168561Sthompsaint
1532168793Sthompsalacp_marker_input(struct lagg_port *lgp, struct mbuf *m)
1533168561Sthompsa{
1534168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
1535168561Sthompsa	struct markerdu *mdu;
1536168561Sthompsa	int error = 0;
1537168561Sthompsa
1538168793Sthompsa	LAGG_LOCK_ASSERT(lgp->lp_lagg);
1539168561Sthompsa
1540168561Sthompsa	if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) {
1541168561Sthompsa		goto bad;
1542168561Sthompsa	}
1543168561Sthompsa
1544168561Sthompsa	if (m->m_pkthdr.len != sizeof(*mdu)) {
1545168561Sthompsa		goto bad;
1546168561Sthompsa	}
1547168561Sthompsa
1548168561Sthompsa	if ((m->m_flags & M_MCAST) == 0) {
1549168561Sthompsa		goto bad;
1550168561Sthompsa	}
1551168561Sthompsa
1552168561Sthompsa	if (m->m_len < sizeof(*mdu)) {
1553168561Sthompsa		m = m_pullup(m, sizeof(*mdu));
1554168561Sthompsa		if (m == NULL) {
1555168561Sthompsa			return (ENOMEM);
1556168561Sthompsa		}
1557168561Sthompsa	}
1558168561Sthompsa
1559168561Sthompsa	mdu = mtod(m, struct markerdu *);
1560168561Sthompsa
1561168561Sthompsa	if (memcmp(&mdu->mdu_eh.ether_dhost,
1562168561Sthompsa	    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) {
1563168561Sthompsa		goto bad;
1564168561Sthompsa	}
1565168561Sthompsa
1566168561Sthompsa	/* XXX
1567168561Sthompsa	KASSERT(mdu->mdu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_MARKER,
1568168561Sthompsa	    ("a very bad kassert!"));
1569168561Sthompsa	*/
1570168561Sthompsa
1571168561Sthompsa	if (mdu->mdu_sph.sph_version != 1) {
1572168561Sthompsa		goto bad;
1573168561Sthompsa	}
1574168561Sthompsa
1575168561Sthompsa	switch (mdu->mdu_tlv.tlv_type) {
1576168561Sthompsa	case MARKER_TYPE_INFO:
1577168561Sthompsa		if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv,
1578168561Sthompsa		    marker_info_tlv_template, TRUE)) {
1579168561Sthompsa			goto bad;
1580168561Sthompsa		}
1581168561Sthompsa		mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE;
1582168561Sthompsa		memcpy(&mdu->mdu_eh.ether_dhost,
1583168561Sthompsa		    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN);
1584168561Sthompsa		memcpy(&mdu->mdu_eh.ether_shost,
1585168793Sthompsa		    lgp->lp_lladdr, ETHER_ADDR_LEN);
1586168793Sthompsa		error = lagg_enqueue(lp->lp_ifp, m);
1587168561Sthompsa		break;
1588168561Sthompsa
1589168561Sthompsa	case MARKER_TYPE_RESPONSE:
1590168561Sthompsa		if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv,
1591168561Sthompsa		    marker_response_tlv_template, TRUE)) {
1592168561Sthompsa			goto bad;
1593168561Sthompsa		}
1594168561Sthompsa		/*
1595168561Sthompsa		 * we are not interested in responses as
1596168561Sthompsa		 * we don't have a marker sender.
1597168561Sthompsa		 */
1598168561Sthompsa		/* FALLTHROUGH */
1599168561Sthompsa	default:
1600168561Sthompsa		goto bad;
1601168561Sthompsa	}
1602168561Sthompsa
1603168561Sthompsa	return (error);
1604168561Sthompsa
1605168561Sthompsabad:
1606168561Sthompsa	m_freem(m);
1607168561Sthompsa	return (EINVAL);
1608168561Sthompsa}
1609168561Sthompsa
1610168561Sthompsastatic int
1611168561Sthompsatlv_check(const void *p, size_t size, const struct tlvhdr *tlv,
1612168561Sthompsa    const struct tlv_template *tmpl, boolean_t check_type)
1613168561Sthompsa{
1614168561Sthompsa	while (/* CONSTCOND */ 1) {
1615168561Sthompsa		if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) {
1616168561Sthompsa			return (EINVAL);
1617168561Sthompsa		}
1618168561Sthompsa		if ((check_type && tlv->tlv_type != tmpl->tmpl_type) ||
1619168561Sthompsa		    tlv->tlv_length != tmpl->tmpl_length) {
1620168561Sthompsa			return (EINVAL);
1621168561Sthompsa		}
1622168561Sthompsa		if (tmpl->tmpl_type == 0) {
1623168561Sthompsa			break;
1624168561Sthompsa		}
1625168561Sthompsa		tlv = (const struct tlvhdr *)
1626168561Sthompsa		    ((const char *)tlv + tlv->tlv_length);
1627168561Sthompsa		tmpl++;
1628168561Sthompsa	}
1629168561Sthompsa
1630168561Sthompsa	return (0);
1631168561Sthompsa}
1632168561Sthompsa
1633168561Sthompsa#if defined(LACP_DEBUG)
1634168561Sthompsaconst char *
1635168561Sthompsalacp_format_mac(const uint8_t *mac, char *buf, size_t buflen)
1636168561Sthompsa{
1637168561Sthompsa	snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X",
1638168561Sthompsa	    (int)mac[0],
1639168561Sthompsa	    (int)mac[1],
1640168561Sthompsa	    (int)mac[2],
1641168561Sthompsa	    (int)mac[3],
1642168561Sthompsa	    (int)mac[4],
1643168561Sthompsa	    (int)mac[5]);
1644168561Sthompsa
1645168561Sthompsa	return (buf);
1646168561Sthompsa}
1647168561Sthompsa
1648168561Sthompsaconst char *
1649168561Sthompsalacp_format_systemid(const struct lacp_systemid *sysid,
1650168561Sthompsa    char *buf, size_t buflen)
1651168561Sthompsa{
1652168561Sthompsa	char macbuf[LACP_MACSTR_MAX+1];
1653168561Sthompsa
1654168561Sthompsa	snprintf(buf, buflen, "%04X,%s",
1655168561Sthompsa	    ntohs(sysid->lsi_prio),
1656168561Sthompsa	    lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf)));
1657168561Sthompsa
1658168561Sthompsa	return (buf);
1659168561Sthompsa}
1660168561Sthompsa
1661168561Sthompsaconst char *
1662168561Sthompsalacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen)
1663168561Sthompsa{
1664168561Sthompsa	snprintf(buf, buflen, "%04X,%04X",
1665168561Sthompsa	    ntohs(portid->lpi_prio),
1666168561Sthompsa	    ntohs(portid->lpi_portno));
1667168561Sthompsa
1668168561Sthompsa	return (buf);
1669168561Sthompsa}
1670168561Sthompsa
1671168561Sthompsaconst char *
1672168561Sthompsalacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen)
1673168561Sthompsa{
1674168561Sthompsa	char sysid[LACP_SYSTEMIDSTR_MAX+1];
1675168561Sthompsa	char portid[LACP_PORTIDSTR_MAX+1];
1676168561Sthompsa
1677168561Sthompsa	snprintf(buf, buflen, "(%s,%04X,%s)",
1678168561Sthompsa	    lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)),
1679168561Sthompsa	    ntohs(peer->lip_key),
1680168561Sthompsa	    lacp_format_portid(&peer->lip_portid, portid, sizeof(portid)));
1681168561Sthompsa
1682168561Sthompsa	return (buf);
1683168561Sthompsa}
1684168561Sthompsa
1685168561Sthompsaconst char *
1686168561Sthompsalacp_format_lagid(const struct lacp_peerinfo *a,
1687168561Sthompsa    const struct lacp_peerinfo *b, char *buf, size_t buflen)
1688168561Sthompsa{
1689168561Sthompsa	char astr[LACP_PARTNERSTR_MAX+1];
1690168561Sthompsa	char bstr[LACP_PARTNERSTR_MAX+1];
1691168561Sthompsa
1692168561Sthompsa#if 0
1693168561Sthompsa	/*
1694168561Sthompsa	 * there's a convention to display small numbered peer
1695168561Sthompsa	 * in the left.
1696168561Sthompsa	 */
1697168561Sthompsa
1698168561Sthompsa	if (lacp_compare_peerinfo(a, b) > 0) {
1699168561Sthompsa		const struct lacp_peerinfo *t;
1700168561Sthompsa
1701168561Sthompsa		t = a;
1702168561Sthompsa		a = b;
1703168561Sthompsa		b = t;
1704168561Sthompsa	}
1705168561Sthompsa#endif
1706168561Sthompsa
1707168561Sthompsa	snprintf(buf, buflen, "[%s,%s]",
1708168561Sthompsa	    lacp_format_partner(a, astr, sizeof(astr)),
1709168561Sthompsa	    lacp_format_partner(b, bstr, sizeof(bstr)));
1710168561Sthompsa
1711168561Sthompsa	return (buf);
1712168561Sthompsa}
1713168561Sthompsa
1714168561Sthompsaconst char *
1715168561Sthompsalacp_format_lagid_aggregator(const struct lacp_aggregator *la,
1716168561Sthompsa    char *buf, size_t buflen)
1717168561Sthompsa{
1718168561Sthompsa	if (la == NULL) {
1719168561Sthompsa		return ("(none)");
1720168561Sthompsa	}
1721168561Sthompsa
1722168561Sthompsa	return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen));
1723168561Sthompsa}
1724168561Sthompsa
1725168561Sthompsaconst char *
1726168561Sthompsalacp_format_state(uint8_t state, char *buf, size_t buflen)
1727168561Sthompsa{
1728168561Sthompsa	snprintf(buf, buflen, "%b", state, LACP_STATE_BITS);
1729168561Sthompsa	return (buf);
1730168561Sthompsa}
1731168561Sthompsa
1732168561Sthompsastatic void
1733168561Sthompsalacp_dump_lacpdu(const struct lacpdu *du)
1734168561Sthompsa{
1735168561Sthompsa	char buf[LACP_PARTNERSTR_MAX+1];
1736168561Sthompsa	char buf2[LACP_STATESTR_MAX+1];
1737168561Sthompsa
1738168561Sthompsa	printf("actor=%s\n",
1739168561Sthompsa	    lacp_format_partner(&du->ldu_actor, buf, sizeof(buf)));
1740168561Sthompsa	printf("actor.state=%s\n",
1741168561Sthompsa	    lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2)));
1742168561Sthompsa	printf("partner=%s\n",
1743168561Sthompsa	    lacp_format_partner(&du->ldu_partner, buf, sizeof(buf)));
1744168561Sthompsa	printf("partner.state=%s\n",
1745168561Sthompsa	    lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2)));
1746168561Sthompsa
1747168561Sthompsa	printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay));
1748168561Sthompsa}
1749168561Sthompsa
1750168561Sthompsastatic void
1751168561Sthompsalacp_dprintf(const struct lacp_port *lp, const char *fmt, ...)
1752168561Sthompsa{
1753168561Sthompsa	va_list va;
1754168561Sthompsa
1755168561Sthompsa	if (lp) {
1756168561Sthompsa		printf("%s: ", lp->lp_ifp->if_xname);
1757168561Sthompsa	}
1758168561Sthompsa
1759168561Sthompsa	va_start(va, fmt);
1760168561Sthompsa	vprintf(fmt, va);
1761168561Sthompsa	va_end(va);
1762168561Sthompsa}
1763168561Sthompsa#endif
1764