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