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