xref: /titanic_50/usr/src/lib/libdladm/common/libdlaggr.c (revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac)
1f595a68aSyz147064 /*
2f595a68aSyz147064  * CDDL HEADER START
3f595a68aSyz147064  *
4f595a68aSyz147064  * The contents of this file are subject to the terms of the
5f595a68aSyz147064  * Common Development and Distribution License (the "License").
6f595a68aSyz147064  * You may not use this file except in compliance with the License.
7f595a68aSyz147064  *
8f595a68aSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f595a68aSyz147064  * or http://www.opensolaris.org/os/licensing.
10f595a68aSyz147064  * See the License for the specific language governing permissions
11f595a68aSyz147064  * and limitations under the License.
12f595a68aSyz147064  *
13f595a68aSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14f595a68aSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f595a68aSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16f595a68aSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17f595a68aSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18f595a68aSyz147064  *
19f595a68aSyz147064  * CDDL HEADER END
20f595a68aSyz147064  */
21f595a68aSyz147064 /*
22*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23f595a68aSyz147064  * Use is subject to license terms.
24f595a68aSyz147064  */
25f595a68aSyz147064 
26f595a68aSyz147064 #include <stdio.h>
27f595a68aSyz147064 #include <sys/types.h>
28f595a68aSyz147064 #include <sys/stat.h>
29f595a68aSyz147064 #include <string.h>
30f595a68aSyz147064 #include <fcntl.h>
31f595a68aSyz147064 #include <unistd.h>
32f595a68aSyz147064 #include <stropts.h>
33f595a68aSyz147064 #include <stdlib.h>
34f595a68aSyz147064 #include <errno.h>
35d62bc4baSyz147064 #include <assert.h>
36f595a68aSyz147064 #include <strings.h>
37f595a68aSyz147064 #include <libintl.h>
38f595a68aSyz147064 #include <net/if_types.h>
39f595a68aSyz147064 #include <net/if_dl.h>
40da14cebeSEric Cheng #include <sys/dld.h>
41d62bc4baSyz147064 #include <libdllink.h>
42d62bc4baSyz147064 #include <libdlvlan.h>
43f595a68aSyz147064 #include <libdlaggr.h>
44f595a68aSyz147064 #include <libdladm_impl.h>
45f595a68aSyz147064 
46f595a68aSyz147064 /*
47f595a68aSyz147064  * Link Aggregation Administration Library.
48f595a68aSyz147064  *
49f595a68aSyz147064  * This library is used by administration tools such as dladm(1M) to
50f595a68aSyz147064  * configure link aggregations.
51f595a68aSyz147064  */
52f595a68aSyz147064 
53f595a68aSyz147064 /* Limits on buffer size for LAIOC_INFO request */
54f595a68aSyz147064 #define	MIN_INFO_SIZE (4*1024)
55f595a68aSyz147064 #define	MAX_INFO_SIZE (128*1024)
56f595a68aSyz147064 
57f595a68aSyz147064 static uchar_t	zero_mac[] = {0, 0, 0, 0, 0, 0};
58d62bc4baSyz147064 #define	VALID_PORT_MAC(mac)						\
59d62bc4baSyz147064 	(((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) &&	\
60*0dc2366fSVenugopal Iyer 	(!((mac)[0] & 0x01)))
61f595a68aSyz147064 
622b24ab6bSSebastien Roy #define	PORT_DELIMITER	":"
63f595a68aSyz147064 
64f595a68aSyz147064 typedef struct dladm_aggr_modify_attr {
65f595a68aSyz147064 	uint32_t	ld_policy;
66f595a68aSyz147064 	boolean_t	ld_mac_fixed;
67f595a68aSyz147064 	uchar_t		ld_mac[ETHERADDRL];
68f595a68aSyz147064 	aggr_lacp_mode_t ld_lacp_mode;
69f595a68aSyz147064 	aggr_lacp_timer_t ld_lacp_timer;
70f595a68aSyz147064 } dladm_aggr_modify_attr_t;
71f595a68aSyz147064 
72f595a68aSyz147064 typedef struct policy_s {
73f595a68aSyz147064 	char		*pol_name;
74f595a68aSyz147064 	uint32_t	policy;
75f595a68aSyz147064 } policy_t;
76f595a68aSyz147064 
77f595a68aSyz147064 static policy_t policies[] = {
78f595a68aSyz147064 	{"L2",		AGGR_POLICY_L2},
79f595a68aSyz147064 	{"L3",		AGGR_POLICY_L3},
80f595a68aSyz147064 	{"L4",		AGGR_POLICY_L4}};
81f595a68aSyz147064 
82f595a68aSyz147064 #define	NPOLICIES	(sizeof (policies) / sizeof (policy_t))
83f595a68aSyz147064 
84f595a68aSyz147064 typedef struct dladm_aggr_lacpmode_s {
85f595a68aSyz147064 	char		*mode_str;
86f595a68aSyz147064 	aggr_lacp_mode_t mode_id;
87f595a68aSyz147064 } dladm_aggr_lacpmode_t;
88f595a68aSyz147064 
89f595a68aSyz147064 static dladm_aggr_lacpmode_t lacp_modes[] = {
90f595a68aSyz147064 	{"off", AGGR_LACP_OFF},
91f595a68aSyz147064 	{"active", AGGR_LACP_ACTIVE},
92f595a68aSyz147064 	{"passive", AGGR_LACP_PASSIVE}};
93f595a68aSyz147064 
94f595a68aSyz147064 #define	NLACP_MODES	(sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
95f595a68aSyz147064 
96f595a68aSyz147064 typedef struct dladm_aggr_lacptimer_s {
97f595a68aSyz147064 	char		*lt_str;
98f595a68aSyz147064 	aggr_lacp_timer_t lt_id;
99f595a68aSyz147064 } dladm_aggr_lacptimer_t;
100f595a68aSyz147064 
101f595a68aSyz147064 static dladm_aggr_lacptimer_t lacp_timers[] = {
102f595a68aSyz147064 	{"short", AGGR_LACP_TIMER_SHORT},
103f595a68aSyz147064 	{"long", AGGR_LACP_TIMER_LONG}};
104f595a68aSyz147064 
105f595a68aSyz147064 #define	NLACP_TIMERS	(sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
106f595a68aSyz147064 
107f595a68aSyz147064 typedef struct dladm_aggr_port_state {
108f595a68aSyz147064 	char			*state_str;
109f595a68aSyz147064 	aggr_port_state_t	state_id;
110f595a68aSyz147064 } dladm_aggr_port_state_t;
111f595a68aSyz147064 
112f595a68aSyz147064 static dladm_aggr_port_state_t port_states[] = {
113f595a68aSyz147064 	{"standby", AGGR_PORT_STATE_STANDBY },
114f595a68aSyz147064 	{"attached", AGGR_PORT_STATE_ATTACHED }
115f595a68aSyz147064 };
116f595a68aSyz147064 
117f595a68aSyz147064 #define	NPORT_STATES	\
118f595a68aSyz147064 	(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
119f595a68aSyz147064 
1202b24ab6bSSebastien Roy static dladm_status_t
1212b24ab6bSSebastien Roy write_port(dladm_handle_t handle, char *portstr, datalink_id_t portid,
1222b24ab6bSSebastien Roy     size_t portstrsize)
1232b24ab6bSSebastien Roy {
1242b24ab6bSSebastien Roy 	char		pname[MAXLINKNAMELEN + 1];
1252b24ab6bSSebastien Roy 	dladm_status_t	status;
1262b24ab6bSSebastien Roy 
1272b24ab6bSSebastien Roy 	if ((status = dladm_datalink_id2info(handle, portid, NULL, NULL, NULL,
1282b24ab6bSSebastien Roy 	    pname, sizeof (pname))) != DLADM_STATUS_OK)
1292b24ab6bSSebastien Roy 		return (status);
1302b24ab6bSSebastien Roy 	(void) strlcat(pname, PORT_DELIMITER, sizeof (pname));
1312b24ab6bSSebastien Roy 	if (strlcat(portstr, pname, portstrsize) >= portstrsize)
1322b24ab6bSSebastien Roy 		status = DLADM_STATUS_TOOSMALL;
1332b24ab6bSSebastien Roy 	return (status);
1342b24ab6bSSebastien Roy }
1352b24ab6bSSebastien Roy 
1362b24ab6bSSebastien Roy static dladm_status_t
1372b24ab6bSSebastien Roy read_port(dladm_handle_t handle, char **portstr, datalink_id_t *portid)
1382b24ab6bSSebastien Roy {
1392b24ab6bSSebastien Roy 	dladm_status_t	status;
1402b24ab6bSSebastien Roy 	char		*pname;
1412b24ab6bSSebastien Roy 
1422b24ab6bSSebastien Roy 	if ((pname = strtok(*portstr, PORT_DELIMITER)) == NULL)
1432b24ab6bSSebastien Roy 		return (DLADM_STATUS_REPOSITORYINVAL);
1442b24ab6bSSebastien Roy 	*portstr += (strlen(pname) + 1);
1452b24ab6bSSebastien Roy 	status = dladm_name2info(handle, pname, portid, NULL, NULL, NULL);
1462b24ab6bSSebastien Roy 	return (status);
1472b24ab6bSSebastien Roy }
1482b24ab6bSSebastien Roy 
149d62bc4baSyz147064 static int
1504ac67f02SAnurag S. Maskey i_dladm_aggr_ioctl(dladm_handle_t handle, int cmd, void *ptr)
151d62bc4baSyz147064 {
1524ac67f02SAnurag S. Maskey 	return (ioctl(dladm_dld_fd(handle), cmd, ptr));
153d62bc4baSyz147064 }
154f595a68aSyz147064 
155f595a68aSyz147064 /*
156d62bc4baSyz147064  * Caller must free attr.lg_ports. The ptr pointer is advanced while convert
157d62bc4baSyz147064  * the laioc_info_t to the dladm_aggr_grp_attr_t structure.
158f595a68aSyz147064  */
159f595a68aSyz147064 static int
160d62bc4baSyz147064 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp)
161f595a68aSyz147064 {
162f595a68aSyz147064 	laioc_info_group_t	*grp;
163f595a68aSyz147064 	laioc_info_port_t	*port;
164d62bc4baSyz147064 	int			i;
165d62bc4baSyz147064 	void			*where = (*ptr);
166f595a68aSyz147064 
167d62bc4baSyz147064 	grp = (laioc_info_group_t *)where;
168d62bc4baSyz147064 
169d62bc4baSyz147064 	attrp->lg_linkid = grp->lg_linkid;
170d62bc4baSyz147064 	attrp->lg_key = grp->lg_key;
171d62bc4baSyz147064 	attrp->lg_nports = grp->lg_nports;
172d62bc4baSyz147064 	attrp->lg_policy = grp->lg_policy;
173d62bc4baSyz147064 	attrp->lg_lacp_mode = grp->lg_lacp_mode;
174d62bc4baSyz147064 	attrp->lg_lacp_timer = grp->lg_lacp_timer;
175d62bc4baSyz147064 	attrp->lg_force = grp->lg_force;
176d62bc4baSyz147064 
177d62bc4baSyz147064 	bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL);
178d62bc4baSyz147064 	attrp->lg_mac_fixed = grp->lg_mac_fixed;
179d62bc4baSyz147064 
180d62bc4baSyz147064 	if ((attrp->lg_ports = malloc(grp->lg_nports *
181d62bc4baSyz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
182d62bc4baSyz147064 		errno = ENOMEM;
183d62bc4baSyz147064 		goto fail;
184d62bc4baSyz147064 	}
185d62bc4baSyz147064 
186d62bc4baSyz147064 	where = (grp + 1);
187d62bc4baSyz147064 
188d62bc4baSyz147064 	/*
189d62bc4baSyz147064 	 * Go through each port that is part of the group.
190d62bc4baSyz147064 	 */
191d62bc4baSyz147064 	for (i = 0; i < grp->lg_nports; i++) {
192d62bc4baSyz147064 		port = (laioc_info_port_t *)where;
193d62bc4baSyz147064 
194d62bc4baSyz147064 		attrp->lg_ports[i].lp_linkid = port->lp_linkid;
195d62bc4baSyz147064 		bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL);
196d62bc4baSyz147064 		attrp->lg_ports[i].lp_state = port->lp_state;
197d62bc4baSyz147064 		attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state;
198d62bc4baSyz147064 
199d62bc4baSyz147064 		where = (port + 1);
200d62bc4baSyz147064 	}
201d62bc4baSyz147064 	*ptr = where;
202d62bc4baSyz147064 	return (0);
203d62bc4baSyz147064 fail:
204f595a68aSyz147064 	return (-1);
205d62bc4baSyz147064 }
206d62bc4baSyz147064 
207d62bc4baSyz147064 /*
208d62bc4baSyz147064  * Get active configuration of a specific aggregation.
209d62bc4baSyz147064  * Caller must free attrp->la_ports.
210d62bc4baSyz147064  */
211d62bc4baSyz147064 static dladm_status_t
2124ac67f02SAnurag S. Maskey i_dladm_aggr_info_active(dladm_handle_t handle, datalink_id_t linkid,
2134ac67f02SAnurag S. Maskey     dladm_aggr_grp_attr_t *attrp)
214d62bc4baSyz147064 {
215d62bc4baSyz147064 	laioc_info_t *ioc;
216eae72b5bSSebastien Roy 	int bufsize;
217d62bc4baSyz147064 	void *where;
218d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
219f595a68aSyz147064 
220f595a68aSyz147064 	bufsize = MIN_INFO_SIZE;
221f595a68aSyz147064 	ioc = (laioc_info_t *)calloc(1, bufsize);
222d62bc4baSyz147064 	if (ioc == NULL)
223d62bc4baSyz147064 		return (DLADM_STATUS_NOMEM);
224d62bc4baSyz147064 
225d62bc4baSyz147064 	ioc->li_group_linkid = linkid;
226f595a68aSyz147064 
227f595a68aSyz147064 tryagain:
228eae72b5bSSebastien Roy 	ioc->li_bufsize = bufsize;
2294ac67f02SAnurag S. Maskey 	if (i_dladm_aggr_ioctl(handle, LAIOC_INFO, ioc) != 0) {
230f595a68aSyz147064 		if (errno == ENOSPC) {
231f595a68aSyz147064 			/*
232f595a68aSyz147064 			 * The LAIOC_INFO call failed due to a short
233f595a68aSyz147064 			 * buffer. Reallocate the buffer and try again.
234f595a68aSyz147064 			 */
235f595a68aSyz147064 			bufsize *= 2;
236f595a68aSyz147064 			if (bufsize <= MAX_INFO_SIZE) {
237f595a68aSyz147064 				ioc = (laioc_info_t *)realloc(ioc, bufsize);
238f595a68aSyz147064 				if (ioc != NULL) {
239f595a68aSyz147064 					bzero(ioc, sizeof (bufsize));
240f595a68aSyz147064 					goto tryagain;
241f595a68aSyz147064 				}
242f595a68aSyz147064 			}
243f595a68aSyz147064 		}
244d62bc4baSyz147064 		status = dladm_errno2status(errno);
245f595a68aSyz147064 		goto bail;
246f595a68aSyz147064 	}
247f595a68aSyz147064 
248f595a68aSyz147064 	/*
249f595a68aSyz147064 	 * Go through each group returned by the aggregation driver.
250f595a68aSyz147064 	 */
251f595a68aSyz147064 	where = (char *)(ioc + 1);
252d62bc4baSyz147064 	if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) {
253d62bc4baSyz147064 		status = dladm_errno2status(errno);
254f595a68aSyz147064 		goto bail;
255f595a68aSyz147064 	}
256f595a68aSyz147064 
257f595a68aSyz147064 bail:
258f595a68aSyz147064 	free(ioc);
259f595a68aSyz147064 	return (status);
260f595a68aSyz147064 }
261f595a68aSyz147064 
262d62bc4baSyz147064 /*
263d62bc4baSyz147064  * Get configuration information of a specific aggregation.
264d62bc4baSyz147064  * Caller must free attrp->la_ports.
265d62bc4baSyz147064  */
266d62bc4baSyz147064 static dladm_status_t
2674ac67f02SAnurag S. Maskey i_dladm_aggr_info_persist(dladm_handle_t handle, datalink_id_t linkid,
2684ac67f02SAnurag S. Maskey     dladm_aggr_grp_attr_t *attrp)
269d62bc4baSyz147064 {
270d62bc4baSyz147064 	dladm_conf_t	conf;
271d62bc4baSyz147064 	uint32_t	nports, i;
2722b24ab6bSSebastien Roy 	char		*portstr = NULL, *next;
273d62bc4baSyz147064 	dladm_status_t	status;
274d62bc4baSyz147064 	uint64_t	u64;
275d62bc4baSyz147064 	int		size;
276d62bc4baSyz147064 	char		macstr[ETHERADDRL * 3];
277f595a68aSyz147064 
278d62bc4baSyz147064 	attrp->lg_linkid = linkid;
2794ac67f02SAnurag S. Maskey 	if ((status = dladm_read_conf(handle, linkid, &conf)) !=
2804ac67f02SAnurag S. Maskey 	    DLADM_STATUS_OK)
281d62bc4baSyz147064 		return (status);
282f595a68aSyz147064 
2834ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
284d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
285d62bc4baSyz147064 		goto done;
286d62bc4baSyz147064 	attrp->lg_key = (uint16_t)u64;
287d62bc4baSyz147064 
2884ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
2894ac67f02SAnurag S. Maskey 	    sizeof (u64));
290d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
291d62bc4baSyz147064 		goto done;
292d62bc4baSyz147064 	attrp->lg_policy = (uint32_t)u64;
293d62bc4baSyz147064 
2944ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FFIXMACADDR,
2954ac67f02SAnurag S. Maskey 	    &attrp->lg_mac_fixed, sizeof (boolean_t));
296d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
297d62bc4baSyz147064 		goto done;
298d62bc4baSyz147064 
299d62bc4baSyz147064 	if (attrp->lg_mac_fixed) {
300d62bc4baSyz147064 		boolean_t fixed;
301d62bc4baSyz147064 
3024ac67f02SAnurag S. Maskey 		if ((status = dladm_get_conf_field(handle, conf, FMACADDR,
3034ac67f02SAnurag S. Maskey 		    macstr, sizeof (macstr))) != DLADM_STATUS_OK) {
304d62bc4baSyz147064 			goto done;
305d62bc4baSyz147064 		}
306d62bc4baSyz147064 		if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) {
307f595a68aSyz147064 			status = DLADM_STATUS_REPOSITORYINVAL;
308f595a68aSyz147064 			goto done;
309f595a68aSyz147064 		}
310d62bc4baSyz147064 	}
311f595a68aSyz147064 
3124ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FFORCE, &attrp->lg_force,
313d62bc4baSyz147064 	    sizeof (boolean_t));
314d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
315f595a68aSyz147064 		goto done;
316f595a68aSyz147064 
3174ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
3184ac67f02SAnurag S. Maskey 	    sizeof (u64));
319d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
320d62bc4baSyz147064 		goto done;
321d62bc4baSyz147064 	attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64;
322f595a68aSyz147064 
3234ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
3244ac67f02SAnurag S. Maskey 	    sizeof (u64));
325d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
326d62bc4baSyz147064 		goto done;
327d62bc4baSyz147064 	attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64;
328f595a68aSyz147064 
3294ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
3304ac67f02SAnurag S. Maskey 	    sizeof (u64));
331d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
332d62bc4baSyz147064 		goto done;
333d62bc4baSyz147064 	nports = (uint32_t)u64;
334d62bc4baSyz147064 	attrp->lg_nports = nports;
335f595a68aSyz147064 
3362b24ab6bSSebastien Roy 	size = nports * (MAXLINKNAMELEN + 1) + 1;
337d62bc4baSyz147064 	if ((portstr = calloc(1, size)) == NULL) {
338f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
339f595a68aSyz147064 		goto done;
340f595a68aSyz147064 	}
341f595a68aSyz147064 
3424ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FPORTS, portstr, size);
3432b24ab6bSSebastien Roy 	if (status != DLADM_STATUS_OK)
344f595a68aSyz147064 		goto done;
345f595a68aSyz147064 
346d62bc4baSyz147064 	if ((attrp->lg_ports = malloc(nports *
347d62bc4baSyz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
348d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
349d62bc4baSyz147064 		goto done;
350d62bc4baSyz147064 	}
351d62bc4baSyz147064 
352d62bc4baSyz147064 	for (next = portstr, i = 0; i < nports; i++) {
3532b24ab6bSSebastien Roy 		if ((status = read_port(handle, &next,
3542b24ab6bSSebastien Roy 		    &attrp->lg_ports[i].lp_linkid)) != DLADM_STATUS_OK)
355d62bc4baSyz147064 			free(attrp->lg_ports);
356d62bc4baSyz147064 	}
357d62bc4baSyz147064 
358d62bc4baSyz147064 done:
3592b24ab6bSSebastien Roy 	free(portstr);
3604ac67f02SAnurag S. Maskey 	dladm_destroy_conf(handle, conf);
361d62bc4baSyz147064 	return (status);
362d62bc4baSyz147064 }
363d62bc4baSyz147064 
364d62bc4baSyz147064 dladm_status_t
3654ac67f02SAnurag S. Maskey dladm_aggr_info(dladm_handle_t handle, datalink_id_t linkid,
3664ac67f02SAnurag S. Maskey     dladm_aggr_grp_attr_t *attrp, uint32_t flags)
367d62bc4baSyz147064 {
368d62bc4baSyz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
369d62bc4baSyz147064 	if (flags == DLADM_OPT_ACTIVE)
3704ac67f02SAnurag S. Maskey 		return (i_dladm_aggr_info_active(handle, linkid, attrp));
371f595a68aSyz147064 	else
3724ac67f02SAnurag S. Maskey 		return (i_dladm_aggr_info_persist(handle, linkid, attrp));
373f595a68aSyz147064 }
374f595a68aSyz147064 
375d62bc4baSyz147064 /*
376d62bc4baSyz147064  * Add or remove one or more ports to/from an existing link aggregation.
377d62bc4baSyz147064  */
378d62bc4baSyz147064 static dladm_status_t
3794ac67f02SAnurag S. Maskey i_dladm_aggr_add_rmv(dladm_handle_t handle, datalink_id_t linkid,
3804ac67f02SAnurag S. Maskey     uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd)
381d62bc4baSyz147064 {
382d62bc4baSyz147064 	char *orig_portstr = NULL, *portstr = NULL;
3838de9d095Syz147064 	laioc_add_rem_t *iocp = NULL;
384d62bc4baSyz147064 	laioc_port_t *ioc_ports;
385d62bc4baSyz147064 	uint32_t orig_nports, result_nports, len, i, j;
386d62bc4baSyz147064 	dladm_conf_t conf;
387d62bc4baSyz147064 	datalink_class_t class;
388d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
389d62bc4baSyz147064 	int size;
390d62bc4baSyz147064 	uint64_t u64;
391d62bc4baSyz147064 	uint32_t media;
392d62bc4baSyz147064 
393d62bc4baSyz147064 	if (nports == 0)
394d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
395d62bc4baSyz147064 
396d62bc4baSyz147064 	/*
397d62bc4baSyz147064 	 * Sanity check - aggregations can only be created over Ethernet
398b509e89bSRishi Srivatsavai 	 * physical links and simnets.
399d62bc4baSyz147064 	 */
400d62bc4baSyz147064 	for (i = 0; i < nports; i++) {
4014ac67f02SAnurag S. Maskey 		if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL,
402d62bc4baSyz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
403b509e89bSRishi Srivatsavai 		    !((class == DATALINK_CLASS_PHYS) ||
404b509e89bSRishi Srivatsavai 		    (class == DATALINK_CLASS_SIMNET)) || (media != DL_ETHER)) {
405d62bc4baSyz147064 			return (DLADM_STATUS_BADARG);
406d62bc4baSyz147064 		}
407d62bc4baSyz147064 	}
408d62bc4baSyz147064 
409d62bc4baSyz147064 	/*
410d62bc4baSyz147064 	 * First, update the persistent configuration if requested.  We only
411d62bc4baSyz147064 	 * need to update the FPORTS and FNPORTS fields of this aggregation.
412d62bc4baSyz147064 	 * Note that FPORTS is a list of port linkids separated by
4132b24ab6bSSebastien Roy 	 * PORT_DELIMITER (':').
414d62bc4baSyz147064 	 */
415d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
4164ac67f02SAnurag S. Maskey 		status = dladm_read_conf(handle, linkid, &conf);
417d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
418d62bc4baSyz147064 			return (status);
419d62bc4baSyz147064 
420d62bc4baSyz147064 		/*
421d62bc4baSyz147064 		 * Get the original configuration of FNPORTS and FPORTS.
422d62bc4baSyz147064 		 */
4234ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
424d62bc4baSyz147064 		    sizeof (u64));
425d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
426d62bc4baSyz147064 			goto destroyconf;
427d62bc4baSyz147064 		orig_nports = (uint32_t)u64;
428d62bc4baSyz147064 
429d62bc4baSyz147064 		/*
430d62bc4baSyz147064 		 * At least one port needs to be in the aggregation.
431d62bc4baSyz147064 		 */
432d62bc4baSyz147064 		if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) {
433d62bc4baSyz147064 			status = DLADM_STATUS_BADARG;
434d62bc4baSyz147064 			goto destroyconf;
435d62bc4baSyz147064 		}
436d62bc4baSyz147064 
4372b24ab6bSSebastien Roy 		size = orig_nports * (MAXLINKNAMELEN + 1) + 1;
438d62bc4baSyz147064 		if ((orig_portstr = calloc(1, size)) == NULL) {
439d62bc4baSyz147064 			status = dladm_errno2status(errno);
440d62bc4baSyz147064 			goto destroyconf;
441d62bc4baSyz147064 		}
442d62bc4baSyz147064 
4434ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FPORTS,
4444ac67f02SAnurag S. Maskey 		    orig_portstr, size);
445d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
446d62bc4baSyz147064 			goto destroyconf;
447d62bc4baSyz147064 
448d62bc4baSyz147064 		result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports :
449d62bc4baSyz147064 		    orig_nports;
450d62bc4baSyz147064 
4512b24ab6bSSebastien Roy 		size = result_nports * (MAXLINKNAMELEN + 1) + 1;
452d62bc4baSyz147064 		if ((portstr = calloc(1, size)) == NULL) {
453d62bc4baSyz147064 			status = dladm_errno2status(errno);
454d62bc4baSyz147064 			goto destroyconf;
455d62bc4baSyz147064 		}
456d62bc4baSyz147064 
457d62bc4baSyz147064 		/*
458d62bc4baSyz147064 		 * get the new configuration and set to result_nports and
459d62bc4baSyz147064 		 * portstr.
460d62bc4baSyz147064 		 */
461d62bc4baSyz147064 		if (cmd == LAIOC_ADD) {
462d62bc4baSyz147064 			(void) strlcpy(portstr, orig_portstr, size);
4632b24ab6bSSebastien Roy 			for (i = 0; i < nports; i++) {
4642b24ab6bSSebastien Roy 				status = write_port(handle, portstr,
4652b24ab6bSSebastien Roy 				    ports[i].lp_linkid, size);
4662b24ab6bSSebastien Roy 				if (status != DLADM_STATUS_OK) {
4672b24ab6bSSebastien Roy 					free(portstr);
4682b24ab6bSSebastien Roy 					goto destroyconf;
4692b24ab6bSSebastien Roy 				}
4702b24ab6bSSebastien Roy 			}
471d62bc4baSyz147064 		} else {
472d62bc4baSyz147064 			char *next;
473d62bc4baSyz147064 			datalink_id_t portid;
474d62bc4baSyz147064 			uint32_t remove = 0;
475d62bc4baSyz147064 
476d62bc4baSyz147064 			for (next = orig_portstr, j = 0; j < orig_nports; j++) {
477d62bc4baSyz147064 				/*
478d62bc4baSyz147064 				 * Read the portids from the old configuration
479d62bc4baSyz147064 				 * one by one.
480d62bc4baSyz147064 				 */
4812b24ab6bSSebastien Roy 				status = read_port(handle, &next, &portid);
482d62bc4baSyz147064 				if (status != DLADM_STATUS_OK) {
483d62bc4baSyz147064 					free(portstr);
484d62bc4baSyz147064 					goto destroyconf;
485d62bc4baSyz147064 				}
486d62bc4baSyz147064 
487d62bc4baSyz147064 				/*
488d62bc4baSyz147064 				 * See whether this port is in the removal
489d62bc4baSyz147064 				 * list.  If not, copy to the new config.
490d62bc4baSyz147064 				 */
491d62bc4baSyz147064 				for (i = 0; i < nports; i++) {
492d62bc4baSyz147064 					if (ports[i].lp_linkid == portid)
493d62bc4baSyz147064 						break;
494d62bc4baSyz147064 				}
495d62bc4baSyz147064 				if (i == nports) {
4962b24ab6bSSebastien Roy 					status = write_port(handle, portstr,
4972b24ab6bSSebastien Roy 					    portid, size);
4982b24ab6bSSebastien Roy 					if (status != DLADM_STATUS_OK) {
4992b24ab6bSSebastien Roy 						free(portstr);
5002b24ab6bSSebastien Roy 						goto destroyconf;
5012b24ab6bSSebastien Roy 					}
502d62bc4baSyz147064 				} else {
503d62bc4baSyz147064 					remove++;
504d62bc4baSyz147064 				}
505d62bc4baSyz147064 			}
506d62bc4baSyz147064 			if (remove != nports) {
507d62bc4baSyz147064 				status = DLADM_STATUS_LINKINVAL;
508d62bc4baSyz147064 				free(portstr);
509d62bc4baSyz147064 				goto destroyconf;
510d62bc4baSyz147064 			}
511d62bc4baSyz147064 			result_nports -= nports;
512d62bc4baSyz147064 		}
513d62bc4baSyz147064 
514d62bc4baSyz147064 		u64 = result_nports;
5154ac67f02SAnurag S. Maskey 		if ((status = dladm_set_conf_field(handle, conf, FNPORTS,
516d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) {
517d62bc4baSyz147064 			free(portstr);
518d62bc4baSyz147064 			goto destroyconf;
519d62bc4baSyz147064 		}
520d62bc4baSyz147064 
5214ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FPORTS,
5224ac67f02SAnurag S. Maskey 		    DLADM_TYPE_STR, portstr);
523d62bc4baSyz147064 		free(portstr);
524d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
525d62bc4baSyz147064 			goto destroyconf;
526d62bc4baSyz147064 
527d62bc4baSyz147064 		/*
528d62bc4baSyz147064 		 * Write the new configuration to the persistent repository.
529d62bc4baSyz147064 		 */
5304ac67f02SAnurag S. Maskey 		status = dladm_write_conf(handle, conf);
531d62bc4baSyz147064 
532d62bc4baSyz147064 destroyconf:
5334ac67f02SAnurag S. Maskey 		dladm_destroy_conf(handle, conf);
534d62bc4baSyz147064 		if (status != DLADM_STATUS_OK) {
535d62bc4baSyz147064 			free(orig_portstr);
536d62bc4baSyz147064 			return (status);
537d62bc4baSyz147064 		}
538d62bc4baSyz147064 	}
539d62bc4baSyz147064 
540d62bc4baSyz147064 	/*
541d62bc4baSyz147064 	 * If the caller only requested to update the persistent
542d62bc4baSyz147064 	 * configuration, we are done.
543d62bc4baSyz147064 	 */
544d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
545d62bc4baSyz147064 		goto done;
546d62bc4baSyz147064 
547d62bc4baSyz147064 	/*
548d62bc4baSyz147064 	 * Update the active configuration.
549d62bc4baSyz147064 	 */
550d62bc4baSyz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
551d62bc4baSyz147064 	if ((iocp = malloc(len)) == NULL) {
552d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
553d62bc4baSyz147064 		goto done;
554d62bc4baSyz147064 	}
555d62bc4baSyz147064 
556d62bc4baSyz147064 	iocp->la_linkid = linkid;
557d62bc4baSyz147064 	iocp->la_nports = nports;
558d62bc4baSyz147064 	if (cmd == LAIOC_ADD)
559d62bc4baSyz147064 		iocp->la_force = (flags & DLADM_OPT_FORCE);
560d62bc4baSyz147064 
561d62bc4baSyz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
562d62bc4baSyz147064 	for (i = 0; i < nports; i++)
563d62bc4baSyz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
564d62bc4baSyz147064 
5654ac67f02SAnurag S. Maskey 	if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0)
566d62bc4baSyz147064 		status = dladm_errno2status(errno);
567f595a68aSyz147064 
568f595a68aSyz147064 done:
569f595a68aSyz147064 	free(iocp);
570d62bc4baSyz147064 
571d62bc4baSyz147064 	/*
572d62bc4baSyz147064 	 * If the active configuration update fails, restore the old
573d62bc4baSyz147064 	 * persistent configuration if we've changed that.
574d62bc4baSyz147064 	 */
575d62bc4baSyz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
5764ac67f02SAnurag S. Maskey 		if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) {
577d62bc4baSyz147064 			u64 = orig_nports;
5784ac67f02SAnurag S. Maskey 			if ((dladm_set_conf_field(handle, conf, FNPORTS,
579d62bc4baSyz147064 			    DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) &&
5804ac67f02SAnurag S. Maskey 			    (dladm_set_conf_field(handle, conf, FPORTS,
5814ac67f02SAnurag S. Maskey 			    DLADM_TYPE_STR, orig_portstr) == DLADM_STATUS_OK)) {
5824ac67f02SAnurag S. Maskey 				(void) dladm_write_conf(handle, conf);
583d62bc4baSyz147064 			}
5844ac67f02SAnurag S. Maskey 			(void) dladm_destroy_conf(handle, conf);
585d62bc4baSyz147064 		}
586d62bc4baSyz147064 	}
587d62bc4baSyz147064 	free(orig_portstr);
588f595a68aSyz147064 	return (status);
589f595a68aSyz147064 }
590f595a68aSyz147064 
591f595a68aSyz147064 /*
592f595a68aSyz147064  * Send a modify command to the link aggregation driver.
593f595a68aSyz147064  */
594f595a68aSyz147064 static dladm_status_t
5954ac67f02SAnurag S. Maskey i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid,
5964ac67f02SAnurag S. Maskey     uint32_t mask, dladm_aggr_modify_attr_t *attr)
597f595a68aSyz147064 {
598f595a68aSyz147064 	laioc_modify_t ioc;
599f595a68aSyz147064 
600d62bc4baSyz147064 	ioc.lu_linkid = linkid;
601f595a68aSyz147064 
602f595a68aSyz147064 	ioc.lu_modify_mask = 0;
603f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY)
604f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY;
605f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC)
606f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_MAC;
607f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
608f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE;
609f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
610f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER;
611f595a68aSyz147064 
612f595a68aSyz147064 	ioc.lu_policy = attr->ld_policy;
613f595a68aSyz147064 	ioc.lu_mac_fixed = attr->ld_mac_fixed;
614f595a68aSyz147064 	bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL);
615f595a68aSyz147064 	ioc.lu_lacp_mode = attr->ld_lacp_mode;
616f595a68aSyz147064 	ioc.lu_lacp_timer = attr->ld_lacp_timer;
617f595a68aSyz147064 
6184ac67f02SAnurag S. Maskey 	if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) {
619f595a68aSyz147064 		if (errno == EINVAL)
620d62bc4baSyz147064 			return (DLADM_STATUS_MACADDRINVAL);
621f595a68aSyz147064 		else
622d62bc4baSyz147064 			return (dladm_errno2status(errno));
623d62bc4baSyz147064 	} else {
624d62bc4baSyz147064 		return (DLADM_STATUS_OK);
625f595a68aSyz147064 	}
626f595a68aSyz147064 }
627f595a68aSyz147064 
628f595a68aSyz147064 /*
629f595a68aSyz147064  * Send a create command to the link aggregation driver.
630f595a68aSyz147064  */
631f595a68aSyz147064 static dladm_status_t
6324ac67f02SAnurag S. Maskey i_dladm_aggr_create_sys(dladm_handle_t handle, datalink_id_t linkid,
6334ac67f02SAnurag S. Maskey     uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports,
6344ac67f02SAnurag S. Maskey     uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr,
635d62bc4baSyz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force)
636f595a68aSyz147064 {
637eae72b5bSSebastien Roy 	int i, len;
638d62bc4baSyz147064 	laioc_create_t *iocp = NULL;
639d62bc4baSyz147064 	laioc_port_t *ioc_ports;
640f595a68aSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
641f595a68aSyz147064 
642d62bc4baSyz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
643f595a68aSyz147064 	iocp = malloc(len);
644f595a68aSyz147064 	if (iocp == NULL)
645f595a68aSyz147064 		return (DLADM_STATUS_NOMEM);
646f595a68aSyz147064 
647d62bc4baSyz147064 	iocp->lc_key = key;
648d62bc4baSyz147064 	iocp->lc_linkid = linkid;
649d62bc4baSyz147064 	iocp->lc_nports = nports;
650d62bc4baSyz147064 	iocp->lc_policy = policy;
651d62bc4baSyz147064 	iocp->lc_lacp_mode = lacp_mode;
652d62bc4baSyz147064 	iocp->lc_lacp_timer = lacp_timer;
653d62bc4baSyz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
654d62bc4baSyz147064 	iocp->lc_force = force;
655f595a68aSyz147064 
656d62bc4baSyz147064 	for (i = 0; i < nports; i++)
657d62bc4baSyz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
658f595a68aSyz147064 
659d62bc4baSyz147064 	if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) {
660d62bc4baSyz147064 		status = DLADM_STATUS_MACADDRINVAL;
661d62bc4baSyz147064 		goto done;
662f595a68aSyz147064 	}
663f595a68aSyz147064 
664d62bc4baSyz147064 	bcopy(mac_addr, iocp->lc_mac, ETHERADDRL);
665d62bc4baSyz147064 	iocp->lc_mac_fixed = mac_addr_fixed;
666f595a68aSyz147064 
6674ac67f02SAnurag S. Maskey 	if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0)
668d62bc4baSyz147064 		status = dladm_errno2status(errno);
669f595a68aSyz147064 
670d62bc4baSyz147064 done:
671f595a68aSyz147064 	free(iocp);
672f595a68aSyz147064 	return (status);
673f595a68aSyz147064 }
674f595a68aSyz147064 
675f595a68aSyz147064 /*
676f595a68aSyz147064  * Invoked to bring up a link aggregation group.
677f595a68aSyz147064  */
678f595a68aSyz147064 static int
6794ac67f02SAnurag S. Maskey i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
680f595a68aSyz147064 {
681d62bc4baSyz147064 	dladm_status_t *statusp = (dladm_status_t *)arg;
682d62bc4baSyz147064 	dladm_aggr_grp_attr_t attr;
683d62bc4baSyz147064 	dladm_aggr_port_attr_db_t *ports = NULL;
684d62bc4baSyz147064 	uint16_t key = 0;
685f595a68aSyz147064 	int i, j;
686f595a68aSyz147064 	dladm_status_t status;
687f595a68aSyz147064 
6884ac67f02SAnurag S. Maskey 	status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST);
689d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
690d62bc4baSyz147064 		*statusp = status;
691d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
692d62bc4baSyz147064 	}
693f595a68aSyz147064 
694d62bc4baSyz147064 	if (attr.lg_key <= AGGR_MAX_KEY)
695d62bc4baSyz147064 		key = attr.lg_key;
696f595a68aSyz147064 
697d62bc4baSyz147064 	ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t));
698d62bc4baSyz147064 	if (ports == NULL) {
699d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
700d62bc4baSyz147064 		goto done;
701f595a68aSyz147064 	}
702f595a68aSyz147064 
703f595a68aSyz147064 	/*
704d62bc4baSyz147064 	 * Validate (and purge) each physical link associated with this
705d62bc4baSyz147064 	 * aggregation, if the specific hardware has been removed during
706d62bc4baSyz147064 	 * the system shutdown.
707f595a68aSyz147064 	 */
708d62bc4baSyz147064 	for (i = 0, j = 0; i < attr.lg_nports; i++) {
709d62bc4baSyz147064 		datalink_id_t	portid = attr.lg_ports[i].lp_linkid;
710d62bc4baSyz147064 		uint32_t	flags;
711d62bc4baSyz147064 		dladm_status_t	s;
712f595a68aSyz147064 
7134ac67f02SAnurag S. Maskey 		s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL,
7144ac67f02SAnurag S. Maskey 		    NULL, 0);
715d62bc4baSyz147064 		if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE))
716d62bc4baSyz147064 			continue;
717f595a68aSyz147064 
718d62bc4baSyz147064 		ports[j++].lp_linkid = portid;
719d62bc4baSyz147064 	}
720d62bc4baSyz147064 
721d62bc4baSyz147064 	if (j == 0) {
722d62bc4baSyz147064 		/*
723d62bc4baSyz147064 		 * All of the physical links are removed.
724d62bc4baSyz147064 		 */
725d62bc4baSyz147064 		status = DLADM_STATUS_BADARG;
726d62bc4baSyz147064 		goto done;
727d62bc4baSyz147064 	}
728d62bc4baSyz147064 
729d62bc4baSyz147064 	/*
730d62bc4baSyz147064 	 * Create active aggregation.
731d62bc4baSyz147064 	 */
7324ac67f02SAnurag S. Maskey 	if ((status = i_dladm_aggr_create_sys(handle, linkid,
733d62bc4baSyz147064 	    key, j, ports, attr.lg_policy, attr.lg_mac_fixed,
734d62bc4baSyz147064 	    (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode,
735d62bc4baSyz147064 	    attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) {
736d62bc4baSyz147064 		goto done;
737d62bc4baSyz147064 	}
738d62bc4baSyz147064 
7394ac67f02SAnurag S. Maskey 	if ((status = dladm_up_datalink_id(handle, linkid)) !=
7404ac67f02SAnurag S. Maskey 	    DLADM_STATUS_OK) {
741d62bc4baSyz147064 		laioc_delete_t ioc;
7422b24ab6bSSebastien Roy 
743d62bc4baSyz147064 		ioc.ld_linkid = linkid;
7444ac67f02SAnurag S. Maskey 		(void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc);
745d62bc4baSyz147064 	}
746d62bc4baSyz147064 done:
747d62bc4baSyz147064 	free(attr.lg_ports);
748d62bc4baSyz147064 	free(ports);
749d62bc4baSyz147064 
750d62bc4baSyz147064 	*statusp = status;
751d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
752d62bc4baSyz147064 }
753d62bc4baSyz147064 
754d62bc4baSyz147064 /*
755d62bc4baSyz147064  * Bring up one aggregation, or all persistent aggregations.  In the latter
756d62bc4baSyz147064  * case, the walk may terminate early if bringup of an aggregation fails.
757d62bc4baSyz147064  */
758d62bc4baSyz147064 dladm_status_t
7594ac67f02SAnurag S. Maskey dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid)
760f595a68aSyz147064 {
761f595a68aSyz147064 	dladm_status_t status;
762f595a68aSyz147064 
763d62bc4baSyz147064 	if (linkid == DATALINK_ALL_LINKID) {
7644ac67f02SAnurag S. Maskey 		(void) dladm_walk_datalink_id(i_dladm_aggr_up, handle, &status,
765d62bc4baSyz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
766d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
767f595a68aSyz147064 		return (DLADM_STATUS_OK);
768d62bc4baSyz147064 	} else {
7694ac67f02SAnurag S. Maskey 		(void) i_dladm_aggr_up(handle, linkid, &status);
770d62bc4baSyz147064 		return (status);
771d62bc4baSyz147064 	}
772f595a68aSyz147064 }
773f595a68aSyz147064 
774f595a68aSyz147064 /*
775f595a68aSyz147064  * Given a policy string, return a policy mask. Returns B_TRUE on
776d62bc4baSyz147064  * success, or B_FALSE if an error occurred during parsing.
777f595a68aSyz147064  */
778f595a68aSyz147064 boolean_t
779f595a68aSyz147064 dladm_aggr_str2policy(const char *str, uint32_t *policy)
780f595a68aSyz147064 {
781f595a68aSyz147064 	int i;
782f595a68aSyz147064 	policy_t *pol;
783f595a68aSyz147064 	char *token = NULL;
784f595a68aSyz147064 	char *lasts;
785f595a68aSyz147064 
786f595a68aSyz147064 	*policy = 0;
787f595a68aSyz147064 
788f595a68aSyz147064 	while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
789f595a68aSyz147064 	    &lasts)) != NULL) {
790f595a68aSyz147064 		for (i = 0; i < NPOLICIES; i++) {
791f595a68aSyz147064 			pol = &policies[i];
792f595a68aSyz147064 			if (strcasecmp(token, pol->pol_name) == 0) {
793f595a68aSyz147064 				*policy |= pol->policy;
794f595a68aSyz147064 				break;
795f595a68aSyz147064 			}
796f595a68aSyz147064 		}
797f595a68aSyz147064 		if (i == NPOLICIES)
798f595a68aSyz147064 			return (B_FALSE);
799f595a68aSyz147064 	}
800f595a68aSyz147064 
801f595a68aSyz147064 	return (B_TRUE);
802f595a68aSyz147064 }
803f595a68aSyz147064 
804f595a68aSyz147064 /*
805f595a68aSyz147064  * Given a policy mask, returns a printable string, or NULL if the
806f595a68aSyz147064  * policy mask is invalid. It is the responsibility of the caller to
807f595a68aSyz147064  * free the returned string after use.
808f595a68aSyz147064  */
809f595a68aSyz147064 char *
810f595a68aSyz147064 dladm_aggr_policy2str(uint32_t policy, char *str)
811f595a68aSyz147064 {
812f595a68aSyz147064 	int i, npolicies = 0;
813f595a68aSyz147064 	policy_t *pol;
814f595a68aSyz147064 
815d62bc4baSyz147064 	if (str == NULL)
816d62bc4baSyz147064 		return (NULL);
817d62bc4baSyz147064 
818f595a68aSyz147064 	str[0] = '\0';
819f595a68aSyz147064 
820f595a68aSyz147064 	for (i = 0; i < NPOLICIES; i++) {
821f595a68aSyz147064 		pol = &policies[i];
822f595a68aSyz147064 		if ((policy & pol->policy) != 0) {
823f595a68aSyz147064 			npolicies++;
824f595a68aSyz147064 			if (npolicies > 1)
825d62bc4baSyz147064 				(void) strlcat(str, ",", DLADM_STRSIZE);
826d62bc4baSyz147064 			(void) strlcat(str, pol->pol_name, DLADM_STRSIZE);
827f595a68aSyz147064 		}
828f595a68aSyz147064 	}
829f595a68aSyz147064 
830f595a68aSyz147064 	return (str);
831f595a68aSyz147064 }
832f595a68aSyz147064 
833f595a68aSyz147064 /*
834f595a68aSyz147064  * Given a MAC address string, return the MAC address in the mac_addr
835f595a68aSyz147064  * array. If the MAC address was not explicitly specified, i.e. is
836f595a68aSyz147064  * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
837f595a68aSyz147064  * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
838f595a68aSyz147064  */
839f595a68aSyz147064 boolean_t
840f595a68aSyz147064 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
841f595a68aSyz147064 {
842f595a68aSyz147064 	uchar_t *conv_str;
843f595a68aSyz147064 	int mac_len;
844f595a68aSyz147064 
845f595a68aSyz147064 	*mac_fixed = (strcmp(str, "auto") != 0);
846f595a68aSyz147064 	if (!*mac_fixed) {
847f595a68aSyz147064 		bzero(mac_addr, ETHERADDRL);
848f595a68aSyz147064 		return (B_TRUE);
849f595a68aSyz147064 	}
850f595a68aSyz147064 
851f595a68aSyz147064 	conv_str = _link_aton(str, &mac_len);
852f595a68aSyz147064 	if (conv_str == NULL)
853f595a68aSyz147064 		return (B_FALSE);
854f595a68aSyz147064 
855f595a68aSyz147064 	if (mac_len != ETHERADDRL) {
856f595a68aSyz147064 		free(conv_str);
857f595a68aSyz147064 		return (B_FALSE);
858f595a68aSyz147064 	}
859f595a68aSyz147064 
860f595a68aSyz147064 	if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
861f595a68aSyz147064 	    (conv_str[0] & 0x01)) {
862f595a68aSyz147064 		free(conv_str);
863f595a68aSyz147064 		return (B_FALSE);
864f595a68aSyz147064 	}
865f595a68aSyz147064 
866f595a68aSyz147064 	bcopy(conv_str, mac_addr, ETHERADDRL);
867f595a68aSyz147064 	free(conv_str);
868f595a68aSyz147064 
869f595a68aSyz147064 	return (B_TRUE);
870f595a68aSyz147064 }
871f595a68aSyz147064 
872f595a68aSyz147064 /*
873f595a68aSyz147064  * Returns a string containing a printable representation of a MAC address.
874f595a68aSyz147064  */
875f595a68aSyz147064 const char *
876d62bc4baSyz147064 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf)
877f595a68aSyz147064 {
878f595a68aSyz147064 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
879f595a68aSyz147064 
880f595a68aSyz147064 	if (buf == NULL)
881f595a68aSyz147064 		return (NULL);
882f595a68aSyz147064 
883f595a68aSyz147064 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
884d62bc4baSyz147064 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
885f595a68aSyz147064 	else
886f595a68aSyz147064 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
887d62bc4baSyz147064 
888d62bc4baSyz147064 	return (buf);
889f595a68aSyz147064 }
890f595a68aSyz147064 
891f595a68aSyz147064 /*
892f595a68aSyz147064  * Given a LACP mode string, find the corresponding LACP mode number. Returns
893f595a68aSyz147064  * B_TRUE if a match was found, B_FALSE otherwise.
894f595a68aSyz147064  */
895f595a68aSyz147064 boolean_t
896f595a68aSyz147064 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
897f595a68aSyz147064 {
898f595a68aSyz147064 	int i;
899f595a68aSyz147064 	dladm_aggr_lacpmode_t *mode;
900f595a68aSyz147064 
901f595a68aSyz147064 	for (i = 0; i < NLACP_MODES; i++) {
902f595a68aSyz147064 		mode = &lacp_modes[i];
903f595a68aSyz147064 		if (strncasecmp(str, mode->mode_str,
904f595a68aSyz147064 		    strlen(mode->mode_str)) == 0) {
905f595a68aSyz147064 			*lacp_mode = mode->mode_id;
906f595a68aSyz147064 			return (B_TRUE);
907f595a68aSyz147064 		}
908f595a68aSyz147064 	}
909f595a68aSyz147064 
910f595a68aSyz147064 	return (B_FALSE);
911f595a68aSyz147064 }
912f595a68aSyz147064 
913f595a68aSyz147064 /*
914f595a68aSyz147064  * Given a LACP mode number, returns a printable string, or NULL if the
915f595a68aSyz147064  * LACP mode number is invalid.
916f595a68aSyz147064  */
917f595a68aSyz147064 const char *
918f595a68aSyz147064 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
919f595a68aSyz147064 {
920f595a68aSyz147064 	int i;
921f595a68aSyz147064 	dladm_aggr_lacpmode_t *mode;
922f595a68aSyz147064 
923d62bc4baSyz147064 	if (buf == NULL)
924d62bc4baSyz147064 		return (NULL);
925d62bc4baSyz147064 
926f595a68aSyz147064 	for (i = 0; i < NLACP_MODES; i++) {
927f595a68aSyz147064 		mode = &lacp_modes[i];
928f595a68aSyz147064 		if (mode->mode_id == mode_id) {
929f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
930f595a68aSyz147064 			    mode->mode_str);
931f595a68aSyz147064 			return (buf);
932f595a68aSyz147064 		}
933f595a68aSyz147064 	}
934f595a68aSyz147064 
935f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
936f595a68aSyz147064 	return (buf);
937f595a68aSyz147064 }
938f595a68aSyz147064 
939f595a68aSyz147064 /*
940f595a68aSyz147064  * Given a LACP timer string, find the corresponding LACP timer number. Returns
941f595a68aSyz147064  * B_TRUE if a match was found, B_FALSE otherwise.
942f595a68aSyz147064  */
943f595a68aSyz147064 boolean_t
944f595a68aSyz147064 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
945f595a68aSyz147064 {
946f595a68aSyz147064 	int i;
947f595a68aSyz147064 	dladm_aggr_lacptimer_t *timer;
948f595a68aSyz147064 
949f595a68aSyz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
950f595a68aSyz147064 		timer = &lacp_timers[i];
951f595a68aSyz147064 		if (strncasecmp(str, timer->lt_str,
952f595a68aSyz147064 		    strlen(timer->lt_str)) == 0) {
953f595a68aSyz147064 			*lacp_timer = timer->lt_id;
954f595a68aSyz147064 			return (B_TRUE);
955f595a68aSyz147064 		}
956f595a68aSyz147064 	}
957f595a68aSyz147064 
958f595a68aSyz147064 	return (B_FALSE);
959f595a68aSyz147064 }
960f595a68aSyz147064 
961f595a68aSyz147064 /*
962f595a68aSyz147064  * Given a LACP timer, returns a printable string, or NULL if the
963f595a68aSyz147064  * LACP timer number is invalid.
964f595a68aSyz147064  */
965f595a68aSyz147064 const char *
966f595a68aSyz147064 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
967f595a68aSyz147064 {
968f595a68aSyz147064 	int i;
969f595a68aSyz147064 	dladm_aggr_lacptimer_t *timer;
970f595a68aSyz147064 
971d62bc4baSyz147064 	if (buf == NULL)
972d62bc4baSyz147064 		return (NULL);
973d62bc4baSyz147064 
974f595a68aSyz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
975f595a68aSyz147064 		timer = &lacp_timers[i];
976f595a68aSyz147064 		if (timer->lt_id == timer_id) {
977f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
978f595a68aSyz147064 			    timer->lt_str);
979f595a68aSyz147064 			return (buf);
980f595a68aSyz147064 		}
981f595a68aSyz147064 	}
982f595a68aSyz147064 
983f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
984f595a68aSyz147064 	return (buf);
985f595a68aSyz147064 }
986f595a68aSyz147064 
987f595a68aSyz147064 const char *
988f595a68aSyz147064 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
989f595a68aSyz147064 {
990f595a68aSyz147064 	int			i;
991f595a68aSyz147064 	dladm_aggr_port_state_t *state;
992f595a68aSyz147064 
993d62bc4baSyz147064 	if (buf == NULL)
994d62bc4baSyz147064 		return (NULL);
995d62bc4baSyz147064 
996f595a68aSyz147064 	for (i = 0; i < NPORT_STATES; i++) {
997f595a68aSyz147064 		state = &port_states[i];
998f595a68aSyz147064 		if (state->state_id == state_id) {
999f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
1000f595a68aSyz147064 			    state->state_str);
1001f595a68aSyz147064 			return (buf);
1002f595a68aSyz147064 		}
1003f595a68aSyz147064 	}
1004f595a68aSyz147064 
1005f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
1006f595a68aSyz147064 	return (buf);
1007f595a68aSyz147064 }
1008f595a68aSyz147064 
1009f595a68aSyz147064 static dladm_status_t
10104ac67f02SAnurag S. Maskey dladm_aggr_persist_aggr_conf(dladm_handle_t handle, const char *link,
10114ac67f02SAnurag S. Maskey     datalink_id_t linkid, uint16_t key, uint32_t nports,
10124ac67f02SAnurag S. Maskey     dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed,
10134ac67f02SAnurag S. Maskey     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
10144ac67f02SAnurag S. Maskey     aggr_lacp_timer_t lacp_timer, boolean_t force)
1015f595a68aSyz147064 {
1016d62bc4baSyz147064 	dladm_conf_t conf = DLADM_INVALID_CONF;
1017d62bc4baSyz147064 	char *portstr = NULL;
1018d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1019f595a68aSyz147064 	dladm_status_t status;
1020d62bc4baSyz147064 	int i, size;
1021d62bc4baSyz147064 	uint64_t u64;
1022f595a68aSyz147064 
10234ac67f02SAnurag S. Maskey 	if ((status = dladm_create_conf(handle, link, linkid,
10244ac67f02SAnurag S. Maskey 	    DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) {
1025f595a68aSyz147064 		return (status);
1026f595a68aSyz147064 	}
1027f595a68aSyz147064 
1028d62bc4baSyz147064 	u64 = key;
10294ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64,
10304ac67f02SAnurag S. Maskey 	    &u64);
1031d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1032d62bc4baSyz147064 		goto done;
1033f595a68aSyz147064 
1034d62bc4baSyz147064 	u64 = nports;
10354ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64,
10364ac67f02SAnurag S. Maskey 	    &u64);
1037d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1038d62bc4baSyz147064 		goto done;
1039f595a68aSyz147064 
10402b24ab6bSSebastien Roy 	size = nports * MAXLINKNAMELEN + 1;
1041d62bc4baSyz147064 	if ((portstr = calloc(1, size)) == NULL) {
1042d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
1043f595a68aSyz147064 		goto done;
1044f595a68aSyz147064 	}
1045f595a68aSyz147064 
10462b24ab6bSSebastien Roy 	for (i = 0; i < nports; i++) {
10472b24ab6bSSebastien Roy 		status = write_port(handle, portstr, ports[i].lp_linkid, size);
10482b24ab6bSSebastien Roy 		if (status != DLADM_STATUS_OK) {
10492b24ab6bSSebastien Roy 			free(portstr);
10502b24ab6bSSebastien Roy 			goto done;
10512b24ab6bSSebastien Roy 		}
10522b24ab6bSSebastien Roy 	}
10534ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR,
10544ac67f02SAnurag S. Maskey 	    portstr);
1055d62bc4baSyz147064 	free(portstr);
1056d62bc4baSyz147064 
1057d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1058d62bc4baSyz147064 		goto done;
1059d62bc4baSyz147064 
1060d62bc4baSyz147064 	u64 = policy;
10614ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64,
10624ac67f02SAnurag S. Maskey 	    &u64);
1063d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1064d62bc4baSyz147064 		goto done;
1065d62bc4baSyz147064 
10664ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FFIXMACADDR,
10674ac67f02SAnurag S. Maskey 	    DLADM_TYPE_BOOLEAN, &mac_addr_fixed);
1068d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1069d62bc4baSyz147064 		goto done;
1070d62bc4baSyz147064 
1071d62bc4baSyz147064 	if (mac_addr_fixed) {
1072d62bc4baSyz147064 		if (!VALID_PORT_MAC(mac_addr)) {
1073d62bc4baSyz147064 			status = DLADM_STATUS_MACADDRINVAL;
1074f595a68aSyz147064 			goto done;
1075f595a68aSyz147064 		}
1076d62bc4baSyz147064 
1077d62bc4baSyz147064 		(void) dladm_aggr_macaddr2str(mac_addr, macstr);
10784ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FMACADDR,
10794ac67f02SAnurag S. Maskey 		    DLADM_TYPE_STR, macstr);
1080d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1081d62bc4baSyz147064 			goto done;
1082f595a68aSyz147064 	}
1083f595a68aSyz147064 
10844ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN,
10854ac67f02SAnurag S. Maskey 	    &force);
1086d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1087d62bc4baSyz147064 		goto done;
1088d62bc4baSyz147064 
1089d62bc4baSyz147064 	u64 = lacp_mode;
10904ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FLACPMODE,
10914ac67f02SAnurag S. Maskey 	    DLADM_TYPE_UINT64, &u64);
1092d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1093d62bc4baSyz147064 		goto done;
1094d62bc4baSyz147064 
1095d62bc4baSyz147064 	u64 = lacp_timer;
10964ac67f02SAnurag S. Maskey 	status = dladm_set_conf_field(handle, conf, FLACPTIMER,
10974ac67f02SAnurag S. Maskey 	    DLADM_TYPE_UINT64, &u64);
1098d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1099d62bc4baSyz147064 		goto done;
1100d62bc4baSyz147064 
1101f595a68aSyz147064 	/*
1102d62bc4baSyz147064 	 * Commit the link aggregation configuration.
1103f595a68aSyz147064 	 */
11044ac67f02SAnurag S. Maskey 	status = dladm_write_conf(handle, conf);
1105f595a68aSyz147064 
1106f595a68aSyz147064 done:
11074ac67f02SAnurag S. Maskey 	dladm_destroy_conf(handle, conf);
1108f595a68aSyz147064 	return (status);
1109f595a68aSyz147064 }
1110f595a68aSyz147064 
1111f595a68aSyz147064 /*
1112f595a68aSyz147064  * Create a new link aggregation group. Update the configuration
1113f595a68aSyz147064  * file and bring it up.
1114f595a68aSyz147064  */
1115f595a68aSyz147064 dladm_status_t
11164ac67f02SAnurag S. Maskey dladm_aggr_create(dladm_handle_t handle, const char *name, uint16_t key,
11174ac67f02SAnurag S. Maskey     uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t policy,
11184ac67f02SAnurag S. Maskey     boolean_t mac_addr_fixed, const uchar_t *mac_addr,
11194ac67f02SAnurag S. Maskey     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, uint32_t flags)
1120f595a68aSyz147064 {
1121d62bc4baSyz147064 	datalink_id_t linkid = DATALINK_INVALID_LINKID;
1122d62bc4baSyz147064 	uint32_t media;
1123d62bc4baSyz147064 	uint32_t i;
1124d62bc4baSyz147064 	datalink_class_t class;
1125f595a68aSyz147064 	dladm_status_t status;
1126d62bc4baSyz147064 	boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE;
1127f595a68aSyz147064 
1128d62bc4baSyz147064 	if (key != 0 && key > AGGR_MAX_KEY)
1129f595a68aSyz147064 		return (DLADM_STATUS_KEYINVAL);
1130f595a68aSyz147064 
1131d62bc4baSyz147064 	if (nports == 0)
1132d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
1133f595a68aSyz147064 
1134d62bc4baSyz147064 	for (i = 0; i < nports; i++) {
11354ac67f02SAnurag S. Maskey 		if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL,
1136d62bc4baSyz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
1137b509e89bSRishi Srivatsavai 		    !((class == DATALINK_CLASS_PHYS || class ==
1138b509e89bSRishi Srivatsavai 		    DATALINK_CLASS_SIMNET) && (media == DL_ETHER))) {
1139d62bc4baSyz147064 			return (DLADM_STATUS_BADARG);
1140d62bc4baSyz147064 		}
1141d62bc4baSyz147064 	}
1142f595a68aSyz147064 
1143d62bc4baSyz147064 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
11444ac67f02SAnurag S. Maskey 	if ((status = dladm_create_datalink_id(handle, name,
11454ac67f02SAnurag S. Maskey 	    DATALINK_CLASS_AGGR, DL_ETHER, flags, &linkid)) !=
11464ac67f02SAnurag S. Maskey 	    DLADM_STATUS_OK) {
1147d62bc4baSyz147064 		goto fail;
1148d62bc4baSyz147064 	}
1149f595a68aSyz147064 
1150d62bc4baSyz147064 	if ((flags & DLADM_OPT_PERSIST) &&
11514ac67f02SAnurag S. Maskey 	    (status = dladm_aggr_persist_aggr_conf(handle, name, linkid, key,
11524ac67f02SAnurag S. Maskey 	    nports, ports, policy, mac_addr_fixed, mac_addr, lacp_mode,
11534ac67f02SAnurag S. Maskey 	    lacp_timer, force)) != DLADM_STATUS_OK) {
1154d62bc4baSyz147064 		goto fail;
1155d62bc4baSyz147064 	}
1156d62bc4baSyz147064 
1157d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1158d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1159d62bc4baSyz147064 
11604ac67f02SAnurag S. Maskey 	status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports,
11614ac67f02SAnurag S. Maskey 	    policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force);
1162d62bc4baSyz147064 
1163d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
1164d62bc4baSyz147064 		if (flags & DLADM_OPT_PERSIST)
11654ac67f02SAnurag S. Maskey 			(void) dladm_remove_conf(handle, linkid);
1166d62bc4baSyz147064 		goto fail;
1167d62bc4baSyz147064 	}
1168d62bc4baSyz147064 
1169d62bc4baSyz147064 	return (DLADM_STATUS_OK);
1170d62bc4baSyz147064 
1171d62bc4baSyz147064 fail:
1172d62bc4baSyz147064 	if (linkid != DATALINK_INVALID_LINKID)
11734ac67f02SAnurag S. Maskey 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
1174d62bc4baSyz147064 
1175f595a68aSyz147064 	return (status);
1176f595a68aSyz147064 }
1177f595a68aSyz147064 
1178d62bc4baSyz147064 static dladm_status_t
11794ac67f02SAnurag S. Maskey i_dladm_aggr_get_aggr_attr(dladm_handle_t handle, dladm_conf_t conf,
11804ac67f02SAnurag S. Maskey     uint32_t mask, dladm_aggr_modify_attr_t *attrp)
1181d62bc4baSyz147064 {
1182d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
1183d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1184d62bc4baSyz147064 	uint64_t u64;
1185f595a68aSyz147064 
1186d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
11874ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
1188d62bc4baSyz147064 		    sizeof (u64));
1189d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1190d62bc4baSyz147064 			return (status);
1191d62bc4baSyz147064 		attrp->ld_policy = (uint32_t)u64;
1192d62bc4baSyz147064 	}
1193d62bc4baSyz147064 
1194d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
11954ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FFIXMACADDR,
1196d62bc4baSyz147064 		    &attrp->ld_mac_fixed, sizeof (boolean_t));
1197d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1198d62bc4baSyz147064 			return (status);
1199d62bc4baSyz147064 
1200d62bc4baSyz147064 		if (attrp->ld_mac_fixed) {
1201d62bc4baSyz147064 			boolean_t fixed;
1202d62bc4baSyz147064 
12034ac67f02SAnurag S. Maskey 			status = dladm_get_conf_field(handle, conf, FMACADDR,
1204d62bc4baSyz147064 			    macstr, sizeof (macstr));
1205d62bc4baSyz147064 			if (status != DLADM_STATUS_OK)
1206d62bc4baSyz147064 				return (status);
1207d62bc4baSyz147064 
1208d62bc4baSyz147064 			if (!dladm_aggr_str2macaddr(macstr, &fixed,
1209d62bc4baSyz147064 			    attrp->ld_mac)) {
1210d62bc4baSyz147064 				return (DLADM_STATUS_REPOSITORYINVAL);
1211d62bc4baSyz147064 			}
1212d62bc4baSyz147064 		}
1213d62bc4baSyz147064 	}
1214d62bc4baSyz147064 
1215d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
12164ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
1217d62bc4baSyz147064 		    sizeof (u64));
1218d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1219d62bc4baSyz147064 			return (status);
1220d62bc4baSyz147064 		attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64;
1221d62bc4baSyz147064 	}
1222d62bc4baSyz147064 
1223d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
12244ac67f02SAnurag S. Maskey 		status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
1225d62bc4baSyz147064 		    sizeof (u64));
1226d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1227d62bc4baSyz147064 			return (status);
1228d62bc4baSyz147064 		attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64;
1229d62bc4baSyz147064 	}
1230d62bc4baSyz147064 
1231d62bc4baSyz147064 	return (status);
1232d62bc4baSyz147064 }
1233d62bc4baSyz147064 
1234d62bc4baSyz147064 static dladm_status_t
12354ac67f02SAnurag S. Maskey i_dladm_aggr_set_aggr_attr(dladm_handle_t handle, dladm_conf_t conf,
12364ac67f02SAnurag S. Maskey     uint32_t mask, dladm_aggr_modify_attr_t *attrp)
1237d62bc4baSyz147064 {
1238d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
1239d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1240d62bc4baSyz147064 	uint64_t u64;
1241d62bc4baSyz147064 
1242d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
1243d62bc4baSyz147064 		u64 = attrp->ld_policy;
12444ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FPOLICY,
12454ac67f02SAnurag S. Maskey 		    DLADM_TYPE_UINT64, &u64);
1246d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1247d62bc4baSyz147064 			return (status);
1248d62bc4baSyz147064 	}
1249d62bc4baSyz147064 
1250d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
12514ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FFIXMACADDR,
1252d62bc4baSyz147064 		    DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed);
1253d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1254d62bc4baSyz147064 			return (status);
1255d62bc4baSyz147064 
1256d62bc4baSyz147064 		if (attrp->ld_mac_fixed) {
1257d62bc4baSyz147064 			(void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr);
12584ac67f02SAnurag S. Maskey 			status = dladm_set_conf_field(handle, conf, FMACADDR,
1259d62bc4baSyz147064 			    DLADM_TYPE_STR, macstr);
1260d62bc4baSyz147064 			if (status != DLADM_STATUS_OK)
1261d62bc4baSyz147064 				return (status);
1262d62bc4baSyz147064 		}
1263d62bc4baSyz147064 	}
1264d62bc4baSyz147064 
1265d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1266d62bc4baSyz147064 		u64 = attrp->ld_lacp_mode;
12674ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FLACPMODE,
1268d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64);
1269d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1270d62bc4baSyz147064 			return (status);
1271d62bc4baSyz147064 	}
1272d62bc4baSyz147064 
1273d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1274d62bc4baSyz147064 		u64 = attrp->ld_lacp_timer;
12754ac67f02SAnurag S. Maskey 		status = dladm_set_conf_field(handle, conf, FLACPTIMER,
1276d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64);
1277d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1278d62bc4baSyz147064 			return (status);
1279d62bc4baSyz147064 	}
1280f595a68aSyz147064 
1281f595a68aSyz147064 	return (status);
1282f595a68aSyz147064 }
1283f595a68aSyz147064 
1284f595a68aSyz147064 /*
1285f595a68aSyz147064  * Modify the parameters of an existing link aggregation group. Update
1286f595a68aSyz147064  * the configuration file and pass the changes to the kernel.
1287f595a68aSyz147064  */
1288f595a68aSyz147064 dladm_status_t
12894ac67f02SAnurag S. Maskey dladm_aggr_modify(dladm_handle_t handle, datalink_id_t linkid,
12904ac67f02SAnurag S. Maskey     uint32_t modify_mask, uint32_t policy, boolean_t mac_fixed,
12914ac67f02SAnurag S. Maskey     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1292d62bc4baSyz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
1293f595a68aSyz147064 {
1294f595a68aSyz147064 	dladm_aggr_modify_attr_t new_attr, old_attr;
1295d62bc4baSyz147064 	dladm_conf_t conf;
1296f595a68aSyz147064 	dladm_status_t status;
1297f595a68aSyz147064 
1298f595a68aSyz147064 	new_attr.ld_policy = policy;
1299f595a68aSyz147064 	new_attr.ld_mac_fixed = mac_fixed;
1300f595a68aSyz147064 	new_attr.ld_lacp_mode = lacp_mode;
1301f595a68aSyz147064 	new_attr.ld_lacp_timer = lacp_timer;
1302d62bc4baSyz147064 	bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL);
1303f595a68aSyz147064 
1304d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
13054ac67f02SAnurag S. Maskey 		status = dladm_read_conf(handle, linkid, &conf);
1306d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1307d62bc4baSyz147064 			return (status);
1308d62bc4baSyz147064 
13094ac67f02SAnurag S. Maskey 		if ((status = i_dladm_aggr_get_aggr_attr(handle, conf,
13104ac67f02SAnurag S. Maskey 		    modify_mask, &old_attr)) != DLADM_STATUS_OK) {
1311d62bc4baSyz147064 			goto done;
1312d62bc4baSyz147064 		}
1313d62bc4baSyz147064 
13144ac67f02SAnurag S. Maskey 		if ((status = i_dladm_aggr_set_aggr_attr(handle, conf,
13154ac67f02SAnurag S. Maskey 		    modify_mask, &new_attr)) != DLADM_STATUS_OK) {
1316d62bc4baSyz147064 			goto done;
1317d62bc4baSyz147064 		}
1318d62bc4baSyz147064 
13194ac67f02SAnurag S. Maskey 		status = dladm_write_conf(handle, conf);
1320d62bc4baSyz147064 
1321d62bc4baSyz147064 done:
13224ac67f02SAnurag S. Maskey 		dladm_destroy_conf(handle, conf);
1323d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1324f595a68aSyz147064 			return (status);
1325f595a68aSyz147064 	}
1326f595a68aSyz147064 
1327d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1328d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1329d62bc4baSyz147064 
13304ac67f02SAnurag S. Maskey 	status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask,
13314ac67f02SAnurag S. Maskey 	    &new_attr);
1332d62bc4baSyz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
13334ac67f02SAnurag S. Maskey 		if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) {
13344ac67f02SAnurag S. Maskey 			if (i_dladm_aggr_set_aggr_attr(handle, conf,
13354ac67f02SAnurag S. Maskey 			    modify_mask, &old_attr) == DLADM_STATUS_OK) {
13364ac67f02SAnurag S. Maskey 				(void) dladm_write_conf(handle, conf);
1337d62bc4baSyz147064 			}
13384ac67f02SAnurag S. Maskey 			dladm_destroy_conf(handle, conf);
1339d62bc4baSyz147064 		}
1340f595a68aSyz147064 	}
1341f595a68aSyz147064 
1342f595a68aSyz147064 	return (status);
1343f595a68aSyz147064 }
1344f595a68aSyz147064 
1345d62bc4baSyz147064 typedef struct aggr_held_arg_s {
1346d62bc4baSyz147064 	datalink_id_t	aggrid;
1347d62bc4baSyz147064 	boolean_t	isheld;
1348d62bc4baSyz147064 } aggr_held_arg_t;
1349d62bc4baSyz147064 
1350d62bc4baSyz147064 static int
13514ac67f02SAnurag S. Maskey i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1352d62bc4baSyz147064 {
1353d62bc4baSyz147064 	aggr_held_arg_t		*aggr_held_arg = arg;
1354d62bc4baSyz147064 	dladm_vlan_attr_t	dva;
1355d62bc4baSyz147064 
13564ac67f02SAnurag S. Maskey 	if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) !=
13574ac67f02SAnurag S. Maskey 	    DLADM_STATUS_OK)
1358d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
1359d62bc4baSyz147064 
1360d62bc4baSyz147064 	if (dva.dv_linkid == aggr_held_arg->aggrid) {
1361d62bc4baSyz147064 		/*
1362d62bc4baSyz147064 		 * This VLAN is created over the given aggregation.
1363d62bc4baSyz147064 		 */
1364d62bc4baSyz147064 		aggr_held_arg->isheld = B_TRUE;
1365d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
1366d62bc4baSyz147064 	}
1367d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
1368d62bc4baSyz147064 }
1369d62bc4baSyz147064 
1370f595a68aSyz147064 /*
1371d62bc4baSyz147064  * Delete a previously created link aggregation group. Either the name "aggr"
1372d62bc4baSyz147064  * or the "key" is specified.
1373f595a68aSyz147064  */
1374f595a68aSyz147064 dladm_status_t
13754ac67f02SAnurag S. Maskey dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags)
1376f595a68aSyz147064 {
1377d62bc4baSyz147064 	laioc_delete_t ioc;
1378d62bc4baSyz147064 	datalink_class_t class;
1379f595a68aSyz147064 	dladm_status_t status;
1380f595a68aSyz147064 
13814ac67f02SAnurag S. Maskey 	if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL,
13824ac67f02SAnurag S. Maskey 	    0) != DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) {
1383d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
1384d62bc4baSyz147064 	}
1385f595a68aSyz147064 
1386d62bc4baSyz147064 	if (flags & DLADM_OPT_ACTIVE) {
1387d62bc4baSyz147064 		ioc.ld_linkid = linkid;
13884ac67f02SAnurag S. Maskey 		if ((i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc) < 0) &&
1389d62bc4baSyz147064 		    ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
1390d62bc4baSyz147064 			status = dladm_errno2status(errno);
1391f595a68aSyz147064 			return (status);
1392f595a68aSyz147064 		}
1393d62bc4baSyz147064 
1394d62bc4baSyz147064 		/*
1395d62bc4baSyz147064 		 * Delete ACTIVE linkprop first.
1396d62bc4baSyz147064 		 */
13974ac67f02SAnurag S. Maskey 		(void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
1398d62bc4baSyz147064 		    DLADM_OPT_ACTIVE);
13994ac67f02SAnurag S. Maskey 		(void) dladm_destroy_datalink_id(handle, linkid,
14004ac67f02SAnurag S. Maskey 		    DLADM_OPT_ACTIVE);
1401f595a68aSyz147064 	}
1402f595a68aSyz147064 
1403d62bc4baSyz147064 	/*
1404d62bc4baSyz147064 	 * If we reach here, it means that the active aggregation has already
1405d62bc4baSyz147064 	 * been deleted, and there is no active VLANs holding this aggregation.
1406d62bc4baSyz147064 	 * Now we see whether there is any persistent VLANs holding this
1407d62bc4baSyz147064 	 * aggregation. If so, we fail the operation.
1408d62bc4baSyz147064 	 */
1409d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
1410d62bc4baSyz147064 		aggr_held_arg_t arg;
1411f595a68aSyz147064 
1412d62bc4baSyz147064 		arg.aggrid = linkid;
1413d62bc4baSyz147064 		arg.isheld = B_FALSE;
1414d62bc4baSyz147064 
14154ac67f02SAnurag S. Maskey 		(void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle,
1416d62bc4baSyz147064 		    &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
1417d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
1418d62bc4baSyz147064 		if (arg.isheld)
1419d62bc4baSyz147064 			return (DLADM_STATUS_LINKBUSY);
1420d62bc4baSyz147064 
14212b24ab6bSSebastien Roy 		(void) dladm_remove_conf(handle, linkid);
14224ac67f02SAnurag S. Maskey 		(void) dladm_destroy_datalink_id(handle, linkid,
14234ac67f02SAnurag S. Maskey 		    DLADM_OPT_PERSIST);
1424d62bc4baSyz147064 	}
1425d62bc4baSyz147064 
1426d62bc4baSyz147064 	return (DLADM_STATUS_OK);
1427f595a68aSyz147064 }
1428f595a68aSyz147064 
1429f595a68aSyz147064 /*
1430f595a68aSyz147064  * Add one or more ports to an existing link aggregation.
1431f595a68aSyz147064  */
1432f595a68aSyz147064 dladm_status_t
14334ac67f02SAnurag S. Maskey dladm_aggr_add(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports,
1434d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1435f595a68aSyz147064 {
14364ac67f02SAnurag S. Maskey 	return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
14374ac67f02SAnurag S. Maskey 	    LAIOC_ADD));
1438f595a68aSyz147064 }
1439f595a68aSyz147064 
1440f595a68aSyz147064 /*
1441f595a68aSyz147064  * Remove one or more ports from an existing link aggregation.
1442f595a68aSyz147064  */
1443f595a68aSyz147064 dladm_status_t
14444ac67f02SAnurag S. Maskey dladm_aggr_remove(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports,
1445d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1446f595a68aSyz147064 {
14474ac67f02SAnurag S. Maskey 	return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
1448d62bc4baSyz147064 	    LAIOC_REMOVE));
1449f595a68aSyz147064 }
1450f595a68aSyz147064 
1451d62bc4baSyz147064 typedef struct i_walk_key_state_s {
1452d62bc4baSyz147064 	uint16_t key;
1453d62bc4baSyz147064 	datalink_id_t linkid;
1454d62bc4baSyz147064 	boolean_t found;
1455d62bc4baSyz147064 } i_walk_key_state_t;
1456f595a68aSyz147064 
1457d62bc4baSyz147064 static int
14584ac67f02SAnurag S. Maskey i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1459d62bc4baSyz147064 {
1460d62bc4baSyz147064 	dladm_conf_t conf;
1461d62bc4baSyz147064 	uint16_t key;
1462d62bc4baSyz147064 	dladm_status_t status;
1463d62bc4baSyz147064 	i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
1464d62bc4baSyz147064 	uint64_t u64;
1465d62bc4baSyz147064 
14664ac67f02SAnurag S. Maskey 	if (dladm_read_conf(handle, linkid, &conf) != 0)
1467d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
1468d62bc4baSyz147064 
14694ac67f02SAnurag S. Maskey 	status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
1470d62bc4baSyz147064 	key = (uint16_t)u64;
14714ac67f02SAnurag S. Maskey 	dladm_destroy_conf(handle, conf);
1472d62bc4baSyz147064 
1473d62bc4baSyz147064 	if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
1474d62bc4baSyz147064 		statep->found = B_TRUE;
1475d62bc4baSyz147064 		statep->linkid = linkid;
1476d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
1477d62bc4baSyz147064 	}
1478d62bc4baSyz147064 
1479d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
1480d62bc4baSyz147064 }
1481d62bc4baSyz147064 
1482d62bc4baSyz147064 dladm_status_t
14834ac67f02SAnurag S. Maskey dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp,
14844ac67f02SAnurag S. Maskey     uint32_t flags)
1485d62bc4baSyz147064 {
1486d62bc4baSyz147064 	i_walk_key_state_t state;
1487d62bc4baSyz147064 
1488d62bc4baSyz147064 	if (key > AGGR_MAX_KEY)
1489d62bc4baSyz147064 		return (DLADM_STATUS_NOTFOUND);
1490d62bc4baSyz147064 
1491d62bc4baSyz147064 	state.found = B_FALSE;
1492d62bc4baSyz147064 	state.key = key;
1493d62bc4baSyz147064 
14944ac67f02SAnurag S. Maskey 	(void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, handle, &state,
1495d62bc4baSyz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
1496d62bc4baSyz147064 	if (state.found == B_TRUE) {
1497d62bc4baSyz147064 		*linkidp = state.linkid;
1498d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1499d62bc4baSyz147064 	} else {
1500d62bc4baSyz147064 		return (DLADM_STATUS_NOTFOUND);
1501d62bc4baSyz147064 	}
1502f595a68aSyz147064 }
1503