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 /*
22*9055SMichael.Lim@Sun.COM  * Copyright 2009 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>
328275SEric Cheng #include <stdlib.h>
336173Syz147064 #include <sys/param.h>
343147Sxc151355 #include <sys/stat.h>
358453SAnurag.Maskey@Sun.COM #include <sys/dld.h>
363147Sxc151355 #include <libdladm_impl.h>
373184Smeem #include <libintl.h>
385895Syz147064 #include <libdlpi.h>
39733Skrgopi 
408453SAnurag.Maskey@Sun.COM static char	dladm_rootdir[MAXPATHLEN] = "/";
418453SAnurag.Maskey@Sun.COM 
428453SAnurag.Maskey@Sun.COM dladm_status_t
438453SAnurag.Maskey@Sun.COM dladm_open(dladm_handle_t *handle)
448453SAnurag.Maskey@Sun.COM {
458453SAnurag.Maskey@Sun.COM 	int dld_fd;
468453SAnurag.Maskey@Sun.COM 
478453SAnurag.Maskey@Sun.COM 	if (handle == NULL)
488453SAnurag.Maskey@Sun.COM 		return (DLADM_STATUS_BADARG);
498453SAnurag.Maskey@Sun.COM 
508453SAnurag.Maskey@Sun.COM 	if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
518453SAnurag.Maskey@Sun.COM 		return (dladm_errno2status(errno));
528453SAnurag.Maskey@Sun.COM 
538453SAnurag.Maskey@Sun.COM 	/*
548453SAnurag.Maskey@Sun.COM 	 * Don't open DLMGMT_DOOR now.  dlmgmtd(1M) is not able to
558453SAnurag.Maskey@Sun.COM 	 * open the door when the dladm handle is opened because the
568453SAnurag.Maskey@Sun.COM 	 * door hasn't been created yet at that time.  Thus, we must
578453SAnurag.Maskey@Sun.COM 	 * open it on-demand in dladm_door_fd().  Move the open()
588453SAnurag.Maskey@Sun.COM 	 * to dladm_door_fd() for all cases.
598453SAnurag.Maskey@Sun.COM 	 */
608453SAnurag.Maskey@Sun.COM 
618453SAnurag.Maskey@Sun.COM 	if ((*handle = malloc(sizeof (struct dladm_handle))) == NULL) {
628453SAnurag.Maskey@Sun.COM 		(void) close(dld_fd);
638453SAnurag.Maskey@Sun.COM 		return (DLADM_STATUS_NOMEM);
648453SAnurag.Maskey@Sun.COM 	}
658453SAnurag.Maskey@Sun.COM 
668453SAnurag.Maskey@Sun.COM 	(*handle)->dld_fd = dld_fd;
678453SAnurag.Maskey@Sun.COM 	(*handle)->door_fd = -1;
688453SAnurag.Maskey@Sun.COM 
698453SAnurag.Maskey@Sun.COM 	return (DLADM_STATUS_OK);
708453SAnurag.Maskey@Sun.COM }
718453SAnurag.Maskey@Sun.COM 
728453SAnurag.Maskey@Sun.COM void
738453SAnurag.Maskey@Sun.COM dladm_close(dladm_handle_t handle)
748453SAnurag.Maskey@Sun.COM {
758453SAnurag.Maskey@Sun.COM 	if (handle != NULL) {
768453SAnurag.Maskey@Sun.COM 		(void) close(handle->dld_fd);
778453SAnurag.Maskey@Sun.COM 		if (handle->door_fd != -1)
788453SAnurag.Maskey@Sun.COM 			(void) close(handle->door_fd);
798453SAnurag.Maskey@Sun.COM 		free(handle);
808453SAnurag.Maskey@Sun.COM 	}
818453SAnurag.Maskey@Sun.COM }
828453SAnurag.Maskey@Sun.COM 
838453SAnurag.Maskey@Sun.COM int
848453SAnurag.Maskey@Sun.COM dladm_dld_fd(dladm_handle_t handle)
858453SAnurag.Maskey@Sun.COM {
868453SAnurag.Maskey@Sun.COM 	return (handle->dld_fd);
878453SAnurag.Maskey@Sun.COM }
888453SAnurag.Maskey@Sun.COM 
898453SAnurag.Maskey@Sun.COM /*
908453SAnurag.Maskey@Sun.COM  * If DLMGMT_DOOR hasn't been opened in the handle yet, open it.
918453SAnurag.Maskey@Sun.COM  */
928453SAnurag.Maskey@Sun.COM dladm_status_t
938453SAnurag.Maskey@Sun.COM dladm_door_fd(dladm_handle_t handle, int *door_fd)
948453SAnurag.Maskey@Sun.COM {
958453SAnurag.Maskey@Sun.COM 	int fd;
968453SAnurag.Maskey@Sun.COM 
978453SAnurag.Maskey@Sun.COM 	if (handle->door_fd == -1) {
988453SAnurag.Maskey@Sun.COM 		if ((fd = open(DLMGMT_DOOR, O_RDONLY)) < 0)
998453SAnurag.Maskey@Sun.COM 			return (dladm_errno2status(errno));
1008453SAnurag.Maskey@Sun.COM 		handle->door_fd = fd;
1018453SAnurag.Maskey@Sun.COM 	}
1028453SAnurag.Maskey@Sun.COM 	*door_fd = handle->door_fd;
1038453SAnurag.Maskey@Sun.COM 
1048453SAnurag.Maskey@Sun.COM 	return (DLADM_STATUS_OK);
1058453SAnurag.Maskey@Sun.COM }
1063147Sxc151355 
1073147Sxc151355 const char *
1083147Sxc151355 dladm_status2str(dladm_status_t status, char *buf)
1093147Sxc151355 {
1103147Sxc151355 	const char	*s;
1113147Sxc151355 
1123147Sxc151355 	switch (status) {
1133147Sxc151355 	case DLADM_STATUS_OK:
1143147Sxc151355 		s = "ok";
1153147Sxc151355 		break;
1163147Sxc151355 	case DLADM_STATUS_BADARG:
1173147Sxc151355 		s = "invalid argument";
1183147Sxc151355 		break;
1193147Sxc151355 	case DLADM_STATUS_FAILED:
1203147Sxc151355 		s = "operation failed";
1213147Sxc151355 		break;
1223147Sxc151355 	case DLADM_STATUS_TOOSMALL:
1233147Sxc151355 		s = "buffer size too small";
1243147Sxc151355 		break;
1253147Sxc151355 	case DLADM_STATUS_NOTSUP:
1263147Sxc151355 		s = "operation not supported";
1273147Sxc151355 		break;
1283147Sxc151355 	case DLADM_STATUS_NOTFOUND:
1293147Sxc151355 		s = "object not found";
1303147Sxc151355 		break;
1313147Sxc151355 	case DLADM_STATUS_BADVAL:
1323147Sxc151355 		s = "invalid value";
1333147Sxc151355 		break;
1343147Sxc151355 	case DLADM_STATUS_NOMEM:
1353147Sxc151355 		s = "insufficient memory";
1363147Sxc151355 		break;
1373147Sxc151355 	case DLADM_STATUS_EXIST:
1383147Sxc151355 		s = "object already exists";
1393147Sxc151355 		break;
1403147Sxc151355 	case DLADM_STATUS_LINKINVAL:
1413147Sxc151355 		s = "invalid link";
1423147Sxc151355 		break;
1433147Sxc151355 	case DLADM_STATUS_PROPRDONLY:
1443147Sxc151355 		s = "read-only property";
1453147Sxc151355 		break;
1463147Sxc151355 	case DLADM_STATUS_BADVALCNT:
1473147Sxc151355 		s = "invalid number of values";
1483147Sxc151355 		break;
1493147Sxc151355 	case DLADM_STATUS_DBNOTFOUND:
1503147Sxc151355 		s = "database not found";
1513147Sxc151355 		break;
1523147Sxc151355 	case DLADM_STATUS_DENIED:
1533147Sxc151355 		s = "permission denied";
1543147Sxc151355 		break;
1553147Sxc151355 	case DLADM_STATUS_IOERR:
1563147Sxc151355 		s = "I/O error";
1573147Sxc151355 		break;
1583448Sdh155122 	case DLADM_STATUS_TEMPONLY:
1598275SEric Cheng 		s = "change cannot be persistent";
1603448Sdh155122 		break;
1613871Syz147064 	case DLADM_STATUS_TIMEDOUT:
1623871Syz147064 		s = "operation timed out";
1633871Syz147064 		break;
1643871Syz147064 	case DLADM_STATUS_ISCONN:
1653871Syz147064 		s = "already connected";
1663871Syz147064 		break;
1673871Syz147064 	case DLADM_STATUS_NOTCONN:
1683871Syz147064 		s = "not connected";
1693871Syz147064 		break;
1703871Syz147064 	case DLADM_STATUS_REPOSITORYINVAL:
1713871Syz147064 		s = "invalid configuration repository";
1723871Syz147064 		break;
1733871Syz147064 	case DLADM_STATUS_MACADDRINVAL:
1743871Syz147064 		s = "invalid MAC address";
1753871Syz147064 		break;
1763871Syz147064 	case DLADM_STATUS_KEYINVAL:
1773871Syz147064 		s = "invalid key";
1783871Syz147064 		break;
1795084Sjohnlev 	case DLADM_STATUS_INVALIDMACADDRLEN:
1805084Sjohnlev 		s = "invalid MAC address length";
1815084Sjohnlev 		break;
1825084Sjohnlev 	case DLADM_STATUS_INVALIDMACADDRTYPE:
1835084Sjohnlev 		s = "invalid MAC address type";
1845084Sjohnlev 		break;
1855895Syz147064 	case DLADM_STATUS_LINKBUSY:
1865895Syz147064 		s = "link busy";
1875895Syz147064 		break;
1885895Syz147064 	case DLADM_STATUS_VIDINVAL:
1895895Syz147064 		s = "invalid VLAN identifier";
1905084Sjohnlev 		break;
1915895Syz147064 	case DLADM_STATUS_TRYAGAIN:
1925895Syz147064 		s = "try again later";
1935084Sjohnlev 		break;
1945895Syz147064 	case DLADM_STATUS_NONOTIF:
1955895Syz147064 		s = "link notification is not supported";
1965084Sjohnlev 		break;
1978275SEric Cheng 	case DLADM_STATUS_BADTIMEVAL:
1988275SEric Cheng 		s = "invalid time range";
1998275SEric Cheng 		break;
2008275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDR:
2018275SEric Cheng 		s = "invalid MAC address value";
2028275SEric Cheng 		break;
2038275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDRNIC:
2048275SEric Cheng 		s = "MAC address reserved for use by underlying data-link";
2058275SEric Cheng 		break;
2068275SEric Cheng 	case DLADM_STATUS_INVALIDMACADDRINUSE:
2078275SEric Cheng 		s = "MAC address is already in use";
2088275SEric Cheng 		break;
2098275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTINVALID:
2108275SEric Cheng 		s = "invalid factory MAC address slot";
2118275SEric Cheng 		break;
2128275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTUSED:
2138275SEric Cheng 		s = "factory MAC address slot already used";
2148275SEric Cheng 		break;
2158275SEric Cheng 	case DLADM_STATUS_MACFACTORYSLOTALLUSED:
2168275SEric Cheng 		s = "all factory MAC address slots are in use";
2178275SEric Cheng 		break;
2188275SEric Cheng 	case DLADM_STATUS_MACFACTORYNOTSUP:
2198275SEric Cheng 		s = "factory MAC address slots not supported";
2208275SEric Cheng 		break;
2218275SEric Cheng 	case DLADM_STATUS_INVALIDMACPREFIX:
2228275SEric Cheng 		s = "Invalid MAC address prefix value";
2238275SEric Cheng 		break;
2248275SEric Cheng 	case DLADM_STATUS_INVALIDMACPREFIXLEN:
2258275SEric Cheng 		s = "Invalid MAC address prefix length";
2268275SEric Cheng 		break;
2278275SEric Cheng 	case DLADM_STATUS_CPUMAX:
2288275SEric Cheng 		s = "non-existent processor ID";
2298275SEric Cheng 		break;
2308275SEric Cheng 	case DLADM_STATUS_CPUERR:
2318275SEric Cheng 		s = "could not determine processor status";
2328275SEric Cheng 		break;
2338275SEric Cheng 	case DLADM_STATUS_CPUNOTONLINE:
2348275SEric Cheng 		s = "processor not online";
2358275SEric Cheng 		break;
2368275SEric Cheng 	case DLADM_STATUS_DB_NOTFOUND:
2378275SEric Cheng 		s = "database not found";
2388275SEric Cheng 		break;
2398275SEric Cheng 	case DLADM_STATUS_DB_PARSE_ERR:
2408275SEric Cheng 		s = "database parse error";
2418275SEric Cheng 		break;
2428275SEric Cheng 	case DLADM_STATUS_PROP_PARSE_ERR:
2438275SEric Cheng 		s = "property parse error";
2448275SEric Cheng 		break;
2458275SEric Cheng 	case DLADM_STATUS_ATTR_PARSE_ERR:
2468275SEric Cheng 		s = "attribute parse error";
2478275SEric Cheng 		break;
2488275SEric Cheng 	case DLADM_STATUS_FLOW_DB_ERR:
2498275SEric Cheng 		s = "flow database error";
2508275SEric Cheng 		break;
2518275SEric Cheng 	case DLADM_STATUS_FLOW_DB_OPEN_ERR:
2528275SEric Cheng 		s = "flow database open error";
2538275SEric Cheng 		break;
2548275SEric Cheng 	case DLADM_STATUS_FLOW_DB_PARSE_ERR:
2558275SEric Cheng 		s = "flow database parse error";
2568275SEric Cheng 		break;
2578275SEric Cheng 	case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
2588275SEric Cheng 		s = "flow property database parse error";
2598275SEric Cheng 		break;
2608275SEric Cheng 	case DLADM_STATUS_FLOW_ADD_ERR:
2618275SEric Cheng 		s = "flow add error";
2628275SEric Cheng 		break;
2638275SEric Cheng 	case DLADM_STATUS_FLOW_WALK_ERR:
2648275SEric Cheng 		s = "flow walk error";
2658275SEric Cheng 		break;
2668275SEric Cheng 	case DLADM_STATUS_FLOW_IDENTICAL:
2678275SEric Cheng 		s = "a flow with identical attributes exists";
2688275SEric Cheng 		break;
2698275SEric Cheng 	case DLADM_STATUS_FLOW_INCOMPATIBLE:
2708275SEric Cheng 		s = "flow(s) with incompatible attributes exists";
2718275SEric Cheng 		break;
2728275SEric Cheng 	case DLADM_STATUS_FLOW_EXISTS:
2738275SEric Cheng 		s = "link still has flows";
2748275SEric Cheng 		break;
2758275SEric Cheng 	case DLADM_STATUS_PERSIST_FLOW_EXISTS:
2768275SEric Cheng 		s = "persistent flow with the same name exists";
2778275SEric Cheng 		break;
2788275SEric Cheng 	case DLADM_STATUS_INVALID_IP:
2798275SEric Cheng 		s = "invalid IP address";
2808275SEric Cheng 		break;
2818275SEric Cheng 	case DLADM_STATUS_INVALID_PREFIXLEN:
2828275SEric Cheng 		s = "invalid IP prefix length";
2838275SEric Cheng 		break;
2848275SEric Cheng 	case DLADM_STATUS_INVALID_PROTOCOL:
2858275SEric Cheng 		s = "invalid IP protocol";
2868275SEric Cheng 		break;
2878275SEric Cheng 	case DLADM_STATUS_INVALID_PORT:
2888275SEric Cheng 		s = "invalid port number";
2898275SEric Cheng 		break;
2908275SEric Cheng 	case DLADM_STATUS_INVALID_DSF:
2918275SEric Cheng 		s = "invalid dsfield";
2928275SEric Cheng 		break;
2938275SEric Cheng 	case DLADM_STATUS_INVALID_DSFMASK:
2948275SEric Cheng 		s = "invalid dsfield mask";
2958275SEric Cheng 		break;
2968275SEric Cheng 	case DLADM_STATUS_INVALID_MACMARGIN:
2978275SEric Cheng 		s = "MTU check failed, use lower MTU or -f option";
2988275SEric Cheng 		break;
2998275SEric Cheng 	case DLADM_STATUS_BADPROP:
3008275SEric Cheng 		s = "invalid property";
3018275SEric Cheng 		break;
3028275SEric Cheng 	case DLADM_STATUS_MINMAXBW:
3038275SEric Cheng 		s = "minimum value for maxbw is 1.2M";
3048275SEric Cheng 		break;
3058275SEric Cheng 	case DLADM_STATUS_NO_HWRINGS:
3068275SEric Cheng 		s = "request hw rings failed";
3078275SEric Cheng 		break;
3083147Sxc151355 	default:
3093184Smeem 		s = "<unknown error>";
3103184Smeem 		break;
3113147Sxc151355 	}
3123184Smeem 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
3133147Sxc151355 	return (buf);
3143147Sxc151355 }
3153147Sxc151355 
3163147Sxc151355 /*
3173147Sxc151355  * Convert a unix errno to a dladm_status_t.
3183147Sxc151355  * We only convert errnos that are likely to be encountered. All others
3193147Sxc151355  * are mapped to DLADM_STATUS_FAILED.
3203147Sxc151355  */
3213147Sxc151355 dladm_status_t
3223147Sxc151355 dladm_errno2status(int err)
3233147Sxc151355 {
3243147Sxc151355 	switch (err) {
3255903Ssowmini 	case 0:
3265903Ssowmini 		return (DLADM_STATUS_OK);
3273147Sxc151355 	case EINVAL:
3283147Sxc151355 		return (DLADM_STATUS_BADARG);
3293147Sxc151355 	case EEXIST:
3303147Sxc151355 		return (DLADM_STATUS_EXIST);
3313147Sxc151355 	case ENOENT:
3323147Sxc151355 		return (DLADM_STATUS_NOTFOUND);
3333147Sxc151355 	case ENOSPC:
3343147Sxc151355 		return (DLADM_STATUS_TOOSMALL);
3353147Sxc151355 	case ENOMEM:
3363147Sxc151355 		return (DLADM_STATUS_NOMEM);
3373147Sxc151355 	case ENOTSUP:
3383147Sxc151355 		return (DLADM_STATUS_NOTSUP);
3395895Syz147064 	case ENETDOWN:
3405895Syz147064 		return (DLADM_STATUS_NONOTIF);
3413147Sxc151355 	case EACCES:
3427408SSebastien.Roy@Sun.COM 	case EPERM:
3433147Sxc151355 		return (DLADM_STATUS_DENIED);
3443147Sxc151355 	case EIO:
3453147Sxc151355 		return (DLADM_STATUS_IOERR);
3465084Sjohnlev 	case EBUSY:
3475895Syz147064 		return (DLADM_STATUS_LINKBUSY);
3485895Syz147064 	case EAGAIN:
3495895Syz147064 		return (DLADM_STATUS_TRYAGAIN);
3508275SEric Cheng 	case ENOTEMPTY:
3518275SEric Cheng 		return (DLADM_STATUS_FLOW_EXISTS);
3528275SEric Cheng 	case EOPNOTSUPP:
3538275SEric Cheng 		return (DLADM_STATUS_FLOW_INCOMPATIBLE);
3548275SEric Cheng 	case EALREADY:
3558275SEric Cheng 		return (DLADM_STATUS_FLOW_IDENTICAL);
3563147Sxc151355 	default:
3573147Sxc151355 		return (DLADM_STATUS_FAILED);
3583147Sxc151355 	}
3593147Sxc151355 }
3603147Sxc151355 
361*9055SMichael.Lim@Sun.COM boolean_t
362*9055SMichael.Lim@Sun.COM dladm_str2interval(char *oarg, uint32_t *interval)
363*9055SMichael.Lim@Sun.COM {
364*9055SMichael.Lim@Sun.COM 	int		val;
365*9055SMichael.Lim@Sun.COM 	char		*endp = NULL;
366*9055SMichael.Lim@Sun.COM 
367*9055SMichael.Lim@Sun.COM 	errno = 0;
368*9055SMichael.Lim@Sun.COM 	val = strtol(oarg, &endp, 10);
369*9055SMichael.Lim@Sun.COM 	if (errno != 0 || val <= 0 || *endp != '\0')
370*9055SMichael.Lim@Sun.COM 		return (B_FALSE);
371*9055SMichael.Lim@Sun.COM 
372*9055SMichael.Lim@Sun.COM 	*interval = val;
373*9055SMichael.Lim@Sun.COM 
374*9055SMichael.Lim@Sun.COM 	return (B_TRUE);
375*9055SMichael.Lim@Sun.COM }
376*9055SMichael.Lim@Sun.COM 
3778275SEric Cheng dladm_status_t
3788275SEric Cheng dladm_str2bw(char *oarg, uint64_t *bw)
3798275SEric Cheng {
3808275SEric Cheng 	char		*endp = NULL;
3818275SEric Cheng 	int64_t		n;
3828275SEric Cheng 	int		mult = 1;
3838275SEric Cheng 
3848275SEric Cheng 	n = strtoull(oarg, &endp, 10);
3858275SEric Cheng 
3868275SEric Cheng 	if ((errno != 0) || (strlen(endp) > 1))
3878275SEric Cheng 		return (DLADM_STATUS_BADARG);
3888275SEric Cheng 
3898275SEric Cheng 	if (n < 0)
3908275SEric Cheng 		return (DLADM_STATUS_BADVAL);
3918275SEric Cheng 
3928275SEric Cheng 	switch (*endp) {
3938275SEric Cheng 	case 'k':
3948275SEric Cheng 	case 'K':
3958275SEric Cheng 		mult = 1000;
3968275SEric Cheng 		break;
3978275SEric Cheng 	case 'm':
3988275SEric Cheng 	case 'M':
3998275SEric Cheng 	case '\0':
4008275SEric Cheng 		mult = 1000000;
4018275SEric Cheng 		break;
4028275SEric Cheng 	case 'g':
4038275SEric Cheng 	case 'G':
4048275SEric Cheng 		mult = 1000000000;
4058275SEric Cheng 		break;
4068275SEric Cheng 	case '%':
4078275SEric Cheng 		/*
4088275SEric Cheng 		 * percentages not supported for now,
4098275SEric Cheng 		 * see RFE 6540675
4108275SEric Cheng 		 */
4118275SEric Cheng 		return (DLADM_STATUS_NOTSUP);
4128275SEric Cheng 	default:
4138275SEric Cheng 		return (DLADM_STATUS_BADVAL);
4148275SEric Cheng 	}
4158275SEric Cheng 
4168275SEric Cheng 	*bw = n * mult;
4178275SEric Cheng 
4188275SEric Cheng 	/* check for overflow */
4198275SEric Cheng 	if (*bw / mult != n)
4208275SEric Cheng 		return (DLADM_STATUS_BADARG);
4218275SEric Cheng 
4228275SEric Cheng 	return (DLADM_STATUS_OK);
4238275SEric Cheng }
4248275SEric Cheng 
4258275SEric Cheng /*
4268275SEric Cheng  * Convert bandwidth in bps to a string in mpbs.  For values greater
4278275SEric Cheng  * than 1mbps or 1000000, print a whole mbps value.  For values that
4288275SEric Cheng  * have fractional Mbps in whole Kbps , print the bandwidth in a manner
4298275SEric Cheng  * simlilar to a floating point format.
4308275SEric Cheng  *
4318275SEric Cheng  *        bps       string
4328275SEric Cheng  *          0            0
4338275SEric Cheng  *        100            0
4348275SEric Cheng  *       2000        0.002
4358275SEric Cheng  *     431000        0.431
4368275SEric Cheng  *    1000000            1
4378275SEric Cheng  *    1030000        1.030
4388275SEric Cheng  *  100000000          100
4398275SEric Cheng  */
4408275SEric Cheng const char *
4418275SEric Cheng dladm_bw2str(int64_t bw, char *buf)
4428275SEric Cheng {
4438275SEric Cheng 	int kbps, mbps;
4448275SEric Cheng 
4458275SEric Cheng 	kbps = (bw%1000000)/1000;
4468275SEric Cheng 	mbps = bw/1000000;
4478275SEric Cheng 	if (kbps != 0) {
4488275SEric Cheng 		if (mbps == 0)
4498275SEric Cheng 			(void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
4508275SEric Cheng 		else
4518275SEric Cheng 			(void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
4528275SEric Cheng 			    kbps);
4538275SEric Cheng 	} else {
4548275SEric Cheng 		(void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
4558275SEric Cheng 	}
4568275SEric Cheng 
4578275SEric Cheng 	return (buf);
4588275SEric Cheng }
4598275SEric Cheng 
4605895Syz147064 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
4613147Sxc151355 
4623147Sxc151355 static int
4633147Sxc151355 i_dladm_lock_db(const char *lock_file, short type)
4643147Sxc151355 {
4653147Sxc151355 	int	lock_fd;
4665895Syz147064 	struct	flock lock;
4673147Sxc151355 
4683147Sxc151355 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
4693147Sxc151355 	    LOCK_DB_PERMS)) < 0)
4703147Sxc151355 		return (-1);
4713147Sxc151355 
4723147Sxc151355 	lock.l_type = type;
4733147Sxc151355 	lock.l_whence = SEEK_SET;
4743147Sxc151355 	lock.l_start = 0;
4753147Sxc151355 	lock.l_len = 0;
4763147Sxc151355 
4773147Sxc151355 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
4783147Sxc151355 		int err = errno;
4793147Sxc151355 
4803147Sxc151355 		(void) close(lock_fd);
4813147Sxc151355 		(void) unlink(lock_file);
4823147Sxc151355 		errno = err;
4833147Sxc151355 		return (-1);
4843147Sxc151355 	}
4853147Sxc151355 	return (lock_fd);
4863147Sxc151355 }
4873147Sxc151355 
4883147Sxc151355 static void
4893147Sxc151355 i_dladm_unlock_db(const char *lock_file, int fd)
4903147Sxc151355 {
4913147Sxc151355 	struct flock lock;
4923147Sxc151355 
4933147Sxc151355 	if (fd < 0)
4943147Sxc151355 		return;
4953147Sxc151355 
4963147Sxc151355 	lock.l_type = F_UNLCK;
4973147Sxc151355 	lock.l_whence = SEEK_SET;
4983147Sxc151355 	lock.l_start = 0;
4993147Sxc151355 	lock.l_len = 0;
5003147Sxc151355 
5013147Sxc151355 	(void) fcntl(fd, F_SETLKW, &lock);
5023147Sxc151355 	(void) close(fd);
5033147Sxc151355 	(void) unlink(lock_file);
5043147Sxc151355 }
5053147Sxc151355 
5065895Syz147064 /*
5075895Syz147064  * Given a link class, returns its class string.
5085895Syz147064  */
5095895Syz147064 const char *
5105895Syz147064 dladm_class2str(datalink_class_t class, char *buf)
5115895Syz147064 {
5125895Syz147064 	const char *s;
5135895Syz147064 
5145895Syz147064 	switch (class) {
5155895Syz147064 	case DATALINK_CLASS_PHYS:
5165895Syz147064 		s = "phys";
5175895Syz147064 		break;
5185895Syz147064 	case DATALINK_CLASS_VLAN:
5195895Syz147064 		s = "vlan";
5205895Syz147064 		break;
5215895Syz147064 	case DATALINK_CLASS_AGGR:
5225895Syz147064 		s = "aggr";
5235895Syz147064 		break;
5245895Syz147064 	case DATALINK_CLASS_VNIC:
5255895Syz147064 		s = "vnic";
5265895Syz147064 		break;
5278275SEric Cheng 	case DATALINK_CLASS_ETHERSTUB:
5288275SEric Cheng 		s = "etherstub";
5298275SEric Cheng 		break;
5305895Syz147064 	default:
5315895Syz147064 		s = "unknown";
5325895Syz147064 		break;
5335895Syz147064 	}
5345895Syz147064 
5355895Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
5365895Syz147064 	return (buf);
5375895Syz147064 }
5385895Syz147064 
5395895Syz147064 /*
5405895Syz147064  * Given a physical link media type, returns its media type string.
5415895Syz147064  */
5425895Syz147064 const char *
5435895Syz147064 dladm_media2str(uint32_t media, char *buf)
5445895Syz147064 {
5455895Syz147064 	const char *s;
5465895Syz147064 
5475895Syz147064 	switch (media) {
5485895Syz147064 	case DL_ETHER:
5495895Syz147064 		s = "Ethernet";
5505895Syz147064 		break;
5515895Syz147064 	case DL_WIFI:
5525895Syz147064 		s = "WiFi";
5535895Syz147064 		break;
5545895Syz147064 	case DL_IB:
5555895Syz147064 		s = "Infiniband";
5565895Syz147064 		break;
5575895Syz147064 	case DL_IPV4:
5585895Syz147064 		s = "IPv4Tunnel";
5595895Syz147064 		break;
5605895Syz147064 	case DL_IPV6:
5615895Syz147064 		s = "IPv6Tunnel";
5625895Syz147064 		break;
5635895Syz147064 	case DL_CSMACD:
5645895Syz147064 		s = "CSMA/CD";
5655895Syz147064 		break;
5665895Syz147064 	case DL_TPB:
5675895Syz147064 		s = "TokenBus";
5685895Syz147064 		break;
5695895Syz147064 	case DL_TPR:
5705895Syz147064 		s = "TokenRing";
5715895Syz147064 		break;
5725895Syz147064 	case DL_METRO:
5735895Syz147064 		s = "MetroNet";
5745895Syz147064 		break;
5755895Syz147064 	case DL_HDLC:
5765895Syz147064 		s = "HDLC";
5775895Syz147064 		break;
5785895Syz147064 	case DL_CHAR:
5795895Syz147064 		s = "SyncCharacter";
5805895Syz147064 		break;
5815895Syz147064 	case DL_CTCA:
5825895Syz147064 		s = "CTCA";
5835895Syz147064 		break;
5845895Syz147064 	case DL_FDDI:
5855895Syz147064 		s = "FDDI";
5865895Syz147064 		break;
5875895Syz147064 	case DL_FC:
5885895Syz147064 		s = "FiberChannel";
5895895Syz147064 		break;
5905895Syz147064 	case DL_ATM:
5915895Syz147064 		s = "ATM";
5925895Syz147064 		break;
5935895Syz147064 	case DL_IPATM:
5945895Syz147064 		s = "ATM(ClassicIP)";
5955895Syz147064 		break;
5965895Syz147064 	case DL_X25:
5975895Syz147064 		s = "X.25";
5985895Syz147064 		break;
5995895Syz147064 	case DL_IPX25:
6005895Syz147064 		s = "X.25(ClassicIP)";
6015895Syz147064 		break;
6025895Syz147064 	case DL_ISDN:
6035895Syz147064 		s = "ISDN";
6045895Syz147064 		break;
6055895Syz147064 	case DL_HIPPI:
6065895Syz147064 		s = "HIPPI";
6075895Syz147064 		break;
6085895Syz147064 	case DL_100VG:
6095895Syz147064 		s = "100BaseVGEthernet";
6105895Syz147064 		break;
6115895Syz147064 	case DL_100VGTPR:
6125895Syz147064 		s = "100BaseVGTokenRing";
6135895Syz147064 		break;
6145895Syz147064 	case DL_ETH_CSMA:
6155895Syz147064 		s = "IEEE802.3";
6165895Syz147064 		break;
6175895Syz147064 	case DL_100BT:
6185895Syz147064 		s = "100BaseT";
6195895Syz147064 		break;
6205895Syz147064 	case DL_FRAME:
6215895Syz147064 		s = "FrameRelay";
6225895Syz147064 		break;
6235895Syz147064 	case DL_MPFRAME:
6245895Syz147064 		s = "MPFrameRelay";
6255895Syz147064 		break;
6265895Syz147064 	case DL_ASYNC:
6275895Syz147064 		s = "AsyncCharacter";
6285895Syz147064 		break;
6298023SPhil.Kirk@Sun.COM 	case DL_IPNET:
6308023SPhil.Kirk@Sun.COM 		s = "IPNET";
6318023SPhil.Kirk@Sun.COM 		break;
6325895Syz147064 	default:
6335895Syz147064 		s = "--";
6345895Syz147064 		break;
6355895Syz147064 	}
6365895Syz147064 
6375895Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
6385895Syz147064 	return (buf);
6395895Syz147064 }
6405895Syz147064 
6413147Sxc151355 dladm_status_t
6428453SAnurag.Maskey@Sun.COM i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
6438453SAnurag.Maskey@Sun.COM     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
6443147Sxc151355     void *arg, boolean_t writeop)
6453147Sxc151355 {
6463147Sxc151355 	dladm_status_t	status = DLADM_STATUS_OK;
6473147Sxc151355 	FILE		*fp, *nfp = NULL;
6483147Sxc151355 	char		lock[MAXPATHLEN];
6493147Sxc151355 	char		file[MAXPATHLEN];
6503147Sxc151355 	char		newfile[MAXPATHLEN];
6513147Sxc151355 	char		*db_basename;
6523147Sxc151355 	int		nfd, lock_fd;
6533147Sxc151355 
6543147Sxc151355 	/*
6553147Sxc151355 	 * If we are called from a boot script such as net-physical,
6563147Sxc151355 	 * it's quite likely that the root fs is still not writable.
6573147Sxc151355 	 * For this case, it's ok for the lock creation to fail since
6583147Sxc151355 	 * no one else could be accessing our configuration file.
6593147Sxc151355 	 */
6603147Sxc151355 	db_basename = strrchr(db_file, '/');
6613147Sxc151355 	if (db_basename == NULL || db_basename[1] == '\0')
6623147Sxc151355 		return (dladm_errno2status(EINVAL));
6633147Sxc151355 	db_basename++;
6643147Sxc151355 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
6653147Sxc151355 	if ((lock_fd = i_dladm_lock_db
6663147Sxc151355 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
6673147Sxc151355 		return (dladm_errno2status(errno));
6683147Sxc151355 
6693147Sxc151355 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
6703147Sxc151355 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
6713147Sxc151355 		int	err = errno;
6723147Sxc151355 
6733147Sxc151355 		i_dladm_unlock_db(lock, lock_fd);
6743147Sxc151355 		if (err == ENOENT)
6753147Sxc151355 			return (DLADM_STATUS_DBNOTFOUND);
6763147Sxc151355 
6773147Sxc151355 		return (dladm_errno2status(err));
6783147Sxc151355 	}
6793147Sxc151355 
6803147Sxc151355 	if (writeop) {
6813147Sxc151355 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
6823147Sxc151355 		    dladm_rootdir, db_file);
6833147Sxc151355 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
6843147Sxc151355 		    db_perms)) < 0) {
6853147Sxc151355 			(void) fclose(fp);
6863147Sxc151355 			i_dladm_unlock_db(lock, lock_fd);
6873147Sxc151355 			return (dladm_errno2status(errno));
6883147Sxc151355 		}
6893147Sxc151355 
6903147Sxc151355 		if ((nfp = fdopen(nfd, "w")) == NULL) {
6913147Sxc151355 			(void) close(nfd);
6923147Sxc151355 			(void) fclose(fp);
6933147Sxc151355 			(void) unlink(newfile);
6943147Sxc151355 			i_dladm_unlock_db(lock, lock_fd);
6953147Sxc151355 			return (dladm_errno2status(errno));
6963147Sxc151355 		}
6973147Sxc151355 	}
6988453SAnurag.Maskey@Sun.COM 	status = (*process_db)(handle, arg, fp, nfp);
6993147Sxc151355 	if (!writeop || status != DLADM_STATUS_OK)
7003147Sxc151355 		goto done;
7013147Sxc151355 
7023147Sxc151355 	/*
7033147Sxc151355 	 * Configuration files need to be owned by the 'dladm' user.
7043147Sxc151355 	 * If we are invoked by root, the file ownership needs to be fixed.
7053147Sxc151355 	 */
7063147Sxc151355 	if (getuid() == 0 || geteuid() == 0) {
7076173Syz147064 		if (fchown(nfd, UID_DLADM, GID_SYS) < 0) {
7083147Sxc151355 			status = dladm_errno2status(errno);
7093147Sxc151355 			goto done;
7103147Sxc151355 		}
7113147Sxc151355 	}
7123147Sxc151355 
7133147Sxc151355 	if (fflush(nfp) == EOF) {
7143147Sxc151355 		status = dladm_errno2status(errno);
7153147Sxc151355 		goto done;
7163147Sxc151355 	}
7173147Sxc151355 	(void) fclose(fp);
7183147Sxc151355 	(void) fclose(nfp);
7193147Sxc151355 
7203147Sxc151355 	if (rename(newfile, file) < 0) {
7213147Sxc151355 		(void) unlink(newfile);
7223147Sxc151355 		i_dladm_unlock_db(lock, lock_fd);
7233147Sxc151355 		return (dladm_errno2status(errno));
7243147Sxc151355 	}
7253147Sxc151355 
7263147Sxc151355 	i_dladm_unlock_db(lock, lock_fd);
7273147Sxc151355 	return (DLADM_STATUS_OK);
7283147Sxc151355 
7293147Sxc151355 done:
7303147Sxc151355 	if (nfp != NULL) {
7313147Sxc151355 		(void) fclose(nfp);
7323147Sxc151355 		if (status != DLADM_STATUS_OK)
7333147Sxc151355 			(void) unlink(newfile);
7343147Sxc151355 	}
7353147Sxc151355 	(void) fclose(fp);
7363147Sxc151355 	i_dladm_unlock_db(lock, lock_fd);
7373147Sxc151355 	return (status);
7383147Sxc151355 }
7393147Sxc151355 
7403147Sxc151355 dladm_status_t
7413147Sxc151355 dladm_set_rootdir(const char *rootdir)
7423147Sxc151355 {
7433147Sxc151355 	DIR	*dp;
7443147Sxc151355 
7453147Sxc151355 	if (rootdir == NULL || *rootdir != '/' ||
7463147Sxc151355 	    (dp = opendir(rootdir)) == NULL)
7473147Sxc151355 		return (DLADM_STATUS_BADARG);
7483147Sxc151355 
7493147Sxc151355 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
7503147Sxc151355 	(void) closedir(dp);
7513147Sxc151355 	return (DLADM_STATUS_OK);
7523147Sxc151355 }
7535895Syz147064 
7545895Syz147064 boolean_t
7555895Syz147064 dladm_valid_linkname(const char *link)
7565895Syz147064 {
7575895Syz147064 	size_t		len = strlen(link);
7585895Syz147064 	const char	*cp;
7595895Syz147064 
7605895Syz147064 	if (len + 1 >= MAXLINKNAMELEN)
7615895Syz147064 		return (B_FALSE);
7625895Syz147064 
7635895Syz147064 	/*
7645895Syz147064 	 * The link name cannot start with a digit and must end with a digit.
7655895Syz147064 	 */
7665895Syz147064 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
7675895Syz147064 		return (B_FALSE);
7685895Syz147064 
7695895Syz147064 	/*
7705895Syz147064 	 * The legal characters in a link name are:
7715895Syz147064 	 * alphanumeric (a-z,  A-Z,  0-9), and the underscore ('_').
7725895Syz147064 	 */
7735895Syz147064 	for (cp = link; *cp != '\0'; cp++) {
7745895Syz147064 		if ((isalnum(*cp) == 0) && (*cp != '_'))
7755895Syz147064 			return (B_FALSE);
7765895Syz147064 	}
7775895Syz147064 
7785895Syz147064 	return (B_TRUE);
7795895Syz147064 }
7808275SEric Cheng 
7818275SEric Cheng /*
7828275SEric Cheng  * Convert priority string to a value.
7838275SEric Cheng  */
7848275SEric Cheng dladm_status_t
7858275SEric Cheng dladm_str2pri(char *token, mac_priority_level_t *pri)
7868275SEric Cheng {
7878275SEric Cheng 	if (strlen(token) == strlen("low") &&
7888275SEric Cheng 	    strncasecmp(token, "low", strlen("low")) == 0) {
7898275SEric Cheng 		*pri = MPL_LOW;
7908275SEric Cheng 	} else if (strlen(token) == strlen("medium") &&
7918275SEric Cheng 	    strncasecmp(token, "medium", strlen("medium")) == 0) {
7928275SEric Cheng 		*pri = MPL_MEDIUM;
7938275SEric Cheng 	} else if (strlen(token) == strlen("high") &&
7948275SEric Cheng 	    strncasecmp(token, "high", strlen("high")) == 0) {
7958275SEric Cheng 		*pri = MPL_HIGH;
7968275SEric Cheng 	} else {
7978275SEric Cheng 		return (DLADM_STATUS_BADVAL);
7988275SEric Cheng 	}
7998275SEric Cheng 	return (DLADM_STATUS_OK);
8008275SEric Cheng }
8018275SEric Cheng 
8028275SEric Cheng /*
8038275SEric Cheng  * Convert priority value to a string.
8048275SEric Cheng  */
8058275SEric Cheng const char *
8068275SEric Cheng dladm_pri2str(mac_priority_level_t pri, char *buf)
8078275SEric Cheng {
8088275SEric Cheng 	const char	*s;
8098275SEric Cheng 
8108275SEric Cheng 	switch (pri) {
8118275SEric Cheng 	case MPL_LOW:
8128275SEric Cheng 		s = "low";
8138275SEric Cheng 		break;
8148275SEric Cheng 	case MPL_MEDIUM:
8158275SEric Cheng 		s = "medium";
8168275SEric Cheng 		break;
8178275SEric Cheng 	case MPL_HIGH:
8188275SEric Cheng 		s = "high";
8198275SEric Cheng 		break;
8208275SEric Cheng 	default:
8218275SEric Cheng 		s = "--";
8228275SEric Cheng 		break;
8238275SEric Cheng 	}
8248275SEric Cheng 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
8258275SEric Cheng 	return (buf);
8268275SEric Cheng }
8278275SEric Cheng 
8288275SEric Cheng void
8298275SEric Cheng dladm_free_args(dladm_arg_list_t *list)
8308275SEric Cheng {
8318275SEric Cheng 	if (list != NULL) {
8328275SEric Cheng 		free(list->al_buf);
8338275SEric Cheng 		free(list);
8348275SEric Cheng 	}
8358275SEric Cheng }
8368275SEric Cheng 
8378275SEric Cheng dladm_status_t
8388275SEric Cheng dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
8398275SEric Cheng {
8408275SEric Cheng 	dladm_arg_list_t	*list;
8418275SEric Cheng 	dladm_arg_info_t	*aip;
8428275SEric Cheng 	char			*buf, *curr;
8438275SEric Cheng 	int			len, i;
8448275SEric Cheng 
845*9055SMichael.Lim@Sun.COM 	if (str == NULL)
846*9055SMichael.Lim@Sun.COM 		return (DLADM_STATUS_BADVAL);
847*9055SMichael.Lim@Sun.COM 
848*9055SMichael.Lim@Sun.COM 	if (str[0] == '\0')
849*9055SMichael.Lim@Sun.COM 		return (DLADM_STATUS_OK);
850*9055SMichael.Lim@Sun.COM 
8518275SEric Cheng 	list = malloc(sizeof (dladm_arg_list_t));
8528275SEric Cheng 	if (list == NULL)
8538275SEric Cheng 		return (dladm_errno2status(errno));
8548275SEric Cheng 
8558275SEric Cheng 	list->al_count = 0;
8568275SEric Cheng 	list->al_buf = buf = strdup(str);
8578275SEric Cheng 	if (buf == NULL)
8588275SEric Cheng 		return (dladm_errno2status(errno));
8598275SEric Cheng 
8608275SEric Cheng 	curr = buf;
8618275SEric Cheng 	len = strlen(buf);
8628275SEric Cheng 	aip = NULL;
8638275SEric Cheng 	for (i = 0; i < len; i++) {
8648275SEric Cheng 		char		c = buf[i];
8658275SEric Cheng 		boolean_t	match = (c == '=' || c == ',');
8668275SEric Cheng 
8678275SEric Cheng 		if (!match && i != len - 1)
8688275SEric Cheng 			continue;
8698275SEric Cheng 
8708275SEric Cheng 		if (match) {
8718275SEric Cheng 			buf[i] = '\0';
8728275SEric Cheng 			if (*curr == '\0')
8738275SEric Cheng 				goto fail;
8748275SEric Cheng 		}
8758275SEric Cheng 
8768275SEric Cheng 		if (aip != NULL && c != '=') {
8778275SEric Cheng 			if (aip->ai_count > DLADM_MAX_ARG_VALS)
8788275SEric Cheng 				goto fail;
8798275SEric Cheng 
8808275SEric Cheng 			if (novalues)
8818275SEric Cheng 				goto fail;
8828275SEric Cheng 
8838275SEric Cheng 			aip->ai_val[aip->ai_count] = curr;
8848275SEric Cheng 			aip->ai_count++;
8858275SEric Cheng 		} else {
8868275SEric Cheng 			if (list->al_count > DLADM_MAX_ARG_VALS)
8878275SEric Cheng 				goto fail;
8888275SEric Cheng 
8898275SEric Cheng 			aip = &list->al_info[list->al_count];
8908275SEric Cheng 			aip->ai_name = curr;
8918275SEric Cheng 			aip->ai_count = 0;
8928275SEric Cheng 			list->al_count++;
8938275SEric Cheng 			if (c == ',')
8948275SEric Cheng 				aip = NULL;
8958275SEric Cheng 		}
8968275SEric Cheng 		curr = buf + i + 1;
8978275SEric Cheng 	}
8988275SEric Cheng 
8998275SEric Cheng 	*listp = list;
9008275SEric Cheng 	return (DLADM_STATUS_OK);
9018275SEric Cheng 
9028275SEric Cheng fail:
9038275SEric Cheng 	dladm_free_args(list);
9048275SEric Cheng 	return (DLADM_STATUS_FAILED);
9058275SEric Cheng }
906