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 2007 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 <strings.h>
38 #include <libintl.h>
39 #include <net/if_types.h>
40 #include <net/if_dl.h>
41 #include <libdlaggr.h>
42 #include <libdladm_impl.h>
43 
44 /*
45  * Link Aggregation Administration Library.
46  *
47  * This library is used by administration tools such as dladm(1M) to
48  * configure link aggregations.
49  *
50  * Link aggregation configuration information is saved in a text
51  * file of the following format:
52  *
53  * <db-file>	::= <groups>*
54  * <group>	::= <key> <sep> <policy> <sep> <nports> <sep> <ports> <sep>
55  *		      <mac> <sep> <lacp-mode> <sep> <lacp-timer>
56  * <sep>	::= ' ' | '\t'
57  * <key>	::= <number>
58  * <nports>	::= <number>
59  * <ports>	::= <port> <m-port>*
60  * <m-port>	::= ',' <port>
61  * <port>	::= <devname>
62  * <devname>	::= <string>
63  * <port-num>	::= <number>
64  * <policy>	::= <pol-level> <m-pol>*
65  * <m-pol>	::= ',' <pol-level>
66  * <pol-level>	::= 'L2' | 'L3' | 'L4'
67  * <mac>	::= 'auto' | <mac-addr>
68  * <mac-addr>	::= <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex>
69  * <lacp-mode>	::= 'off' | 'active' | 'passive'
70  * <lacp-timer>	::= 'short' | 'long'
71  */
72 
73 #define	DLADM_AGGR_DEV		"/devices/pseudo/aggr@0:" AGGR_DEVNAME_CTL
74 #define	DLADM_AGGR_DB		"/etc/dladm/aggregation.conf"
75 #define	DLADM_AGGR_DB_TMP	"/etc/dladm/aggregation.conf.new"
76 #define	DLADM_AGGR_DB_LOCK	"/tmp/aggregation.conf.lock"
77 
78 #define	DLADM_AGGR_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
79 #define	DLADM_AGGR_DB_OWNER	15	/* "dladm" UID */
80 #define	DLADM_AGGR_DB_GROUP	3	/* "sys" GID */
81 
82 /*
83  * The largest configurable aggregation key.  Because by default the key is
84  * used as the DLPI device PPA and default VLAN PPA's are calculated as
85  * ((1000 * vid) + PPA), the largest key can't be > 999.
86  */
87 #define	DLADM_AGGR_MAX_KEY	999
88 
89 #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
90 
91 /* Limits on buffer size for LAIOC_INFO request */
92 #define	MIN_INFO_SIZE (4*1024)
93 #define	MAX_INFO_SIZE (128*1024)
94 
95 #define	MAXPATHLEN	1024
96 
97 static uchar_t	zero_mac[] = {0, 0, 0, 0, 0, 0};
98 
99 /* configuration database entry */
100 typedef struct dladm_aggr_grp_attr_db {
101 	uint32_t	lt_key;
102 	uint32_t	lt_policy;
103 	uint32_t	lt_nports;
104 	dladm_aggr_port_attr_db_t *lt_ports;
105 	boolean_t	lt_mac_fixed;
106 	uchar_t		lt_mac[ETHERADDRL];
107 	aggr_lacp_mode_t lt_lacp_mode;
108 	aggr_lacp_timer_t lt_lacp_timer;
109 } dladm_aggr_grp_attr_db_t;
110 
111 typedef struct dladm_aggr_up {
112 	uint32_t	lu_key;
113 	boolean_t	lu_found;
114 	int		lu_fd;
115 } dladm_aggr_up_t;
116 
117 typedef struct dladm_aggr_down {
118 	uint32_t	ld_key;
119 	boolean_t	ld_found;
120 } dladm_aggr_down_t;
121 
122 typedef struct dladm_aggr_modify_attr {
123 	uint32_t	ld_policy;
124 	boolean_t	ld_mac_fixed;
125 	uchar_t		ld_mac[ETHERADDRL];
126 	aggr_lacp_mode_t ld_lacp_mode;
127 	aggr_lacp_timer_t ld_lacp_timer;
128 } dladm_aggr_modify_attr_t;
129 
130 typedef struct policy_s {
131 	char		*pol_name;
132 	uint32_t	policy;
133 } policy_t;
134 
135 static policy_t policies[] = {
136 	{"L2",		AGGR_POLICY_L2},
137 	{"L3",		AGGR_POLICY_L3},
138 	{"L4",		AGGR_POLICY_L4}};
139 
140 #define	NPOLICIES	(sizeof (policies) / sizeof (policy_t))
141 
142 typedef struct dladm_aggr_lacpmode_s {
143 	char		*mode_str;
144 	aggr_lacp_mode_t mode_id;
145 } dladm_aggr_lacpmode_t;
146 
147 static dladm_aggr_lacpmode_t lacp_modes[] = {
148 	{"off", AGGR_LACP_OFF},
149 	{"active", AGGR_LACP_ACTIVE},
150 	{"passive", AGGR_LACP_PASSIVE}};
151 
152 #define	NLACP_MODES	(sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
153 
154 typedef struct dladm_aggr_lacptimer_s {
155 	char		*lt_str;
156 	aggr_lacp_timer_t lt_id;
157 } dladm_aggr_lacptimer_t;
158 
159 static dladm_aggr_lacptimer_t lacp_timers[] = {
160 	{"short", AGGR_LACP_TIMER_SHORT},
161 	{"long", AGGR_LACP_TIMER_LONG}};
162 
163 #define	NLACP_TIMERS	(sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
164 
165 typedef struct dladm_aggr_port_state {
166 	char			*state_str;
167 	aggr_port_state_t	state_id;
168 } dladm_aggr_port_state_t;
169 
170 static dladm_aggr_port_state_t port_states[] = {
171 	{"standby", AGGR_PORT_STATE_STANDBY },
172 	{"attached", AGGR_PORT_STATE_ATTACHED }
173 };
174 
175 #define	NPORT_STATES	\
176 	(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
177 
178 typedef struct delete_db_state {
179 	uint32_t	ds_key;
180 	boolean_t	ds_found;
181 } delete_db_state_t;
182 
183 typedef struct modify_db_state {
184 	uint32_t		us_key;
185 	uint32_t		us_mask;
186 	dladm_aggr_modify_attr_t *us_attr_new;
187 	dladm_aggr_modify_attr_t *us_attr_old;
188 	boolean_t		us_found;
189 } modify_db_state_t;
190 
191 typedef struct add_db_state {
192 	dladm_aggr_grp_attr_db_t *as_attr;
193 	boolean_t	as_found;
194 } add_db_state_t;
195 
196 static int i_dladm_aggr_fput_grp(FILE *, dladm_aggr_grp_attr_db_t *);
197 
198 /*
199  * Open and lock the aggregation configuration file lock. The lock is
200  * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
201  */
202 static int
203 i_dladm_aggr_lock_db(short type)
204 {
205 	int lock_fd;
206 	struct flock lock;
207 	int errno_save;
208 
209 	if ((lock_fd = open(DLADM_AGGR_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
210 	    DLADM_AGGR_DB_PERMS)) < 0)
211 		return (-1);
212 
213 	lock.l_type = type;
214 	lock.l_whence = SEEK_SET;
215 	lock.l_start = 0;
216 	lock.l_len = 0;
217 
218 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
219 		errno_save = errno;
220 		(void) close(lock_fd);
221 		(void) unlink(DLADM_AGGR_DB_LOCK);
222 		errno = errno_save;
223 		return (-1);
224 	}
225 	return (lock_fd);
226 }
227 
228 /*
229  * Unlock and close the specified file.
230  */
231 static void
232 i_dladm_aggr_unlock_db(int fd)
233 {
234 	struct flock lock;
235 
236 	if (fd < 0)
237 		return;
238 
239 	lock.l_type = F_UNLCK;
240 	lock.l_whence = SEEK_SET;
241 	lock.l_start = 0;
242 	lock.l_len = 0;
243 
244 	(void) fcntl(fd, F_SETLKW, &lock);
245 	(void) close(fd);
246 	(void) unlink(DLADM_AGGR_DB_LOCK);
247 }
248 
249 /*
250  * Walk through the groups defined on the system and for each group <grp>,
251  * invoke <fn>(<arg>, <grp>);
252  * Terminate the walk if at any time <fn> returns non-NULL value
253  */
254 int
255 dladm_aggr_walk(int (*fn)(void *, dladm_aggr_grp_attr_t *), void *arg)
256 {
257 	laioc_info_t *ioc;
258 	laioc_info_group_t *grp;
259 	laioc_info_port_t *port;
260 	dladm_aggr_grp_attr_t attr;
261 	int rc, i, j, bufsize, fd;
262 	char *where;
263 
264 	if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) == -1)
265 		return (-1);
266 
267 	bufsize = MIN_INFO_SIZE;
268 	ioc = (laioc_info_t *)calloc(1, bufsize);
269 	if (ioc == NULL) {
270 		(void) close(fd);
271 		errno = ENOMEM;
272 		return (-1);
273 	}
274 
275 tryagain:
276 	rc = i_dladm_ioctl(fd, LAIOC_INFO, ioc, bufsize);
277 
278 	if (rc != 0) {
279 		if (errno == ENOSPC) {
280 			/*
281 			 * The LAIOC_INFO call failed due to a short
282 			 * buffer. Reallocate the buffer and try again.
283 			 */
284 			bufsize *= 2;
285 			if (bufsize <= MAX_INFO_SIZE) {
286 				ioc = (laioc_info_t *)realloc(ioc, bufsize);
287 				if (ioc != NULL) {
288 					bzero(ioc, sizeof (bufsize));
289 					goto tryagain;
290 				}
291 			}
292 		}
293 		goto bail;
294 	}
295 
296 	/*
297 	 * Go through each group returned by the aggregation driver.
298 	 */
299 	where = (char *)(ioc + 1);
300 	for (i = 0; i < ioc->li_ngroups; i++) {
301 		/* LINTED E_BAD_PTR_CAST_ALIGN */
302 		grp = (laioc_info_group_t *)where;
303 
304 		attr.lg_key = grp->lg_key;
305 		attr.lg_nports = grp->lg_nports;
306 		attr.lg_policy = grp->lg_policy;
307 		attr.lg_lacp_mode = grp->lg_lacp_mode;
308 		attr.lg_lacp_timer = grp->lg_lacp_timer;
309 
310 		bcopy(grp->lg_mac, attr.lg_mac, ETHERADDRL);
311 		attr.lg_mac_fixed = grp->lg_mac_fixed;
312 
313 		attr.lg_ports = malloc(grp->lg_nports *
314 		    sizeof (dladm_aggr_port_attr_t));
315 		if (attr.lg_ports == NULL) {
316 			errno = ENOMEM;
317 			goto bail;
318 		}
319 
320 		where = (char *)(grp + 1);
321 
322 		/*
323 		 * Go through each port that is part of the group.
324 		 */
325 		for (j = 0; j < grp->lg_nports; j++) {
326 			/* LINTED E_BAD_PTR_CAST_ALIGN */
327 			port = (laioc_info_port_t *)where;
328 
329 			bcopy(port->lp_devname, attr.lg_ports[j].lp_devname,
330 			    MAXNAMELEN + 1);
331 			bcopy(port->lp_mac, attr.lg_ports[j].lp_mac,
332 			    ETHERADDRL);
333 			attr.lg_ports[j].lp_state = port->lp_state;
334 			attr.lg_ports[j].lp_lacp_state = port->lp_lacp_state;
335 
336 			where = (char *)(port + 1);
337 		}
338 
339 		rc = fn(arg, &attr);
340 		free(attr.lg_ports);
341 		if (rc != 0)
342 			goto bail;
343 	}
344 
345 bail:
346 	free(ioc);
347 	(void) close(fd);
348 	return (rc);
349 }
350 
351 /*
352  * Parse one line of the link aggregation DB, and return the corresponding
353  * group. Memory for the ports associated with the aggregation may be
354  * allocated. It is the responsibility of the caller to free the lt_ports
355  * aggregation group attribute.
356  *
357  * Returns -1 on parsing failure, or 0 on success.
358  */
359 static int
360 i_dladm_aggr_parse_db(char *line, dladm_aggr_grp_attr_db_t *attr)
361 {
362 	char	*token;
363 	int	i;
364 	int	value;
365 	char	*endp = NULL;
366 	char	*lasts = NULL;
367 
368 	bzero(attr, sizeof (*attr));
369 
370 	/* key */
371 	if ((token = strtok_r(line, " \t", &lasts)) == NULL)
372 		goto failed;
373 
374 	errno = 0;
375 	value = (int)strtol(token, &endp, 10);
376 	if (errno != 0 || *endp != '\0')
377 		goto failed;
378 
379 	attr->lt_key = value;
380 
381 	/* policy */
382 	if ((token = strtok_r(NULL, " \t", &lasts)) == NULL ||
383 	    !dladm_aggr_str2policy(token, &attr->lt_policy))
384 		goto failed;
385 
386 	/* number of ports */
387 	if ((token = strtok_r(NULL, " \t", &lasts)) == NULL)
388 		return (-1);
389 
390 	errno = 0;
391 	value = (int)strtol(token, &endp, 10);
392 	if (errno != 0 || *endp != '\0')
393 		goto failed;
394 
395 	attr->lt_nports = value;
396 
397 	/* ports */
398 	if ((attr->lt_ports = malloc(attr->lt_nports *
399 	    sizeof (dladm_aggr_port_attr_db_t))) == NULL)
400 		goto failed;
401 
402 	for (i = 0; i < attr->lt_nports; i++) {
403 		char *where, *devname;
404 
405 		/* port */
406 		if ((token = strtok_r(NULL, ", \t\n", &lasts)) == NULL)
407 			goto failed;
408 
409 		/*
410 		 * device name: In a previous version of this file, a port
411 		 * number could be specified using <devname>/<portnum>.
412 		 * This syntax is unecessary and obsolete.
413 		 */
414 		if ((devname = strtok_r(token, "/", &where)) == NULL)
415 			goto failed;
416 		if (strlcpy(attr->lt_ports[i].lp_devname, devname,
417 		    MAXNAMELEN) >= MAXNAMELEN)
418 			goto failed;
419 	}
420 
421 	/* unicast MAC address */
422 	if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL ||
423 	    !dladm_aggr_str2macaddr(token, &attr->lt_mac_fixed,
424 	    attr->lt_mac))
425 		goto failed;
426 
427 	/* LACP mode */
428 	if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL ||
429 	    !dladm_aggr_str2lacpmode(token, &attr->lt_lacp_mode))
430 		attr->lt_lacp_mode = AGGR_LACP_OFF;
431 
432 	/* LACP timer */
433 	if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL ||
434 	    !dladm_aggr_str2lacptimer(token, &attr->lt_lacp_timer))
435 		attr->lt_lacp_timer = AGGR_LACP_TIMER_SHORT;
436 
437 	return (0);
438 
439 failed:
440 	free(attr->lt_ports);
441 	attr->lt_ports = NULL;
442 	return (-1);
443 }
444 
445 /*
446  * Walk through the groups defined in the DB and for each group <grp>,
447  * invoke <fn>(<arg>, <grp>);
448  */
449 static dladm_status_t
450 i_dladm_aggr_walk_db(dladm_status_t (*fn)(void *, dladm_aggr_grp_attr_db_t *),
451     void *arg, const char *root)
452 {
453 	FILE *fp;
454 	char line[MAXLINELEN];
455 	dladm_aggr_grp_attr_db_t attr;
456 	char *db_file;
457 	char db_file_buf[MAXPATHLEN];
458 	int lock_fd;
459 	dladm_status_t status = DLADM_STATUS_OK;
460 
461 	if (root == NULL) {
462 		db_file = DLADM_AGGR_DB;
463 	} else {
464 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
465 		    DLADM_AGGR_DB);
466 		db_file = db_file_buf;
467 	}
468 
469 	lock_fd = i_dladm_aggr_lock_db(F_RDLCK);
470 
471 	if ((fp = fopen(db_file, "r")) == NULL) {
472 		status = dladm_errno2status(errno);
473 		i_dladm_aggr_unlock_db(lock_fd);
474 		return (status);
475 	}
476 
477 	bzero(&attr, sizeof (attr));
478 
479 	while (fgets(line, MAXLINELEN, fp) != NULL) {
480 		/* skip comments */
481 		if (BLANK_LINE(line))
482 			continue;
483 
484 		if (i_dladm_aggr_parse_db(line, &attr) != 0) {
485 			status = DLADM_STATUS_REPOSITORYINVAL;
486 			goto done;
487 		}
488 
489 		if ((status = fn(arg, &attr)) != DLADM_STATUS_OK)
490 			goto done;
491 
492 		free(attr.lt_ports);
493 		attr.lt_ports = NULL;
494 	}
495 
496 done:
497 	free(attr.lt_ports);
498 	(void) fclose(fp);
499 	i_dladm_aggr_unlock_db(lock_fd);
500 	return (status);
501 }
502 
503 /*
504  * Send an add or remove command to the link aggregation driver.
505  */
506 static dladm_status_t
507 i_dladm_aggr_add_rem_sys(dladm_aggr_grp_attr_db_t *attr, int cmd)
508 {
509 	int i, rc, fd, len;
510 	laioc_add_rem_t *iocp;
511 	laioc_port_t *ports;
512 	dladm_status_t status = DLADM_STATUS_OK;
513 
514 	len = sizeof (*iocp) + attr->lt_nports * sizeof (laioc_port_t);
515 	iocp = malloc(len);
516 	if (iocp == NULL) {
517 		status = DLADM_STATUS_NOMEM;
518 		goto done;
519 	}
520 
521 	iocp->la_key = attr->lt_key;
522 	iocp->la_nports = attr->lt_nports;
523 	ports = (laioc_port_t *)(iocp + 1);
524 
525 	for (i = 0; i < attr->lt_nports; i++) {
526 		if (strlcpy(ports[i].lp_devname,
527 		    attr->lt_ports[i].lp_devname,
528 		    MAXNAMELEN) >= MAXNAMELEN) {
529 			status = DLADM_STATUS_BADARG;
530 			goto done;
531 		}
532 	}
533 
534 	if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0) {
535 		status = dladm_errno2status(errno);
536 		goto done;
537 	}
538 
539 	rc = i_dladm_ioctl(fd, cmd, iocp, len);
540 	if (rc < 0) {
541 		if (errno == EINVAL)
542 			status = DLADM_STATUS_LINKINVAL;
543 		else
544 			status = dladm_errno2status(errno);
545 	}
546 
547 	(void) close(fd);
548 
549 done:
550 	free(iocp);
551 	return (status);
552 }
553 
554 /*
555  * Send a modify command to the link aggregation driver.
556  */
557 static dladm_status_t
558 i_dladm_aggr_modify_sys(uint32_t key, uint32_t mask,
559     dladm_aggr_modify_attr_t *attr)
560 {
561 	int rc, fd;
562 	laioc_modify_t ioc;
563 	dladm_status_t status = DLADM_STATUS_OK;
564 
565 	ioc.lu_key = key;
566 
567 	ioc.lu_modify_mask = 0;
568 	if (mask & DLADM_AGGR_MODIFY_POLICY)
569 		ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY;
570 	if (mask & DLADM_AGGR_MODIFY_MAC)
571 		ioc.lu_modify_mask |= LAIOC_MODIFY_MAC;
572 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
573 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE;
574 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
575 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER;
576 
577 	ioc.lu_policy = attr->ld_policy;
578 	ioc.lu_mac_fixed = attr->ld_mac_fixed;
579 	bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL);
580 	ioc.lu_lacp_mode = attr->ld_lacp_mode;
581 	ioc.lu_lacp_timer = attr->ld_lacp_timer;
582 
583 	if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0)
584 		return (dladm_errno2status(errno));
585 
586 	rc = i_dladm_ioctl(fd, LAIOC_MODIFY, &ioc, sizeof (ioc));
587 	if (rc < 0) {
588 		if (errno == EINVAL)
589 			status = DLADM_STATUS_MACADDRINVAL;
590 		else
591 			status = dladm_errno2status(errno);
592 	}
593 
594 	(void) close(fd);
595 	return (status);
596 }
597 
598 /*
599  * Send a create command to the link aggregation driver.
600  */
601 static dladm_status_t
602 i_dladm_aggr_create_sys(int fd, dladm_aggr_grp_attr_db_t *attr)
603 {
604 	int i, rc, len;
605 	laioc_create_t *iocp;
606 	laioc_port_t *ports;
607 	dladm_status_t status = DLADM_STATUS_OK;
608 
609 	len = sizeof (*iocp) + attr->lt_nports * sizeof (laioc_port_t);
610 	iocp = malloc(len);
611 	if (iocp == NULL)
612 		return (DLADM_STATUS_NOMEM);
613 
614 	iocp->lc_key = attr->lt_key;
615 	iocp->lc_nports = attr->lt_nports;
616 	iocp->lc_policy = attr->lt_policy;
617 	iocp->lc_lacp_mode = attr->lt_lacp_mode;
618 	iocp->lc_lacp_timer = attr->lt_lacp_timer;
619 
620 	ports = (laioc_port_t *)(iocp + 1);
621 
622 	for (i = 0; i < attr->lt_nports; i++) {
623 		if (strlcpy(ports[i].lp_devname,
624 		    attr->lt_ports[i].lp_devname,
625 		    MAXNAMELEN) >= MAXNAMELEN) {
626 			free(iocp);
627 			return (DLADM_STATUS_BADARG);
628 		}
629 	}
630 
631 	if (attr->lt_mac_fixed &&
632 	    ((bcmp(zero_mac, attr->lt_mac, ETHERADDRL) == 0) ||
633 	    (attr->lt_mac[0] & 0x01))) {
634 		free(iocp);
635 		return (DLADM_STATUS_MACADDRINVAL);
636 	}
637 
638 	bcopy(attr->lt_mac, iocp->lc_mac, ETHERADDRL);
639 	iocp->lc_mac_fixed = attr->lt_mac_fixed;
640 
641 	rc = i_dladm_ioctl(fd, LAIOC_CREATE, iocp, len);
642 	if (rc < 0)
643 		status = DLADM_STATUS_LINKINVAL;
644 
645 	free(iocp);
646 	return (status);
647 }
648 
649 /*
650  * Invoked to bring up a link aggregation group.
651  */
652 static dladm_status_t
653 i_dladm_aggr_up(void *arg, dladm_aggr_grp_attr_db_t *attr)
654 {
655 	dladm_aggr_up_t	*up = (dladm_aggr_up_t *)arg;
656 	dladm_status_t	status;
657 
658 	if (up->lu_key != 0 && up->lu_key != attr->lt_key)
659 		return (DLADM_STATUS_OK);
660 
661 	up->lu_found = B_TRUE;
662 
663 	status = i_dladm_aggr_create_sys(up->lu_fd, attr);
664 	if (status != DLADM_STATUS_OK && up->lu_key != 0)
665 		return (status);
666 
667 	return (DLADM_STATUS_OK);
668 }
669 
670 /*
671  * Bring up a link aggregation group or all of them if the key is zero.
672  * If key is 0, walk may terminate early if any of the links fail
673  */
674 dladm_status_t
675 dladm_aggr_up(uint32_t key, const char *root)
676 {
677 	dladm_aggr_up_t up;
678 	dladm_status_t status;
679 
680 	if ((up.lu_fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0)
681 		return (dladm_errno2status(errno));
682 
683 	up.lu_key = key;
684 	up.lu_found = B_FALSE;
685 
686 	status = i_dladm_aggr_walk_db(i_dladm_aggr_up, &up, root);
687 	if (status != DLADM_STATUS_OK) {
688 		(void) close(up.lu_fd);
689 		return (status);
690 	}
691 	(void) close(up.lu_fd);
692 
693 	/*
694 	 * only return error if user specified key and key was
695 	 * not found
696 	 */
697 	if (!up.lu_found && key != 0)
698 		return (DLADM_STATUS_NOTFOUND);
699 
700 	return (DLADM_STATUS_OK);
701 }
702 /*
703  * Send a delete command to the link aggregation driver.
704  */
705 static int
706 i_dladm_aggr_delete_sys(int fd, dladm_aggr_grp_attr_t *attr)
707 {
708 	laioc_delete_t ioc;
709 
710 	ioc.ld_key = attr->lg_key;
711 
712 	return (i_dladm_ioctl(fd, LAIOC_DELETE, &ioc, sizeof (ioc)));
713 }
714 
715 /*
716  * Invoked to bring down a link aggregation group.
717  */
718 static int
719 i_dladm_aggr_down(void *arg, dladm_aggr_grp_attr_t *attr)
720 {
721 	dladm_aggr_down_t *down = (dladm_aggr_down_t *)arg;
722 	int fd, errno_save;
723 
724 	if (down->ld_key != 0 && down->ld_key != attr->lg_key)
725 		return (0);
726 
727 	down->ld_found = B_TRUE;
728 
729 	if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0)
730 		return (-1);
731 
732 	if (i_dladm_aggr_delete_sys(fd, attr) < 0 && down->ld_key != 0) {
733 		errno_save = errno;
734 		(void) close(fd);
735 		errno = errno_save;
736 		return (-1);
737 	}
738 
739 	(void) close(fd);
740 	return (0);
741 }
742 
743 /*
744  * Bring down a link aggregation group or all of them if the key is zero.
745  * If key is 0, walk may terminate early if any of the links fail
746  */
747 dladm_status_t
748 dladm_aggr_down(uint32_t key)
749 {
750 	dladm_aggr_down_t down;
751 
752 	down.ld_key = key;
753 	down.ld_found = B_FALSE;
754 
755 	if (dladm_aggr_walk(i_dladm_aggr_down, &down) < 0)
756 		return (dladm_errno2status(errno));
757 
758 	/*
759 	 * only return error if user specified key and key was
760 	 * not found
761 	 */
762 	if (!down.ld_found && key != 0)
763 		return (DLADM_STATUS_NOTFOUND);
764 
765 	return (DLADM_STATUS_OK);
766 }
767 
768 /*
769  * For each group <grp> found in the DB, invokes <fn>(<grp>, <arg>).
770  *
771  * The following values can be returned by <fn>():
772  *
773  * -1: an error occured. This will cause the walk to be terminated,
774  *     and the original DB file to be preserved.
775  *
776  *  0: success and write. The walker will write the contents of
777  *     the attribute passed as argument to <fn>(), and continue walking
778  *     the entries found in the DB.
779  *
780  *  1: skip. The walker should not write the contents of the current
781  *     group attributes to the new DB, but should continue walking
782  *     the entries found in the DB.
783  */
784 static dladm_status_t
785 i_dladm_aggr_walk_rw_db(int (*fn)(void *, dladm_aggr_grp_attr_db_t *),
786     void *arg, const char *root)
787 {
788 	FILE *fp, *nfp;
789 	int nfd, fn_rc, lock_fd;
790 	char line[MAXLINELEN];
791 	dladm_aggr_grp_attr_db_t attr;
792 	char *db_file, *tmp_db_file;
793 	char db_file_buf[MAXPATHLEN];
794 	char tmp_db_file_buf[MAXPATHLEN];
795 	dladm_status_t status;
796 
797 	if (root == NULL) {
798 		db_file = DLADM_AGGR_DB;
799 		tmp_db_file = DLADM_AGGR_DB_TMP;
800 	} else {
801 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
802 		    DLADM_AGGR_DB);
803 		(void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root,
804 		    DLADM_AGGR_DB_TMP);
805 		db_file = db_file_buf;
806 		tmp_db_file = tmp_db_file_buf;
807 	}
808 
809 	if ((lock_fd = i_dladm_aggr_lock_db(F_WRLCK)) < 0)
810 		return (dladm_errno2status(errno));
811 
812 	if ((fp = fopen(db_file, "r")) == NULL) {
813 		status = dladm_errno2status(errno);
814 		i_dladm_aggr_unlock_db(lock_fd);
815 		return (status);
816 	}
817 
818 	if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC,
819 	    DLADM_AGGR_DB_PERMS)) == -1) {
820 		status = dladm_errno2status(errno);
821 		(void) fclose(fp);
822 		i_dladm_aggr_unlock_db(lock_fd);
823 		return (status);
824 	}
825 
826 	if ((nfp = fdopen(nfd, "w")) == NULL) {
827 		status = dladm_errno2status(errno);
828 		(void) close(nfd);
829 		(void) fclose(fp);
830 		(void) unlink(tmp_db_file);
831 		i_dladm_aggr_unlock_db(lock_fd);
832 		return (status);
833 	}
834 
835 	attr.lt_ports = NULL;
836 
837 	while (fgets(line, MAXLINELEN, fp) != NULL) {
838 
839 		/* skip comments */
840 		if (BLANK_LINE(line)) {
841 			if (fputs(line, nfp) == EOF) {
842 				status = dladm_errno2status(errno);
843 				goto failed;
844 			}
845 			continue;
846 		}
847 
848 		if (i_dladm_aggr_parse_db(line, &attr) != 0) {
849 			status = DLADM_STATUS_REPOSITORYINVAL;
850 			goto failed;
851 		}
852 
853 		fn_rc = fn(arg, &attr);
854 
855 		switch (fn_rc) {
856 		case -1:
857 			/* failure, stop walking */
858 			status = dladm_errno2status(errno);
859 			goto failed;
860 		case 0:
861 			/*
862 			 * Success, write group attributes, which could
863 			 * have been modified by fn().
864 			 */
865 			if (i_dladm_aggr_fput_grp(nfp, &attr) != 0) {
866 				status = dladm_errno2status(errno);
867 				goto failed;
868 			}
869 			break;
870 		case 1:
871 			/* skip current group */
872 			break;
873 		}
874 
875 		free(attr.lt_ports);
876 		attr.lt_ports = NULL;
877 	}
878 
879 	if (getuid() == 0 || geteuid() == 0) {
880 		if (fchmod(nfd, DLADM_AGGR_DB_PERMS) == -1) {
881 			status = dladm_errno2status(errno);
882 			goto failed;
883 		}
884 
885 		if (fchown(nfd, DLADM_AGGR_DB_OWNER,
886 		    DLADM_AGGR_DB_GROUP) == -1) {
887 			status = dladm_errno2status(errno);
888 			goto failed;
889 		}
890 	}
891 
892 	if (fflush(nfp) == EOF) {
893 		status = dladm_errno2status(errno);
894 		goto failed;
895 	}
896 
897 	(void) fclose(fp);
898 	(void) fclose(nfp);
899 
900 	if (rename(tmp_db_file, db_file) == -1) {
901 		status = dladm_errno2status(errno);
902 		(void) unlink(tmp_db_file);
903 		i_dladm_aggr_unlock_db(lock_fd);
904 		return (status);
905 	}
906 
907 	i_dladm_aggr_unlock_db(lock_fd);
908 	return (DLADM_STATUS_OK);
909 
910 failed:
911 	free(attr.lt_ports);
912 	(void) fclose(fp);
913 	(void) fclose(nfp);
914 	(void) unlink(tmp_db_file);
915 	i_dladm_aggr_unlock_db(lock_fd);
916 
917 	return (status);
918 }
919 
920 /*
921  * Remove an entry from the DB.
922  */
923 static int
924 i_dladm_aggr_del_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp)
925 {
926 	delete_db_state_t *state = arg;
927 
928 	if (grp->lt_key != state->ds_key)
929 		return (0);
930 
931 	state->ds_found = B_TRUE;
932 
933 	/* don't save matching group */
934 	return (1);
935 }
936 
937 static dladm_status_t
938 i_dladm_aggr_del_db(dladm_aggr_grp_attr_db_t *attr, const char *root)
939 {
940 	delete_db_state_t state;
941 	dladm_status_t status;
942 
943 	state.ds_key = attr->lt_key;
944 	state.ds_found = B_FALSE;
945 
946 	status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_del_db_fn, &state, root);
947 	if (status != DLADM_STATUS_OK)
948 		return (status);
949 
950 	if (!state.ds_found)
951 		return (DLADM_STATUS_NOTFOUND);
952 
953 	return (DLADM_STATUS_OK);
954 }
955 
956 /*
957  * Modify the properties of an existing group in the DB.
958  */
959 static int
960 i_dladm_aggr_modify_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp)
961 {
962 	modify_db_state_t *state = arg;
963 	dladm_aggr_modify_attr_t *new_attr = state->us_attr_new;
964 	dladm_aggr_modify_attr_t *old_attr = state->us_attr_old;
965 
966 	if (grp->lt_key != state->us_key)
967 		return (0);
968 
969 	state->us_found = B_TRUE;
970 
971 	if (state->us_mask & DLADM_AGGR_MODIFY_POLICY) {
972 		if (old_attr != NULL)
973 			old_attr->ld_policy = grp->lt_policy;
974 		grp->lt_policy = new_attr->ld_policy;
975 	}
976 
977 	if (state->us_mask & DLADM_AGGR_MODIFY_MAC) {
978 		if (old_attr != NULL) {
979 			old_attr->ld_mac_fixed = grp->lt_mac_fixed;
980 			bcopy(grp->lt_mac, old_attr->ld_mac, ETHERADDRL);
981 		}
982 		grp->lt_mac_fixed = new_attr->ld_mac_fixed;
983 		bcopy(new_attr->ld_mac, grp->lt_mac, ETHERADDRL);
984 	}
985 
986 	if (state->us_mask & DLADM_AGGR_MODIFY_LACP_MODE) {
987 		if (old_attr != NULL)
988 			old_attr->ld_lacp_mode = grp->lt_lacp_mode;
989 		grp->lt_lacp_mode = new_attr->ld_lacp_mode;
990 	}
991 
992 	if (state->us_mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
993 		if (old_attr != NULL)
994 			old_attr->ld_lacp_timer = grp->lt_lacp_timer;
995 		grp->lt_lacp_timer = new_attr->ld_lacp_timer;
996 	}
997 
998 	/* save modified group */
999 	return (0);
1000 }
1001 
1002 static dladm_status_t
1003 i_dladm_aggr_modify_db(uint32_t key, uint32_t mask,
1004     dladm_aggr_modify_attr_t *new, dladm_aggr_modify_attr_t *old,
1005     const char *root)
1006 {
1007 	modify_db_state_t state;
1008 	dladm_status_t status;
1009 
1010 	state.us_key = key;
1011 	state.us_mask = mask;
1012 	state.us_attr_new = new;
1013 	state.us_attr_old = old;
1014 	state.us_found = B_FALSE;
1015 
1016 	if ((status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_modify_db_fn,
1017 	    &state, root)) != DLADM_STATUS_OK) {
1018 		return (status);
1019 	}
1020 
1021 	if (!state.us_found)
1022 		return (DLADM_STATUS_NOTFOUND);
1023 
1024 	return (DLADM_STATUS_OK);
1025 }
1026 
1027 /*
1028  * Add ports to an existing group in the DB.
1029  */
1030 static int
1031 i_dladm_aggr_add_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp)
1032 {
1033 	add_db_state_t *state = arg;
1034 	dladm_aggr_grp_attr_db_t *attr = state->as_attr;
1035 	void *ports;
1036 	int i, j;
1037 
1038 	if (grp->lt_key != attr->lt_key)
1039 		return (0);
1040 
1041 	state->as_found = B_TRUE;
1042 
1043 	/* are any of the ports to be added already members of the group? */
1044 	for (i = 0; i < grp->lt_nports; i++) {
1045 		for (j = 0; j < attr->lt_nports; j++) {
1046 			if (strcmp(grp->lt_ports[i].lp_devname,
1047 			    attr->lt_ports[j].lp_devname) == 0) {
1048 				errno = EEXIST;
1049 				return (-1);
1050 			}
1051 		}
1052 	}
1053 
1054 	/* add groups specified by attr to grp */
1055 	ports = realloc(grp->lt_ports, (grp->lt_nports +
1056 	    attr->lt_nports) * sizeof (dladm_aggr_port_attr_db_t));
1057 	if (ports == NULL)
1058 		return (-1);
1059 	grp->lt_ports = ports;
1060 
1061 	for (i = 0; i < attr->lt_nports; i++) {
1062 		if (strlcpy(grp->lt_ports[grp->lt_nports + i].lp_devname,
1063 		    attr->lt_ports[i].lp_devname, MAXNAMELEN + 1) >=
1064 		    MAXNAMELEN + 1)
1065 			return (-1);
1066 	}
1067 
1068 	grp->lt_nports += attr->lt_nports;
1069 
1070 	/* save modified group */
1071 	return (0);
1072 }
1073 
1074 static dladm_status_t
1075 i_dladm_aggr_add_db(dladm_aggr_grp_attr_db_t *attr, const char *root)
1076 {
1077 	add_db_state_t state;
1078 	dladm_status_t status;
1079 
1080 	state.as_attr = attr;
1081 	state.as_found = B_FALSE;
1082 
1083 	status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_add_db_fn, &state, root);
1084 	if (status != DLADM_STATUS_OK)
1085 		return (status);
1086 
1087 	if (!state.as_found)
1088 		return (DLADM_STATUS_NOTFOUND);
1089 
1090 	return (DLADM_STATUS_OK);
1091 }
1092 
1093 /*
1094  * Remove ports from an existing group in the DB.
1095  */
1096 
1097 typedef struct remove_db_state {
1098 	dladm_aggr_grp_attr_db_t *rs_attr;
1099 	boolean_t	rs_found;
1100 } remove_db_state_t;
1101 
1102 static int
1103 i_dladm_aggr_remove_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp)
1104 {
1105 	remove_db_state_t *state = (remove_db_state_t *)arg;
1106 	dladm_aggr_grp_attr_db_t *attr = state->rs_attr;
1107 	int i, j, k, nremoved;
1108 	boolean_t match;
1109 
1110 	if (grp->lt_key != attr->lt_key)
1111 		return (0);
1112 
1113 	state->rs_found = B_TRUE;
1114 
1115 	/* remove the ports specified by attr from the group */
1116 	nremoved = 0;
1117 	k = 0;
1118 	for (i = 0; i < grp->lt_nports; i++) {
1119 		match = B_FALSE;
1120 		for (j = 0; j < attr->lt_nports && !match; j++) {
1121 			match = (strcmp(grp->lt_ports[i].lp_devname,
1122 			    attr->lt_ports[j].lp_devname) == 0);
1123 		}
1124 		if (match)
1125 			nremoved++;
1126 		else
1127 			grp->lt_ports[k++] = grp->lt_ports[i];
1128 	}
1129 
1130 	if (nremoved != attr->lt_nports) {
1131 		errno = ENOENT;
1132 		return (-1);
1133 	}
1134 
1135 	grp->lt_nports -= nremoved;
1136 
1137 	/* save modified group */
1138 	return (0);
1139 }
1140 
1141 static dladm_status_t
1142 i_dladm_aggr_remove_db(dladm_aggr_grp_attr_db_t *attr, const char *root)
1143 {
1144 	remove_db_state_t state;
1145 	dladm_status_t status;
1146 
1147 	state.rs_attr = attr;
1148 	state.rs_found = B_FALSE;
1149 
1150 	status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_remove_db_fn,
1151 	    &state, root);
1152 	if (status != DLADM_STATUS_OK)
1153 		return (status);
1154 
1155 	if (!state.rs_found)
1156 		return (DLADM_STATUS_NOTFOUND);
1157 
1158 	return (DLADM_STATUS_OK);
1159 }
1160 
1161 /*
1162  * Given a policy string, return a policy mask. Returns B_TRUE on
1163  * success, or B_FALSE if an error occured during parsing.
1164  */
1165 boolean_t
1166 dladm_aggr_str2policy(const char *str, uint32_t *policy)
1167 {
1168 	int i;
1169 	policy_t *pol;
1170 	char *token = NULL;
1171 	char *lasts;
1172 
1173 	*policy = 0;
1174 
1175 	while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
1176 	    &lasts)) != NULL) {
1177 		for (i = 0; i < NPOLICIES; i++) {
1178 			pol = &policies[i];
1179 			if (strcasecmp(token, pol->pol_name) == 0) {
1180 				*policy |= pol->policy;
1181 				break;
1182 			}
1183 		}
1184 		if (i == NPOLICIES)
1185 			return (B_FALSE);
1186 	}
1187 
1188 	return (B_TRUE);
1189 }
1190 
1191 /*
1192  * Given a policy mask, returns a printable string, or NULL if the
1193  * policy mask is invalid. It is the responsibility of the caller to
1194  * free the returned string after use.
1195  */
1196 char *
1197 dladm_aggr_policy2str(uint32_t policy, char *str)
1198 {
1199 	int i, npolicies = 0;
1200 	policy_t *pol;
1201 
1202 	str[0] = '\0';
1203 
1204 	for (i = 0; i < NPOLICIES; i++) {
1205 		pol = &policies[i];
1206 		if ((policy & pol->policy) != 0) {
1207 			npolicies++;
1208 			if (npolicies > 1)
1209 				(void) strcat(str, ",");
1210 			(void) strcat(str, pol->pol_name);
1211 		}
1212 	}
1213 
1214 	return (str);
1215 }
1216 
1217 /*
1218  * Given a MAC address string, return the MAC address in the mac_addr
1219  * array. If the MAC address was not explicitly specified, i.e. is
1220  * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
1221  * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
1222  */
1223 boolean_t
1224 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
1225 {
1226 	uchar_t *conv_str;
1227 	int mac_len;
1228 
1229 	*mac_fixed = (strcmp(str, "auto") != 0);
1230 	if (!*mac_fixed) {
1231 		bzero(mac_addr, ETHERADDRL);
1232 		return (B_TRUE);
1233 	}
1234 
1235 	conv_str = _link_aton(str, &mac_len);
1236 	if (conv_str == NULL)
1237 		return (B_FALSE);
1238 
1239 	if (mac_len != ETHERADDRL) {
1240 		free(conv_str);
1241 		return (B_FALSE);
1242 	}
1243 
1244 	if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
1245 	    (conv_str[0] & 0x01)) {
1246 		free(conv_str);
1247 		return (B_FALSE);
1248 	}
1249 
1250 	bcopy(conv_str, mac_addr, ETHERADDRL);
1251 	free(conv_str);
1252 
1253 	return (B_TRUE);
1254 }
1255 
1256 /*
1257  * Returns a string containing a printable representation of a MAC address.
1258  */
1259 const char *
1260 dladm_aggr_macaddr2str(unsigned char *mac, char *buf)
1261 {
1262 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
1263 
1264 	if (buf == NULL)
1265 		return (NULL);
1266 
1267 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
1268 		return (gettext("<unknown>"));
1269 	else
1270 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
1271 }
1272 
1273 /*
1274  * Given a LACP mode string, find the corresponding LACP mode number. Returns
1275  * B_TRUE if a match was found, B_FALSE otherwise.
1276  */
1277 boolean_t
1278 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
1279 {
1280 	int i;
1281 	dladm_aggr_lacpmode_t *mode;
1282 
1283 	for (i = 0; i < NLACP_MODES; i++) {
1284 		mode = &lacp_modes[i];
1285 		if (strncasecmp(str, mode->mode_str,
1286 		    strlen(mode->mode_str)) == 0) {
1287 			*lacp_mode = mode->mode_id;
1288 			return (B_TRUE);
1289 		}
1290 	}
1291 
1292 	return (B_FALSE);
1293 }
1294 
1295 /*
1296  * Given a LACP mode number, returns a printable string, or NULL if the
1297  * LACP mode number is invalid.
1298  */
1299 const char *
1300 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
1301 {
1302 	int i;
1303 	dladm_aggr_lacpmode_t *mode;
1304 
1305 	for (i = 0; i < NLACP_MODES; i++) {
1306 		mode = &lacp_modes[i];
1307 		if (mode->mode_id == mode_id) {
1308 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
1309 			    mode->mode_str);
1310 			return (buf);
1311 		}
1312 	}
1313 
1314 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
1315 	return (buf);
1316 }
1317 
1318 /*
1319  * Given a LACP timer string, find the corresponding LACP timer number. Returns
1320  * B_TRUE if a match was found, B_FALSE otherwise.
1321  */
1322 boolean_t
1323 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
1324 {
1325 	int i;
1326 	dladm_aggr_lacptimer_t *timer;
1327 
1328 	for (i = 0; i < NLACP_TIMERS; i++) {
1329 		timer = &lacp_timers[i];
1330 		if (strncasecmp(str, timer->lt_str,
1331 		    strlen(timer->lt_str)) == 0) {
1332 			*lacp_timer = timer->lt_id;
1333 			return (B_TRUE);
1334 		}
1335 	}
1336 
1337 	return (B_FALSE);
1338 }
1339 
1340 /*
1341  * Given a LACP timer, returns a printable string, or NULL if the
1342  * LACP timer number is invalid.
1343  */
1344 const char *
1345 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
1346 {
1347 	int i;
1348 	dladm_aggr_lacptimer_t *timer;
1349 
1350 	for (i = 0; i < NLACP_TIMERS; i++) {
1351 		timer = &lacp_timers[i];
1352 		if (timer->lt_id == timer_id) {
1353 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
1354 			    timer->lt_str);
1355 			return (buf);
1356 		}
1357 	}
1358 
1359 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
1360 	return (buf);
1361 }
1362 
1363 const char *
1364 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
1365 {
1366 	int			i;
1367 	dladm_aggr_port_state_t	*state;
1368 
1369 	for (i = 0; i < NPORT_STATES; i++) {
1370 		state = &port_states[i];
1371 		if (state->state_id == state_id) {
1372 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
1373 			    state->state_str);
1374 			return (buf);
1375 		}
1376 	}
1377 
1378 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
1379 	return (buf);
1380 }
1381 
1382 #define	FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
1383 
1384 /*
1385  * Write the attribute of a group to the specified file. Returns 0 on
1386  * success, -1 on failure.
1387  */
1388 static int
1389 i_dladm_aggr_fput_grp(FILE *fp, dladm_aggr_grp_attr_db_t *attr)
1390 {
1391 	int i;
1392 	char addr_str[ETHERADDRL * 3];
1393 	char buf[DLADM_STRSIZE];
1394 
1395 	/* key, policy */
1396 	FPRINTF_ERR(fprintf(fp, "%d\t%s\t", attr->lt_key,
1397 	    dladm_aggr_policy2str(attr->lt_policy, buf)));
1398 
1399 	/* number of ports, ports */
1400 	FPRINTF_ERR(fprintf(fp, "%d\t", attr->lt_nports));
1401 	for (i = 0; i < attr->lt_nports; i++) {
1402 		if (i > 0)
1403 			FPRINTF_ERR(fprintf(fp, ","));
1404 		FPRINTF_ERR(fprintf(fp, "%s", attr->lt_ports[i].lp_devname));
1405 	}
1406 	FPRINTF_ERR(fprintf(fp, "\t"));
1407 
1408 	/* MAC address */
1409 	if (!attr->lt_mac_fixed) {
1410 		FPRINTF_ERR(fprintf(fp, "auto"));
1411 	} else {
1412 		FPRINTF_ERR(fprintf(fp, "%s",
1413 		    dladm_aggr_macaddr2str(attr->lt_mac, addr_str)));
1414 	}
1415 	FPRINTF_ERR(fprintf(fp, "\t"));
1416 
1417 	FPRINTF_ERR(fprintf(fp, "%s\t",
1418 	    dladm_aggr_lacpmode2str(attr->lt_lacp_mode, buf)));
1419 
1420 	FPRINTF_ERR(fprintf(fp, "%s\n",
1421 	    dladm_aggr_lacptimer2str(attr->lt_lacp_timer, buf)));
1422 
1423 	return (0);
1424 }
1425 
1426 static dladm_status_t
1427 i_dladm_aggr_create_db(dladm_aggr_grp_attr_db_t *attr, const char *root)
1428 {
1429 	FILE		*fp;
1430 	char		line[MAXLINELEN];
1431 	uint32_t	key;
1432 	int 		lock_fd;
1433 	char 		*db_file;
1434 	char 		db_file_buf[MAXPATHLEN];
1435 	char 		*endp = NULL;
1436 	dladm_status_t	status;
1437 
1438 	if (root == NULL) {
1439 		db_file = DLADM_AGGR_DB;
1440 	} else {
1441 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
1442 		    DLADM_AGGR_DB);
1443 		db_file = db_file_buf;
1444 	}
1445 
1446 	if ((lock_fd = i_dladm_aggr_lock_db(F_WRLCK)) < 0)
1447 		return (dladm_errno2status(errno));
1448 
1449 	if ((fp = fopen(db_file, "r+")) == NULL &&
1450 	    (fp = fopen(db_file, "w")) == NULL) {
1451 		status = dladm_errno2status(errno);
1452 		i_dladm_aggr_unlock_db(lock_fd);
1453 		return (status);
1454 	}
1455 
1456 	/* look for existing group with same key */
1457 	while (fgets(line, MAXLINELEN, fp) != NULL) {
1458 		char *holder, *lasts;
1459 
1460 		/* skip comments */
1461 		if (BLANK_LINE(line))
1462 			continue;
1463 
1464 		/* ignore corrupted lines */
1465 		holder = strtok_r(line, " \t", &lasts);
1466 		if (holder == NULL)
1467 			continue;
1468 
1469 		/* port number */
1470 		errno = 0;
1471 		key = (int)strtol(holder, &endp, 10);
1472 		if (errno != 0 || *endp != '\0') {
1473 			status = DLADM_STATUS_REPOSITORYINVAL;
1474 			goto done;
1475 		}
1476 
1477 		if (key == attr->lt_key) {
1478 			/* group with key already exists */
1479 			status = DLADM_STATUS_EXIST;
1480 			goto done;
1481 		}
1482 	}
1483 
1484 	/*
1485 	 * If we get here, we've verified that no existing group with
1486 	 * the same key already exists. It's now time to add the
1487 	 * new group to the DB.
1488 	 */
1489 	if (i_dladm_aggr_fput_grp(fp, attr) != 0) {
1490 		status = dladm_errno2status(errno);
1491 		goto done;
1492 	}
1493 
1494 	status = DLADM_STATUS_OK;
1495 
1496 done:
1497 	(void) fclose(fp);
1498 	i_dladm_aggr_unlock_db(lock_fd);
1499 	return (status);
1500 }
1501 
1502 /*
1503  * Create a new link aggregation group. Update the configuration
1504  * file and bring it up.
1505  */
1506 dladm_status_t
1507 dladm_aggr_create(uint32_t key, uint32_t nports,
1508     dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed,
1509     uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer,
1510     boolean_t tempop, const char *root)
1511 {
1512 	dladm_aggr_grp_attr_db_t attr;
1513 	dladm_status_t status;
1514 
1515 	if (key == 0 || key > DLADM_AGGR_MAX_KEY)
1516 		return (DLADM_STATUS_KEYINVAL);
1517 
1518 	attr.lt_key = key;
1519 	attr.lt_nports = nports;
1520 	attr.lt_ports = ports;
1521 	attr.lt_policy = policy;
1522 	attr.lt_mac_fixed = mac_addr_fixed;
1523 	if (attr.lt_mac_fixed)
1524 		bcopy(mac_addr, attr.lt_mac, ETHERADDRL);
1525 	else
1526 		bzero(attr.lt_mac, ETHERADDRL);
1527 	attr.lt_lacp_mode = lacp_mode;
1528 	attr.lt_lacp_timer = lacp_timer;
1529 
1530 	/* add the link aggregation group to the DB */
1531 	if (!tempop) {
1532 		status = i_dladm_aggr_create_db(&attr, root);
1533 		if (status != DLADM_STATUS_OK)
1534 			return (status);
1535 	} else {
1536 		dladm_aggr_up_t up;
1537 
1538 		up.lu_key = key;
1539 		up.lu_found = B_FALSE;
1540 		up.lu_fd = open(DLADM_AGGR_DEV, O_RDWR);
1541 		if (up.lu_fd < 0)
1542 			return (dladm_errno2status(errno));
1543 
1544 		status = i_dladm_aggr_up((void *)&up, &attr);
1545 		(void) close(up.lu_fd);
1546 		return (status);
1547 	}
1548 
1549 	/* bring up the link aggregation group */
1550 	status = dladm_aggr_up(key, root);
1551 	/*
1552 	 * If the operation fails because the aggregation already exists,
1553 	 * then only update the persistent configuration repository and
1554 	 * return success.
1555 	 */
1556 	if (status == DLADM_STATUS_EXIST)
1557 		status = DLADM_STATUS_OK;
1558 
1559 	if (status != DLADM_STATUS_OK && !tempop)
1560 		(void) i_dladm_aggr_del_db(&attr, root);
1561 
1562 	return (status);
1563 }
1564 
1565 /*
1566  * Modify the parameters of an existing link aggregation group. Update
1567  * the configuration file and pass the changes to the kernel.
1568  */
1569 dladm_status_t
1570 dladm_aggr_modify(uint32_t key, uint32_t modify_mask, uint32_t policy,
1571     boolean_t mac_fixed, uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1572     aggr_lacp_timer_t lacp_timer, boolean_t tempop, const char *root)
1573 {
1574 	dladm_aggr_modify_attr_t new_attr, old_attr;
1575 	dladm_status_t status;
1576 
1577 	if (key == 0)
1578 		return (DLADM_STATUS_KEYINVAL);
1579 
1580 	if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
1581 		new_attr.ld_policy = policy;
1582 
1583 	if (modify_mask & DLADM_AGGR_MODIFY_MAC) {
1584 		new_attr.ld_mac_fixed = mac_fixed;
1585 		bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL);
1586 	}
1587 
1588 	if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
1589 		new_attr.ld_lacp_mode = lacp_mode;
1590 
1591 	if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
1592 		new_attr.ld_lacp_timer = lacp_timer;
1593 
1594 	/* update the DB */
1595 	if (!tempop && ((status = i_dladm_aggr_modify_db(key, modify_mask,
1596 	    &new_attr, &old_attr, root)) != DLADM_STATUS_OK)) {
1597 		return (status);
1598 	}
1599 
1600 	status = i_dladm_aggr_modify_sys(key, modify_mask, &new_attr);
1601 	if (status != DLADM_STATUS_OK && !tempop) {
1602 		(void) i_dladm_aggr_modify_db(key, modify_mask, &old_attr,
1603 		    NULL, root);
1604 	}
1605 
1606 	return (status);
1607 }
1608 
1609 /*
1610  * Delete a previously created link aggregation group.
1611  */
1612 dladm_status_t
1613 dladm_aggr_delete(uint32_t key, boolean_t tempop, const char *root)
1614 {
1615 	dladm_aggr_grp_attr_db_t db_attr;
1616 	dladm_status_t status;
1617 
1618 	if (key == 0)
1619 		return (DLADM_STATUS_KEYINVAL);
1620 
1621 	if (tempop) {
1622 		dladm_aggr_down_t down;
1623 		dladm_aggr_grp_attr_t sys_attr;
1624 
1625 		down.ld_key = key;
1626 		down.ld_found = B_FALSE;
1627 		sys_attr.lg_key = key;
1628 		if (i_dladm_aggr_down((void *)&down, &sys_attr) < 0)
1629 			return (dladm_errno2status(errno));
1630 		else
1631 			return (DLADM_STATUS_OK);
1632 	} else {
1633 		status = dladm_aggr_down(key);
1634 
1635 		/*
1636 		 * Only continue to delete the configuration repository
1637 		 * either if we successfully delete the active aggregation
1638 		 * or if the aggregation is not found.
1639 		 */
1640 		if (status != DLADM_STATUS_OK &&
1641 		    status != DLADM_STATUS_NOTFOUND) {
1642 			return (status);
1643 		}
1644 	}
1645 
1646 	if (tempop)
1647 		return (DLADM_STATUS_OK);
1648 
1649 	db_attr.lt_key = key;
1650 	return (i_dladm_aggr_del_db(&db_attr, root));
1651 }
1652 
1653 /*
1654  * Add one or more ports to an existing link aggregation.
1655  */
1656 dladm_status_t
1657 dladm_aggr_add(uint32_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports,
1658     boolean_t tempop, const char *root)
1659 {
1660 	dladm_aggr_grp_attr_db_t attr;
1661 	dladm_status_t status;
1662 
1663 	if (key == 0)
1664 		return (DLADM_STATUS_KEYINVAL);
1665 
1666 	bzero(&attr, sizeof (attr));
1667 	attr.lt_key = key;
1668 	attr.lt_nports = nports;
1669 	attr.lt_ports = ports;
1670 
1671 	if (!tempop &&
1672 	    ((status = i_dladm_aggr_add_db(&attr, root)) != DLADM_STATUS_OK)) {
1673 		return (status);
1674 	}
1675 
1676 	status = i_dladm_aggr_add_rem_sys(&attr, LAIOC_ADD);
1677 	if (status != DLADM_STATUS_OK && !tempop)
1678 		(void) i_dladm_aggr_remove_db(&attr, root);
1679 
1680 	return (status);
1681 }
1682 
1683 /*
1684  * Remove one or more ports from an existing link aggregation.
1685  */
1686 dladm_status_t
1687 dladm_aggr_remove(uint32_t key, uint32_t nports,
1688     dladm_aggr_port_attr_db_t *ports, boolean_t tempop, const char *root)
1689 {
1690 	dladm_aggr_grp_attr_db_t attr;
1691 	dladm_status_t status;
1692 
1693 	if (key == 0)
1694 		return (DLADM_STATUS_KEYINVAL);
1695 
1696 	bzero(&attr, sizeof (attr));
1697 	attr.lt_key = key;
1698 	attr.lt_nports = nports;
1699 	attr.lt_ports = ports;
1700 
1701 	if (!tempop &&
1702 	    ((status = i_dladm_aggr_remove_db(&attr, root)) !=
1703 	    DLADM_STATUS_OK)) {
1704 		return (status);
1705 	}
1706 
1707 	status = i_dladm_aggr_add_rem_sys(&attr, LAIOC_REMOVE);
1708 	if (status != DLADM_STATUS_OK && !tempop)
1709 		(void) i_dladm_aggr_add_db(&attr, root);
1710 
1711 	return (status);
1712 }
1713