10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52311Sseb  * Common Development and Distribution License (the "License").
62311Sseb  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
225895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <unistd.h>
270Sstevel@tonic-gate #include <errno.h>
285895Syz147064 #include <ctype.h>
293147Sxc151355 #include <fcntl.h>
303147Sxc151355 #include <strings.h>
313147Sxc151355 #include <dirent.h>
32*8275SEric Cheng #include <stdlib.h>
336173Syz147064 #include <sys/param.h>
343147Sxc151355 #include <sys/stat.h>
353147Sxc151355 #include <libdladm_impl.h>
363184Smeem #include <libintl.h>
375895Syz147064 #include <libdlpi.h>
38733Skrgopi 
393147Sxc151355 static char		dladm_rootdir[MAXPATHLEN] = "/";
403147Sxc151355 
413147Sxc151355 const char *
423147Sxc151355 dladm_status2str(dladm_status_t status, char *buf)
433147Sxc151355 {
443147Sxc151355 	const char	*s;
453147Sxc151355 
463147Sxc151355 	switch (status) {
473147Sxc151355 	case DLADM_STATUS_OK:
483147Sxc151355 		s = "ok";
493147Sxc151355 		break;
503147Sxc151355 	case DLADM_STATUS_BADARG:
513147Sxc151355 		s = "invalid argument";
523147Sxc151355 		break;
533147Sxc151355 	case DLADM_STATUS_FAILED:
543147Sxc151355 		s = "operation failed";
553147Sxc151355 		break;
563147Sxc151355 	case DLADM_STATUS_TOOSMALL:
573147Sxc151355 		s = "buffer size too small";
583147Sxc151355 		break;
593147Sxc151355 	case DLADM_STATUS_NOTSUP:
603147Sxc151355 		s = "operation not supported";
613147Sxc151355 		break;
623147Sxc151355 	case DLADM_STATUS_NOTFOUND:
633147Sxc151355 		s = "object not found";
643147Sxc151355 		break;
653147Sxc151355 	case DLADM_STATUS_BADVAL:
663147Sxc151355 		s = "invalid value";
673147Sxc151355 		break;
683147Sxc151355 	case DLADM_STATUS_NOMEM:
693147Sxc151355 		s = "insufficient memory";
703147Sxc151355 		break;
713147Sxc151355 	case DLADM_STATUS_EXIST:
723147Sxc151355 		s = "object already exists";
733147Sxc151355 		break;
743147Sxc151355 	case DLADM_STATUS_LINKINVAL:
753147Sxc151355 		s = "invalid link";
763147Sxc151355 		break;
773147Sxc151355 	case DLADM_STATUS_PROPRDONLY:
783147Sxc151355 		s = "read-only property";
793147Sxc151355 		break;
803147Sxc151355 	case DLADM_STATUS_BADVALCNT:
813147Sxc151355 		s = "invalid number of values";
823147Sxc151355 		break;
833147Sxc151355 	case DLADM_STATUS_DBNOTFOUND:
843147Sxc151355 		s = "database not found";
853147Sxc151355 		break;
863147Sxc151355 	case DLADM_STATUS_DENIED:
873147Sxc151355 		s = "permission denied";
883147Sxc151355 		break;
893147Sxc151355 	case DLADM_STATUS_IOERR:
903147Sxc151355 		s = "I/O error";
913147Sxc151355 		break;
923448Sdh155122 	case DLADM_STATUS_TEMPONLY:
93*8275SEric Cheng 		s = "change cannot be persistent";
943448Sdh155122 		break;
953871Syz147064 	case DLADM_STATUS_TIMEDOUT:
963871Syz147064 		s = "operation timed out";
973871Syz147064 		break;
983871Syz147064 	case DLADM_STATUS_ISCONN:
993871Syz147064 		s = "already connected";
1003871Syz147064 		break;
1013871Syz147064 	case DLADM_STATUS_NOTCONN:
1023871Syz147064 		s = "not connected";
1033871Syz147064 		break;
1043871Syz147064 	case DLADM_STATUS_REPOSITORYINVAL:
1053871Syz147064 		s = "invalid configuration repository";
1063871Syz147064 		break;
1073871Syz147064 	case DLADM_STATUS_MACADDRINVAL:
1083871Syz147064 		s = "invalid MAC address";
1093871Syz147064 		break;
1103871Syz147064 	case DLADM_STATUS_KEYINVAL:
1113871Syz147064 		s = "invalid key";
1123871Syz147064 		break;
1135084Sjohnlev 	case DLADM_STATUS_INVALIDMACADDRLEN:
1145084Sjohnlev 		s = "invalid MAC address length";
1155084Sjohnlev 		break;
1165084Sjohnlev 	case DLADM_STATUS_INVALIDMACADDRTYPE:
1175084Sjohnlev 		s = "invalid MAC address type";
1185084Sjohnlev 		break;
1195895Syz147064 	case DLADM_STATUS_LINKBUSY:
1205895Syz147064 		s = "link busy";
1215895Syz147064 		break;
1225895Syz147064 	case DLADM_STATUS_VIDINVAL:
1235895Syz147064 		s = "invalid VLAN identifier";
1245084Sjohnlev 		break;
1255895Syz147064 	case DLADM_STATUS_TRYAGAIN:
1265895Syz147064 		s = "try again later";
1275084Sjohnlev 		break;
1285895Syz147064 	case DLADM_STATUS_NONOTIF:
1295895Syz147064 		s = "link notification is not supported";
1305084Sjohnlev 		break;
131*8275SEric Cheng 	case DLADM_STATUS_BADTIMEVAL:
132*8275SEric Cheng 		s = "invalid time range";
133*8275SEric Cheng 		break;
134*8275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDR:
135*8275SEric Cheng 		s = "invalid MAC address value";
136*8275SEric Cheng 		break;
137*8275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDRNIC:
138*8275SEric Cheng 		s = "MAC address reserved for use by underlying data-link";
139*8275SEric Cheng 		break;
140*8275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDRINUSE:
141*8275SEric Cheng 		s = "MAC address is already in use";
142*8275SEric Cheng 		break;
143*8275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTINVALID:
144*8275SEric Cheng 		s = "invalid factory MAC address slot";
145*8275SEric Cheng 		break;
146*8275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTUSED:
147*8275SEric Cheng 		s = "factory MAC address slot already used";
148*8275SEric Cheng 		break;
149*8275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTALLUSED:
150*8275SEric Cheng 		s = "all factory MAC address slots are in use";
151*8275SEric Cheng 		break;
152*8275SEric Cheng 	case DLADM_STATUS_MACFACTORYNOTSUP:
153*8275SEric Cheng 		s = "factory MAC address slots not supported";
154*8275SEric Cheng 		break;
155*8275SEric Cheng 	case DLADM_STATUS_INVALIDMACPREFIX:
156*8275SEric Cheng 		s = "Invalid MAC address prefix value";
157*8275SEric Cheng 		break;
158*8275SEric Cheng 	case DLADM_STATUS_INVALIDMACPREFIXLEN:
159*8275SEric Cheng 		s = "Invalid MAC address prefix length";
160*8275SEric Cheng 		break;
161*8275SEric Cheng 	case DLADM_STATUS_CPUMAX:
162*8275SEric Cheng 		s = "non-existent processor ID";
163*8275SEric Cheng 		break;
164*8275SEric Cheng 	case DLADM_STATUS_CPUERR:
165*8275SEric Cheng 		s = "could not determine processor status";
166*8275SEric Cheng 		break;
167*8275SEric Cheng 	case DLADM_STATUS_CPUNOTONLINE:
168*8275SEric Cheng 		s = "processor not online";
169*8275SEric Cheng 		break;
170*8275SEric Cheng 	case DLADM_STATUS_DB_NOTFOUND:
171*8275SEric Cheng 		s = "database not found";
172*8275SEric Cheng 		break;
173*8275SEric Cheng 	case DLADM_STATUS_DB_PARSE_ERR:
174*8275SEric Cheng 		s = "database parse error";
175*8275SEric Cheng 		break;
176*8275SEric Cheng 	case DLADM_STATUS_PROP_PARSE_ERR:
177*8275SEric Cheng 		s = "property parse error";
178*8275SEric Cheng 		break;
179*8275SEric Cheng 	case DLADM_STATUS_ATTR_PARSE_ERR:
180*8275SEric Cheng 		s = "attribute parse error";
181*8275SEric Cheng 		break;
182*8275SEric Cheng 	case DLADM_STATUS_FLOW_DB_ERR:
183*8275SEric Cheng 		s = "flow database error";
184*8275SEric Cheng 		break;
185*8275SEric Cheng 	case DLADM_STATUS_FLOW_DB_OPEN_ERR:
186*8275SEric Cheng 		s = "flow database open error";
187*8275SEric Cheng 		break;
188*8275SEric Cheng 	case DLADM_STATUS_FLOW_DB_PARSE_ERR:
189*8275SEric Cheng 		s = "flow database parse error";
190*8275SEric Cheng 		break;
191*8275SEric Cheng 	case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
192*8275SEric Cheng 		s = "flow property database parse error";
193*8275SEric Cheng 		break;
194*8275SEric Cheng 	case DLADM_STATUS_FLOW_ADD_ERR:
195*8275SEric Cheng 		s = "flow add error";
196*8275SEric Cheng 		break;
197*8275SEric Cheng 	case DLADM_STATUS_FLOW_WALK_ERR:
198*8275SEric Cheng 		s = "flow walk error";
199*8275SEric Cheng 		break;
200*8275SEric Cheng 	case DLADM_STATUS_FLOW_IDENTICAL:
201*8275SEric Cheng 		s = "a flow with identical attributes exists";
202*8275SEric Cheng 		break;
203*8275SEric Cheng 	case DLADM_STATUS_FLOW_INCOMPATIBLE:
204*8275SEric Cheng 		s = "flow(s) with incompatible attributes exists";
205*8275SEric Cheng 		break;
206*8275SEric Cheng 	case DLADM_STATUS_FLOW_EXISTS:
207*8275SEric Cheng 		s = "link still has flows";
208*8275SEric Cheng 		break;
209*8275SEric Cheng 	case DLADM_STATUS_PERSIST_FLOW_EXISTS:
210*8275SEric Cheng 		s = "persistent flow with the same name exists";
211*8275SEric Cheng 		break;
212*8275SEric Cheng 	case DLADM_STATUS_INVALID_IP:
213*8275SEric Cheng 		s = "invalid IP address";
214*8275SEric Cheng 		break;
215*8275SEric Cheng 	case DLADM_STATUS_INVALID_PREFIXLEN:
216*8275SEric Cheng 		s = "invalid IP prefix length";
217*8275SEric Cheng 		break;
218*8275SEric Cheng 	case DLADM_STATUS_INVALID_PROTOCOL:
219*8275SEric Cheng 		s = "invalid IP protocol";
220*8275SEric Cheng 		break;
221*8275SEric Cheng 	case DLADM_STATUS_INVALID_PORT:
222*8275SEric Cheng 		s = "invalid port number";
223*8275SEric Cheng 		break;
224*8275SEric Cheng 	case DLADM_STATUS_INVALID_DSF:
225*8275SEric Cheng 		s = "invalid dsfield";
226*8275SEric Cheng 		break;
227*8275SEric Cheng 	case DLADM_STATUS_INVALID_DSFMASK:
228*8275SEric Cheng 		s = "invalid dsfield mask";
229*8275SEric Cheng 		break;
230*8275SEric Cheng 	case DLADM_STATUS_INVALID_MACMARGIN:
231*8275SEric Cheng 		s = "MTU check failed, use lower MTU or -f option";
232*8275SEric Cheng 		break;
233*8275SEric Cheng 	case DLADM_STATUS_BADPROP:
234*8275SEric Cheng 		s = "invalid property";
235*8275SEric Cheng 		break;
236*8275SEric Cheng 	case DLADM_STATUS_MINMAXBW:
237*8275SEric Cheng 		s = "minimum value for maxbw is 1.2M";
238*8275SEric Cheng 		break;
239*8275SEric Cheng 	case DLADM_STATUS_NO_HWRINGS:
240*8275SEric Cheng 		s = "request hw rings failed";
241*8275SEric Cheng 		break;
2423147Sxc151355 	default:
2433184Smeem 		s = "<unknown error>";
2443184Smeem 		break;
2453147Sxc151355 	}
2463184Smeem 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
2473147Sxc151355 	return (buf);
2483147Sxc151355 }
2493147Sxc151355 
2503147Sxc151355 /*
2513147Sxc151355  * Convert a unix errno to a dladm_status_t.
2523147Sxc151355  * We only convert errnos that are likely to be encountered. All others
2533147Sxc151355  * are mapped to DLADM_STATUS_FAILED.
2543147Sxc151355  */
2553147Sxc151355 dladm_status_t
2563147Sxc151355 dladm_errno2status(int err)
2573147Sxc151355 {
2583147Sxc151355 	switch (err) {
2595903Ssowmini 	case 0:
2605903Ssowmini 		return (DLADM_STATUS_OK);
2613147Sxc151355 	case EINVAL:
2623147Sxc151355 		return (DLADM_STATUS_BADARG);
2633147Sxc151355 	case EEXIST:
2643147Sxc151355 		return (DLADM_STATUS_EXIST);
2653147Sxc151355 	case ENOENT:
2663147Sxc151355 		return (DLADM_STATUS_NOTFOUND);
2673147Sxc151355 	case ENOSPC:
2683147Sxc151355 		return (DLADM_STATUS_TOOSMALL);
2693147Sxc151355 	case ENOMEM:
2703147Sxc151355 		return (DLADM_STATUS_NOMEM);
2713147Sxc151355 	case ENOTSUP:
2723147Sxc151355 		return (DLADM_STATUS_NOTSUP);
2735895Syz147064 	case ENETDOWN:
2745895Syz147064 		return (DLADM_STATUS_NONOTIF);
2753147Sxc151355 	case EACCES:
2767408SSebastien.Roy@Sun.COM 	case EPERM:
2773147Sxc151355 		return (DLADM_STATUS_DENIED);
2783147Sxc151355 	case EIO:
2793147Sxc151355 		return (DLADM_STATUS_IOERR);
2805084Sjohnlev 	case EBUSY:
2815895Syz147064 		return (DLADM_STATUS_LINKBUSY);
2825895Syz147064 	case EAGAIN:
2835895Syz147064 		return (DLADM_STATUS_TRYAGAIN);
284*8275SEric Cheng 	case ENOTEMPTY:
285*8275SEric Cheng 		return (DLADM_STATUS_FLOW_EXISTS);
286*8275SEric Cheng 	case EOPNOTSUPP:
287*8275SEric Cheng 		return (DLADM_STATUS_FLOW_INCOMPATIBLE);
288*8275SEric Cheng 	case EALREADY:
289*8275SEric Cheng 		return (DLADM_STATUS_FLOW_IDENTICAL);
2903147Sxc151355 	default:
2913147Sxc151355 		return (DLADM_STATUS_FAILED);
2923147Sxc151355 	}
2933147Sxc151355 }
2943147Sxc151355 
295*8275SEric Cheng dladm_status_t
296*8275SEric Cheng dladm_str2bw(char *oarg, uint64_t *bw)
297*8275SEric Cheng {
298*8275SEric Cheng 	char		*endp = NULL;
299*8275SEric Cheng 	int64_t		n;
300*8275SEric Cheng 	int		mult = 1;
301*8275SEric Cheng 
302*8275SEric Cheng 	n = strtoull(oarg, &endp, 10);
303*8275SEric Cheng 
304*8275SEric Cheng 	if ((errno != 0) || (strlen(endp) > 1))
305*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
306*8275SEric Cheng 
307*8275SEric Cheng 	if (n < 0)
308*8275SEric Cheng 		return (DLADM_STATUS_BADVAL);
309*8275SEric Cheng 
310*8275SEric Cheng 	switch (*endp) {
311*8275SEric Cheng 	case 'k':
312*8275SEric Cheng 	case 'K':
313*8275SEric Cheng 		mult = 1000;
314*8275SEric Cheng 		break;
315*8275SEric Cheng 	case 'm':
316*8275SEric Cheng 	case 'M':
317*8275SEric Cheng 	case '\0':
318*8275SEric Cheng 		mult = 1000000;
319*8275SEric Cheng 		break;
320*8275SEric Cheng 	case 'g':
321*8275SEric Cheng 	case 'G':
322*8275SEric Cheng 		mult = 1000000000;
323*8275SEric Cheng 		break;
324*8275SEric Cheng 	case '%':
325*8275SEric Cheng 		/*
326*8275SEric Cheng 		 * percentages not supported for now,
327*8275SEric Cheng 		 * see RFE 6540675
328*8275SEric Cheng 		 */
329*8275SEric Cheng 		return (DLADM_STATUS_NOTSUP);
330*8275SEric Cheng 	default:
331*8275SEric Cheng 		return (DLADM_STATUS_BADVAL);
332*8275SEric Cheng 	}
333*8275SEric Cheng 
334*8275SEric Cheng 	*bw = n * mult;
335*8275SEric Cheng 
336*8275SEric Cheng 	/* check for overflow */
337*8275SEric Cheng 	if (*bw / mult != n)
338*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
339*8275SEric Cheng 
340*8275SEric Cheng 	return (DLADM_STATUS_OK);
341*8275SEric Cheng }
342*8275SEric Cheng 
343*8275SEric Cheng /*
344*8275SEric Cheng  * Convert bandwidth in bps to a string in mpbs.  For values greater
345*8275SEric Cheng  * than 1mbps or 1000000, print a whole mbps value.  For values that
346*8275SEric Cheng  * have fractional Mbps in whole Kbps , print the bandwidth in a manner
347*8275SEric Cheng  * simlilar to a floating point format.
348*8275SEric Cheng  *
349*8275SEric Cheng  *        bps       string
350*8275SEric Cheng  *          0            0
351*8275SEric Cheng  *        100            0
352*8275SEric Cheng  *       2000        0.002
353*8275SEric Cheng  *     431000        0.431
354*8275SEric Cheng  *    1000000            1
355*8275SEric Cheng  *    1030000        1.030
356*8275SEric Cheng  *  100000000          100
357*8275SEric Cheng  */
358*8275SEric Cheng const char *
359*8275SEric Cheng dladm_bw2str(int64_t bw, char *buf)
360*8275SEric Cheng {
361*8275SEric Cheng 	int kbps, mbps;
362*8275SEric Cheng 
363*8275SEric Cheng 	kbps = (bw%1000000)/1000;
364*8275SEric Cheng 	mbps = bw/1000000;
365*8275SEric Cheng 	if (kbps != 0) {
366*8275SEric Cheng 		if (mbps == 0)
367*8275SEric Cheng 			(void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
368*8275SEric Cheng 		else
369*8275SEric Cheng 			(void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
370*8275SEric Cheng 			    kbps);
371*8275SEric Cheng 	} else {
372*8275SEric Cheng 		(void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
373*8275SEric Cheng 	}
374*8275SEric Cheng 
375*8275SEric Cheng 	return (buf);
376*8275SEric Cheng }
377*8275SEric Cheng 
3785895Syz147064 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
3793147Sxc151355 
3803147Sxc151355 static int
3813147Sxc151355 i_dladm_lock_db(const char *lock_file, short type)
3823147Sxc151355 {
3833147Sxc151355 	int	lock_fd;
3845895Syz147064 	struct	flock lock;
3853147Sxc151355 
3863147Sxc151355 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
3873147Sxc151355 	    LOCK_DB_PERMS)) < 0)
3883147Sxc151355 		return (-1);
3893147Sxc151355 
3903147Sxc151355 	lock.l_type = type;
3913147Sxc151355 	lock.l_whence = SEEK_SET;
3923147Sxc151355 	lock.l_start = 0;
3933147Sxc151355 	lock.l_len = 0;
3943147Sxc151355 
3953147Sxc151355 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
3963147Sxc151355 		int err = errno;
3973147Sxc151355 
3983147Sxc151355 		(void) close(lock_fd);
3993147Sxc151355 		(void) unlink(lock_file);
4003147Sxc151355 		errno = err;
4013147Sxc151355 		return (-1);
4023147Sxc151355 	}
4033147Sxc151355 	return (lock_fd);
4043147Sxc151355 }
4053147Sxc151355 
4063147Sxc151355 static void
4073147Sxc151355 i_dladm_unlock_db(const char *lock_file, int fd)
4083147Sxc151355 {
4093147Sxc151355 	struct flock lock;
4103147Sxc151355 
4113147Sxc151355 	if (fd < 0)
4123147Sxc151355 		return;
4133147Sxc151355 
4143147Sxc151355 	lock.l_type = F_UNLCK;
4153147Sxc151355 	lock.l_whence = SEEK_SET;
4163147Sxc151355 	lock.l_start = 0;
4173147Sxc151355 	lock.l_len = 0;
4183147Sxc151355 
4193147Sxc151355 	(void) fcntl(fd, F_SETLKW, &lock);
4203147Sxc151355 	(void) close(fd);
4213147Sxc151355 	(void) unlink(lock_file);
4223147Sxc151355 }
4233147Sxc151355 
4245895Syz147064 /*
4255895Syz147064  * Given a link class, returns its class string.
4265895Syz147064  */
4275895Syz147064 const char *
4285895Syz147064 dladm_class2str(datalink_class_t class, char *buf)
4295895Syz147064 {
4305895Syz147064 	const char *s;
4315895Syz147064 
4325895Syz147064 	switch (class) {
4335895Syz147064 	case DATALINK_CLASS_PHYS:
4345895Syz147064 		s = "phys";
4355895Syz147064 		break;
4365895Syz147064 	case DATALINK_CLASS_VLAN:
4375895Syz147064 		s = "vlan";
4385895Syz147064 		break;
4395895Syz147064 	case DATALINK_CLASS_AGGR:
4405895Syz147064 		s = "aggr";
4415895Syz147064 		break;
4425895Syz147064 	case DATALINK_CLASS_VNIC:
4435895Syz147064 		s = "vnic";
4445895Syz147064 		break;
445*8275SEric Cheng 	case DATALINK_CLASS_ETHERSTUB:
446*8275SEric Cheng 		s = "etherstub";
447*8275SEric Cheng 		break;
4485895Syz147064 	default:
4495895Syz147064 		s = "unknown";
4505895Syz147064 		break;
4515895Syz147064 	}
4525895Syz147064 
4535895Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
4545895Syz147064 	return (buf);
4555895Syz147064 }
4565895Syz147064 
4575895Syz147064 /*
4585895Syz147064  * Given a physical link media type, returns its media type string.
4595895Syz147064  */
4605895Syz147064 const char *
4615895Syz147064 dladm_media2str(uint32_t media, char *buf)
4625895Syz147064 {
4635895Syz147064 	const char *s;
4645895Syz147064 
4655895Syz147064 	switch (media) {
4665895Syz147064 	case DL_ETHER:
4675895Syz147064 		s = "Ethernet";
4685895Syz147064 		break;
4695895Syz147064 	case DL_WIFI:
4705895Syz147064 		s = "WiFi";
4715895Syz147064 		break;
4725895Syz147064 	case DL_IB:
4735895Syz147064 		s = "Infiniband";
4745895Syz147064 		break;
4755895Syz147064 	case DL_IPV4:
4765895Syz147064 		s = "IPv4Tunnel";
4775895Syz147064 		break;
4785895Syz147064 	case DL_IPV6:
4795895Syz147064 		s = "IPv6Tunnel";
4805895Syz147064 		break;
4815895Syz147064 	case DL_CSMACD:
4825895Syz147064 		s = "CSMA/CD";
4835895Syz147064 		break;
4845895Syz147064 	case DL_TPB:
4855895Syz147064 		s = "TokenBus";
4865895Syz147064 		break;
4875895Syz147064 	case DL_TPR:
4885895Syz147064 		s = "TokenRing";
4895895Syz147064 		break;
4905895Syz147064 	case DL_METRO:
4915895Syz147064 		s = "MetroNet";
4925895Syz147064 		break;
4935895Syz147064 	case DL_HDLC:
4945895Syz147064 		s = "HDLC";
4955895Syz147064 		break;
4965895Syz147064 	case DL_CHAR:
4975895Syz147064 		s = "SyncCharacter";
4985895Syz147064 		break;
4995895Syz147064 	case DL_CTCA:
5005895Syz147064 		s = "CTCA";
5015895Syz147064 		break;
5025895Syz147064 	case DL_FDDI:
5035895Syz147064 		s = "FDDI";
5045895Syz147064 		break;
5055895Syz147064 	case DL_FC:
5065895Syz147064 		s = "FiberChannel";
5075895Syz147064 		break;
5085895Syz147064 	case DL_ATM:
5095895Syz147064 		s = "ATM";
5105895Syz147064 		break;
5115895Syz147064 	case DL_IPATM:
5125895Syz147064 		s = "ATM(ClassicIP)";
5135895Syz147064 		break;
5145895Syz147064 	case DL_X25:
5155895Syz147064 		s = "X.25";
5165895Syz147064 		break;
5175895Syz147064 	case DL_IPX25:
5185895Syz147064 		s = "X.25(ClassicIP)";
5195895Syz147064 		break;
5205895Syz147064 	case DL_ISDN:
5215895Syz147064 		s = "ISDN";
5225895Syz147064 		break;
5235895Syz147064 	case DL_HIPPI:
5245895Syz147064 		s = "HIPPI";
5255895Syz147064 		break;
5265895Syz147064 	case DL_100VG:
5275895Syz147064 		s = "100BaseVGEthernet";
5285895Syz147064 		break;
5295895Syz147064 	case DL_100VGTPR:
5305895Syz147064 		s = "100BaseVGTokenRing";
5315895Syz147064 		break;
5325895Syz147064 	case DL_ETH_CSMA:
5335895Syz147064 		s = "IEEE802.3";
5345895Syz147064 		break;
5355895Syz147064 	case DL_100BT:
5365895Syz147064 		s = "100BaseT";
5375895Syz147064 		break;
5385895Syz147064 	case DL_FRAME:
5395895Syz147064 		s = "FrameRelay";
5405895Syz147064 		break;
5415895Syz147064 	case DL_MPFRAME:
5425895Syz147064 		s = "MPFrameRelay";
5435895Syz147064 		break;
5445895Syz147064 	case DL_ASYNC:
5455895Syz147064 		s = "AsyncCharacter";
5465895Syz147064 		break;
5478023SPhil.Kirk@Sun.COM 	case DL_IPNET:
5488023SPhil.Kirk@Sun.COM 		s = "IPNET";
5498023SPhil.Kirk@Sun.COM 		break;
5505895Syz147064 	default:
5515895Syz147064 		s = "--";
5525895Syz147064 		break;
5535895Syz147064 	}
5545895Syz147064 
5555895Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
5565895Syz147064 	return (buf);
5575895Syz147064 }
5585895Syz147064 
5593147Sxc151355 dladm_status_t
5603147Sxc151355 i_dladm_rw_db(const char *db_file, mode_t db_perms,
5613147Sxc151355     dladm_status_t (*process_db)(void *, FILE *, FILE *),
5623147Sxc151355     void *arg, boolean_t writeop)
5633147Sxc151355 {
5643147Sxc151355 	dladm_status_t	status = DLADM_STATUS_OK;
5653147Sxc151355 	FILE		*fp, *nfp = NULL;
5663147Sxc151355 	char		lock[MAXPATHLEN];
5673147Sxc151355 	char		file[MAXPATHLEN];
5683147Sxc151355 	char		newfile[MAXPATHLEN];
5693147Sxc151355 	char		*db_basename;
5703147Sxc151355 	int		nfd, lock_fd;
5713147Sxc151355 
5723147Sxc151355 	/*
5733147Sxc151355 	 * If we are called from a boot script such as net-physical,
5743147Sxc151355 	 * it's quite likely that the root fs is still not writable.
5753147Sxc151355 	 * For this case, it's ok for the lock creation to fail since
5763147Sxc151355 	 * no one else could be accessing our configuration file.
5773147Sxc151355 	 */
5783147Sxc151355 	db_basename = strrchr(db_file, '/');
5793147Sxc151355 	if (db_basename == NULL || db_basename[1] == '\0')
5803147Sxc151355 		return (dladm_errno2status(EINVAL));
5813147Sxc151355 	db_basename++;
5823147Sxc151355 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
5833147Sxc151355 	if ((lock_fd = i_dladm_lock_db
5843147Sxc151355 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
5853147Sxc151355 		return (dladm_errno2status(errno));
5863147Sxc151355 
5873147Sxc151355 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
5883147Sxc151355 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
5893147Sxc151355 		int	err = errno;
5903147Sxc151355 
5913147Sxc151355 		i_dladm_unlock_db(lock, lock_fd);
5923147Sxc151355 		if (err == ENOENT)
5933147Sxc151355 			return (DLADM_STATUS_DBNOTFOUND);
5943147Sxc151355 
5953147Sxc151355 		return (dladm_errno2status(err));
5963147Sxc151355 	}
5973147Sxc151355 
5983147Sxc151355 	if (writeop) {
5993147Sxc151355 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
6003147Sxc151355 		    dladm_rootdir, db_file);
6013147Sxc151355 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
6023147Sxc151355 		    db_perms)) < 0) {
6033147Sxc151355 			(void) fclose(fp);
6043147Sxc151355 			i_dladm_unlock_db(lock, lock_fd);
6053147Sxc151355 			return (dladm_errno2status(errno));
6063147Sxc151355 		}
6073147Sxc151355 
6083147Sxc151355 		if ((nfp = fdopen(nfd, "w")) == NULL) {
6093147Sxc151355 			(void) close(nfd);
6103147Sxc151355 			(void) fclose(fp);
6113147Sxc151355 			(void) unlink(newfile);
6123147Sxc151355 			i_dladm_unlock_db(lock, lock_fd);
6133147Sxc151355 			return (dladm_errno2status(errno));
6143147Sxc151355 		}
6153147Sxc151355 	}
6163147Sxc151355 	status = (*process_db)(arg, fp, nfp);
6173147Sxc151355 	if (!writeop || status != DLADM_STATUS_OK)
6183147Sxc151355 		goto done;
6193147Sxc151355 
6203147Sxc151355 	/*
6213147Sxc151355 	 * Configuration files need to be owned by the 'dladm' user.
6223147Sxc151355 	 * If we are invoked by root, the file ownership needs to be fixed.
6233147Sxc151355 	 */
6243147Sxc151355 	if (getuid() == 0 || geteuid() == 0) {
6256173Syz147064 		if (fchown(nfd, UID_DLADM, GID_SYS) < 0) {
6263147Sxc151355 			status = dladm_errno2status(errno);
6273147Sxc151355 			goto done;
6283147Sxc151355 		}
6293147Sxc151355 	}
6303147Sxc151355 
6313147Sxc151355 	if (fflush(nfp) == EOF) {
6323147Sxc151355 		status = dladm_errno2status(errno);
6333147Sxc151355 		goto done;
6343147Sxc151355 	}
6353147Sxc151355 	(void) fclose(fp);
6363147Sxc151355 	(void) fclose(nfp);
6373147Sxc151355 
6383147Sxc151355 	if (rename(newfile, file) < 0) {
6393147Sxc151355 		(void) unlink(newfile);
6403147Sxc151355 		i_dladm_unlock_db(lock, lock_fd);
6413147Sxc151355 		return (dladm_errno2status(errno));
6423147Sxc151355 	}
6433147Sxc151355 
6443147Sxc151355 	i_dladm_unlock_db(lock, lock_fd);
6453147Sxc151355 	return (DLADM_STATUS_OK);
6463147Sxc151355 
6473147Sxc151355 done:
6483147Sxc151355 	if (nfp != NULL) {
6493147Sxc151355 		(void) fclose(nfp);
6503147Sxc151355 		if (status != DLADM_STATUS_OK)
6513147Sxc151355 			(void) unlink(newfile);
6523147Sxc151355 	}
6533147Sxc151355 	(void) fclose(fp);
6543147Sxc151355 	i_dladm_unlock_db(lock, lock_fd);
6553147Sxc151355 	return (status);
6563147Sxc151355 }
6573147Sxc151355 
6583147Sxc151355 dladm_status_t
6593147Sxc151355 dladm_set_rootdir(const char *rootdir)
6603147Sxc151355 {
6613147Sxc151355 	DIR	*dp;
6623147Sxc151355 
6633147Sxc151355 	if (rootdir == NULL || *rootdir != '/' ||
6643147Sxc151355 	    (dp = opendir(rootdir)) == NULL)
6653147Sxc151355 		return (DLADM_STATUS_BADARG);
6663147Sxc151355 
6673147Sxc151355 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
6683147Sxc151355 	(void) closedir(dp);
6693147Sxc151355 	return (DLADM_STATUS_OK);
6703147Sxc151355 }
6715895Syz147064 
6725895Syz147064 boolean_t
6735895Syz147064 dladm_valid_linkname(const char *link)
6745895Syz147064 {
6755895Syz147064 	size_t		len = strlen(link);
6765895Syz147064 	const char	*cp;
6775895Syz147064 
6785895Syz147064 	if (len + 1 >= MAXLINKNAMELEN)
6795895Syz147064 		return (B_FALSE);
6805895Syz147064 
6815895Syz147064 	/*
6825895Syz147064 	 * The link name cannot start with a digit and must end with a digit.
6835895Syz147064 	 */
6845895Syz147064 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
6855895Syz147064 		return (B_FALSE);
6865895Syz147064 
6875895Syz147064 	/*
6885895Syz147064 	 * The legal characters in a link name are:
6895895Syz147064 	 * alphanumeric (a-z,  A-Z,  0-9), and the underscore ('_').
6905895Syz147064 	 */
6915895Syz147064 	for (cp = link; *cp != '\0'; cp++) {
6925895Syz147064 		if ((isalnum(*cp) == 0) && (*cp != '_'))
6935895Syz147064 			return (B_FALSE);
6945895Syz147064 	}
6955895Syz147064 
6965895Syz147064 	return (B_TRUE);
6975895Syz147064 }
698*8275SEric Cheng 
699*8275SEric Cheng /*
700*8275SEric Cheng  * Convert priority string to a value.
701*8275SEric Cheng  */
702*8275SEric Cheng dladm_status_t
703*8275SEric Cheng dladm_str2pri(char *token, mac_priority_level_t *pri)
704*8275SEric Cheng {
705*8275SEric Cheng 	if (strlen(token) == strlen("low") &&
706*8275SEric Cheng 	    strncasecmp(token, "low", strlen("low")) == 0) {
707*8275SEric Cheng 		*pri = MPL_LOW;
708*8275SEric Cheng 	} else if (strlen(token) == strlen("medium") &&
709*8275SEric Cheng 	    strncasecmp(token, "medium", strlen("medium")) == 0) {
710*8275SEric Cheng 		*pri = MPL_MEDIUM;
711*8275SEric Cheng 	} else if (strlen(token) == strlen("high") &&
712*8275SEric Cheng 	    strncasecmp(token, "high", strlen("high")) == 0) {
713*8275SEric Cheng 		*pri = MPL_HIGH;
714*8275SEric Cheng 	} else {
715*8275SEric Cheng 		return (DLADM_STATUS_BADVAL);
716*8275SEric Cheng 	}
717*8275SEric Cheng 	return (DLADM_STATUS_OK);
718*8275SEric Cheng }
719*8275SEric Cheng 
720*8275SEric Cheng /*
721*8275SEric Cheng  * Convert priority value to a string.
722*8275SEric Cheng  */
723*8275SEric Cheng const char *
724*8275SEric Cheng dladm_pri2str(mac_priority_level_t pri, char *buf)
725*8275SEric Cheng {
726*8275SEric Cheng 	const char	*s;
727*8275SEric Cheng 
728*8275SEric Cheng 	switch (pri) {
729*8275SEric Cheng 	case MPL_LOW:
730*8275SEric Cheng 		s = "low";
731*8275SEric Cheng 		break;
732*8275SEric Cheng 	case MPL_MEDIUM:
733*8275SEric Cheng 		s = "medium";
734*8275SEric Cheng 		break;
735*8275SEric Cheng 	case MPL_HIGH:
736*8275SEric Cheng 		s = "high";
737*8275SEric Cheng 		break;
738*8275SEric Cheng 	default:
739*8275SEric Cheng 		s = "--";
740*8275SEric Cheng 		break;
741*8275SEric Cheng 	}
742*8275SEric Cheng 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
743*8275SEric Cheng 	return (buf);
744*8275SEric Cheng }
745*8275SEric Cheng 
746*8275SEric Cheng void
747*8275SEric Cheng dladm_free_args(dladm_arg_list_t *list)
748*8275SEric Cheng {
749*8275SEric Cheng 	if (list != NULL) {
750*8275SEric Cheng 		free(list->al_buf);
751*8275SEric Cheng 		free(list);
752*8275SEric Cheng 	}
753*8275SEric Cheng }
754*8275SEric Cheng 
755*8275SEric Cheng dladm_status_t
756*8275SEric Cheng dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
757*8275SEric Cheng {
758*8275SEric Cheng 	dladm_arg_list_t	*list;
759*8275SEric Cheng 	dladm_arg_info_t	*aip;
760*8275SEric Cheng 	char			*buf, *curr;
761*8275SEric Cheng 	int			len, i;
762*8275SEric Cheng 
763*8275SEric Cheng 	list = malloc(sizeof (dladm_arg_list_t));
764*8275SEric Cheng 	if (list == NULL)
765*8275SEric Cheng 		return (dladm_errno2status(errno));
766*8275SEric Cheng 
767*8275SEric Cheng 	list->al_count = 0;
768*8275SEric Cheng 	list->al_buf = buf = strdup(str);
769*8275SEric Cheng 	if (buf == NULL)
770*8275SEric Cheng 		return (dladm_errno2status(errno));
771*8275SEric Cheng 
772*8275SEric Cheng 	curr = buf;
773*8275SEric Cheng 	len = strlen(buf);
774*8275SEric Cheng 	aip = NULL;
775*8275SEric Cheng 	for (i = 0; i < len; i++) {
776*8275SEric Cheng 		char		c = buf[i];
777*8275SEric Cheng 		boolean_t	match = (c == '=' || c == ',');
778*8275SEric Cheng 
779*8275SEric Cheng 		if (!match && i != len - 1)
780*8275SEric Cheng 			continue;
781*8275SEric Cheng 
782*8275SEric Cheng 		if (match) {
783*8275SEric Cheng 			buf[i] = '\0';
784*8275SEric Cheng 			if (*curr == '\0')
785*8275SEric Cheng 				goto fail;
786*8275SEric Cheng 		}
787*8275SEric Cheng 
788*8275SEric Cheng 		if (aip != NULL && c != '=') {
789*8275SEric Cheng 			if (aip->ai_count > DLADM_MAX_ARG_VALS)
790*8275SEric Cheng 				goto fail;
791*8275SEric Cheng 
792*8275SEric Cheng 			if (novalues)
793*8275SEric Cheng 				goto fail;
794*8275SEric Cheng 
795*8275SEric Cheng 			aip->ai_val[aip->ai_count] = curr;
796*8275SEric Cheng 			aip->ai_count++;
797*8275SEric Cheng 		} else {
798*8275SEric Cheng 			if (list->al_count > DLADM_MAX_ARG_VALS)
799*8275SEric Cheng 				goto fail;
800*8275SEric Cheng 
801*8275SEric Cheng 			aip = &list->al_info[list->al_count];
802*8275SEric Cheng 			aip->ai_name = curr;
803*8275SEric Cheng 			aip->ai_count = 0;
804*8275SEric Cheng 			list->al_count++;
805*8275SEric Cheng 			if (c == ',')
806*8275SEric Cheng 				aip = NULL;
807*8275SEric Cheng 		}
808*8275SEric Cheng 		curr = buf + i + 1;
809*8275SEric Cheng 	}
810*8275SEric Cheng 
811*8275SEric Cheng 	*listp = list;
812*8275SEric Cheng 	return (DLADM_STATUS_OK);
813*8275SEric Cheng 
814*8275SEric Cheng fail:
815*8275SEric Cheng 	dladm_free_args(list);
816*8275SEric Cheng 	return (DLADM_STATUS_FAILED);
817*8275SEric Cheng }
818