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