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