Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <unistd.h>
     27 #include <errno.h>
     28 #include <ctype.h>
     29 #include <fcntl.h>
     30 #include <strings.h>
     31 #include <dirent.h>
     32 #include <stdlib.h>
     33 #include <netinet/in.h>
     34 #include <arpa/inet.h>
     35 #include <sys/param.h>
     36 #include <sys/stat.h>
     37 #include <sys/dld.h>
     38 #include <sys/dld_ioc.h>
     39 #include <libdladm_impl.h>
     40 #include <libintl.h>
     41 #include <libdlpi.h>
     42 
     43 static char	dladm_rootdir[MAXPATHLEN] = "/";
     44 
     45 typedef struct media_type_desc {
     46 	uint32_t	media_type;
     47 #define	MAX_MEDIA_TYPE_STRING	32
     48 	const char	media_type_str[MAX_MEDIA_TYPE_STRING];
     49 } media_type_t;
     50 
     51 static media_type_t media_type_table[] =  {
     52 	{ DL_ETHER,	"Ethernet" },
     53 	{ DL_WIFI,	"WiFi" },
     54 	{ DL_IB,	"Infiniband" },
     55 	{ DL_IPV4,	"IPv4Tunnel" },
     56 	{ DL_IPV6,	"IPv6Tunnel" },
     57 	{ DL_6TO4,	"6to4Tunnel" },
     58 	{ DL_CSMACD,	"CSMA/CD" },
     59 	{ DL_TPB,	"TokenBus" },
     60 	{ DL_TPR,	"TokenRing" },
     61 	{ DL_METRO,	"MetroNet" },
     62 	{ DL_HDLC,	"HDLC" },
     63 	{ DL_CHAR,	"SyncCharacter" },
     64 	{ DL_CTCA,	"CTCA" },
     65 	{ DL_FDDI, 	"FDDI" },
     66 	{ DL_FC, 	"FiberChannel" },
     67 	{ DL_ATM, 	"ATM" },
     68 	{ DL_IPATM, 	"ATM(ClassicIP)" },
     69 	{ DL_X25, 	"X.25" },
     70 	{ DL_IPX25, 	"X.25(ClassicIP)" },
     71 	{ DL_ISDN, 	"ISDN" },
     72 	{ DL_HIPPI, 	"HIPPI" },
     73 	{ DL_100VG, 	"100BaseVGEthernet" },
     74 	{ DL_100VGTPR, 	"100BaseVGTokenRing" },
     75 	{ DL_ETH_CSMA, 	"IEEE802.3" },
     76 	{ DL_100BT, 	"100BaseT" },
     77 	{ DL_FRAME, 	"FrameRelay" },
     78 	{ DL_MPFRAME, 	"MPFrameRelay" },
     79 	{ DL_ASYNC, 	"AsyncCharacter" },
     80 	{ DL_IPNET, 	"IPNET" },
     81 	{ DL_OTHER, 	"Other" }
     82 };
     83 #define	MEDIATYPECOUNT	(sizeof (media_type_table) / sizeof (media_type_t))
     84 
     85 typedef struct {
     86 	uint32_t	lp_type;
     87 	char		*lp_name;
     88 } link_protect_t;
     89 
     90 static link_protect_t link_protect_types[] = {
     91 	{ MPT_MACNOSPOOF, "mac-nospoof" },
     92 	{ MPT_IPNOSPOOF, "ip-nospoof" },
     93 	{ MPT_RESTRICTED, "restricted" }
     94 };
     95 #define	LPTYPES	(sizeof (link_protect_types) / sizeof (link_protect_t))
     96 
     97 dladm_status_t
     98 dladm_open(dladm_handle_t *handle)
     99 {
    100 	int dld_fd;
    101 
    102 	if (handle == NULL)
    103 		return (DLADM_STATUS_BADARG);
    104 
    105 	if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
    106 		return (dladm_errno2status(errno));
    107 
    108 	/*
    109 	 * Don't open DLMGMT_DOOR now.  dlmgmtd(1M) is not able to
    110 	 * open the door when the dladm handle is opened because the
    111 	 * door hasn't been created yet at that time.  Thus, we must
    112 	 * open it on-demand in dladm_door_fd().  Move the open()
    113 	 * to dladm_door_fd() for all cases.
    114 	 */
    115 
    116 	if ((*handle = malloc(sizeof (struct dladm_handle))) == NULL) {
    117 		(void) close(dld_fd);
    118 		return (DLADM_STATUS_NOMEM);
    119 	}
    120 
    121 	(*handle)->dld_fd = dld_fd;
    122 	(*handle)->door_fd = -1;
    123 
    124 	return (DLADM_STATUS_OK);
    125 }
    126 
    127 void
    128 dladm_close(dladm_handle_t handle)
    129 {
    130 	if (handle != NULL) {
    131 		(void) close(handle->dld_fd);
    132 		if (handle->door_fd != -1)
    133 			(void) close(handle->door_fd);
    134 		free(handle);
    135 	}
    136 }
    137 
    138 int
    139 dladm_dld_fd(dladm_handle_t handle)
    140 {
    141 	return (handle->dld_fd);
    142 }
    143 
    144 /*
    145  * If DLMGMT_DOOR hasn't been opened in the handle yet, open it.
    146  */
    147 dladm_status_t
    148 dladm_door_fd(dladm_handle_t handle, int *door_fd)
    149 {
    150 	int fd;
    151 
    152 	if (handle->door_fd == -1) {
    153 		if ((fd = open(DLMGMT_DOOR, O_RDONLY)) < 0)
    154 			return (dladm_errno2status(errno));
    155 		handle->door_fd = fd;
    156 	}
    157 	*door_fd = handle->door_fd;
    158 
    159 	return (DLADM_STATUS_OK);
    160 }
    161 
    162 const char *
    163 dladm_status2str(dladm_status_t status, char *buf)
    164 {
    165 	const char	*s;
    166 
    167 	switch (status) {
    168 	case DLADM_STATUS_OK:
    169 		s = "ok";
    170 		break;
    171 	case DLADM_STATUS_BADARG:
    172 		s = "invalid argument";
    173 		break;
    174 	case DLADM_STATUS_FAILED:
    175 		s = "operation failed";
    176 		break;
    177 	case DLADM_STATUS_TOOSMALL:
    178 		s = "buffer size too small";
    179 		break;
    180 	case DLADM_STATUS_NOTSUP:
    181 		s = "operation not supported";
    182 		break;
    183 	case DLADM_STATUS_NOTFOUND:
    184 		s = "object not found";
    185 		break;
    186 	case DLADM_STATUS_BADVAL:
    187 		s = "invalid value";
    188 		break;
    189 	case DLADM_STATUS_NOMEM:
    190 		s = "insufficient memory";
    191 		break;
    192 	case DLADM_STATUS_EXIST:
    193 		s = "object already exists";
    194 		break;
    195 	case DLADM_STATUS_LINKINVAL:
    196 		s = "invalid link";
    197 		break;
    198 	case DLADM_STATUS_PROPRDONLY:
    199 		s = "read-only property";
    200 		break;
    201 	case DLADM_STATUS_BADVALCNT:
    202 		s = "invalid number of values";
    203 		break;
    204 	case DLADM_STATUS_DBNOTFOUND:
    205 		s = "database not found";
    206 		break;
    207 	case DLADM_STATUS_DENIED:
    208 		s = "permission denied";
    209 		break;
    210 	case DLADM_STATUS_IOERR:
    211 		s = "I/O error";
    212 		break;
    213 	case DLADM_STATUS_TEMPONLY:
    214 		s = "change cannot be persistent";
    215 		break;
    216 	case DLADM_STATUS_TIMEDOUT:
    217 		s = "operation timed out";
    218 		break;
    219 	case DLADM_STATUS_ISCONN:
    220 		s = "already connected";
    221 		break;
    222 	case DLADM_STATUS_NOTCONN:
    223 		s = "not connected";
    224 		break;
    225 	case DLADM_STATUS_REPOSITORYINVAL:
    226 		s = "invalid configuration repository";
    227 		break;
    228 	case DLADM_STATUS_MACADDRINVAL:
    229 		s = "invalid MAC address";
    230 		break;
    231 	case DLADM_STATUS_KEYINVAL:
    232 		s = "invalid key";
    233 		break;
    234 	case DLADM_STATUS_INVALIDMACADDRLEN:
    235 		s = "invalid MAC address length";
    236 		break;
    237 	case DLADM_STATUS_INVALIDMACADDRTYPE:
    238 		s = "invalid MAC address type";
    239 		break;
    240 	case DLADM_STATUS_LINKBUSY:
    241 		s = "link busy";
    242 		break;
    243 	case DLADM_STATUS_VIDINVAL:
    244 		s = "invalid VLAN identifier";
    245 		break;
    246 	case DLADM_STATUS_TRYAGAIN:
    247 		s = "try again later";
    248 		break;
    249 	case DLADM_STATUS_NONOTIF:
    250 		s = "link notification is not supported";
    251 		break;
    252 	case DLADM_STATUS_BADTIMEVAL:
    253 		s = "invalid time range";
    254 		break;
    255 	case DLADM_STATUS_INVALIDMACADDR:
    256 		s = "invalid MAC address value";
    257 		break;
    258 	case DLADM_STATUS_INVALIDMACADDRNIC:
    259 		s = "MAC address reserved for use by underlying data-link";
    260 		break;
    261 	case DLADM_STATUS_INVALIDMACADDRINUSE:
    262 		s = "MAC address is already in use";
    263 		break;
    264 	case DLADM_STATUS_MACFACTORYSLOTINVALID:
    265 		s = "invalid factory MAC address slot";
    266 		break;
    267 	case DLADM_STATUS_MACFACTORYSLOTUSED:
    268 		s = "factory MAC address slot already used";
    269 		break;
    270 	case DLADM_STATUS_MACFACTORYSLOTALLUSED:
    271 		s = "all factory MAC address slots are in use";
    272 		break;
    273 	case DLADM_STATUS_MACFACTORYNOTSUP:
    274 		s = "factory MAC address slots not supported";
    275 		break;
    276 	case DLADM_STATUS_INVALIDMACPREFIX:
    277 		s = "Invalid MAC address prefix value";
    278 		break;
    279 	case DLADM_STATUS_INVALIDMACPREFIXLEN:
    280 		s = "Invalid MAC address prefix length";
    281 		break;
    282 	case DLADM_STATUS_CPUMAX:
    283 		s = "non-existent processor ID";
    284 		break;
    285 	case DLADM_STATUS_CPUERR:
    286 		s = "could not determine processor status";
    287 		break;
    288 	case DLADM_STATUS_CPUNOTONLINE:
    289 		s = "processor not online";
    290 		break;
    291 	case DLADM_STATUS_DB_NOTFOUND:
    292 		s = "database not found";
    293 		break;
    294 	case DLADM_STATUS_DB_PARSE_ERR:
    295 		s = "database parse error";
    296 		break;
    297 	case DLADM_STATUS_PROP_PARSE_ERR:
    298 		s = "property parse error";
    299 		break;
    300 	case DLADM_STATUS_ATTR_PARSE_ERR:
    301 		s = "attribute parse error";
    302 		break;
    303 	case DLADM_STATUS_FLOW_DB_ERR:
    304 		s = "flow database error";
    305 		break;
    306 	case DLADM_STATUS_FLOW_DB_OPEN_ERR:
    307 		s = "flow database open error";
    308 		break;
    309 	case DLADM_STATUS_FLOW_DB_PARSE_ERR:
    310 		s = "flow database parse error";
    311 		break;
    312 	case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
    313 		s = "flow property database parse error";
    314 		break;
    315 	case DLADM_STATUS_FLOW_ADD_ERR:
    316 		s = "flow add error";
    317 		break;
    318 	case DLADM_STATUS_FLOW_WALK_ERR:
    319 		s = "flow walk error";
    320 		break;
    321 	case DLADM_STATUS_FLOW_IDENTICAL:
    322 		s = "a flow with identical attributes exists";
    323 		break;
    324 	case DLADM_STATUS_FLOW_INCOMPATIBLE:
    325 		s = "flow(s) with incompatible attributes exists";
    326 		break;
    327 	case DLADM_STATUS_FLOW_EXISTS:
    328 		s = "link still has flows";
    329 		break;
    330 	case DLADM_STATUS_PERSIST_FLOW_EXISTS:
    331 		s = "persistent flow with the same name exists";
    332 		break;
    333 	case DLADM_STATUS_INVALID_IP:
    334 		s = "invalid IP address";
    335 		break;
    336 	case DLADM_STATUS_INVALID_PREFIXLEN:
    337 		s = "invalid IP prefix length";
    338 		break;
    339 	case DLADM_STATUS_INVALID_PROTOCOL:
    340 		s = "invalid IP protocol";
    341 		break;
    342 	case DLADM_STATUS_INVALID_PORT:
    343 		s = "invalid port number";
    344 		break;
    345 	case DLADM_STATUS_INVALID_DSF:
    346 		s = "invalid dsfield";
    347 		break;
    348 	case DLADM_STATUS_INVALID_DSFMASK:
    349 		s = "invalid dsfield mask";
    350 		break;
    351 	case DLADM_STATUS_INVALID_MACMARGIN:
    352 		s = "MTU check failed, use lower MTU or -f option";
    353 		break;
    354 	case DLADM_STATUS_BADPROP:
    355 		s = "invalid property";
    356 		break;
    357 	case DLADM_STATUS_MINMAXBW:
    358 		s = "minimum value for maxbw is 1.2M";
    359 		break;
    360 	case DLADM_STATUS_NO_HWRINGS:
    361 		s = "request hw rings failed";
    362 		break;
    363 	case DLADM_STATUS_PERMONLY:
    364 		s = "change must be persistent";
    365 		break;
    366 	case DLADM_STATUS_OPTMISSING:
    367 		s = "optional software not installed";
    368 		break;
    369 	case DLADM_STATUS_IPTUNTYPE:
    370 		s = "invalid IP tunnel type";
    371 		break;
    372 	case DLADM_STATUS_IPTUNTYPEREQD:
    373 		s = "IP tunnel type required";
    374 		break;
    375 	case DLADM_STATUS_BADIPTUNLADDR:
    376 		s = "invalid local IP tunnel address";
    377 		break;
    378 	case DLADM_STATUS_BADIPTUNRADDR:
    379 		s = "invalid remote IP tunnel address";
    380 		break;
    381 	case DLADM_STATUS_ADDRINUSE:
    382 		s = "address already in use";
    383 		break;
    384 	default:
    385 		s = "<unknown error>";
    386 		break;
    387 	}
    388 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
    389 	return (buf);
    390 }
    391 
    392 /*
    393  * Convert a unix errno to a dladm_status_t.
    394  * We only convert errnos that are likely to be encountered. All others
    395  * are mapped to DLADM_STATUS_FAILED.
    396  */
    397 dladm_status_t
    398 dladm_errno2status(int err)
    399 {
    400 	switch (err) {
    401 	case 0:
    402 		return (DLADM_STATUS_OK);
    403 	case EINVAL:
    404 		return (DLADM_STATUS_BADARG);
    405 	case EEXIST:
    406 		return (DLADM_STATUS_EXIST);
    407 	case ENOENT:
    408 		return (DLADM_STATUS_NOTFOUND);
    409 	case ENOSPC:
    410 		return (DLADM_STATUS_TOOSMALL);
    411 	case ENOMEM:
    412 		return (DLADM_STATUS_NOMEM);
    413 	case ENOTSUP:
    414 		return (DLADM_STATUS_NOTSUP);
    415 	case ENETDOWN:
    416 		return (DLADM_STATUS_NONOTIF);
    417 	case EACCES:
    418 	case EPERM:
    419 		return (DLADM_STATUS_DENIED);
    420 	case EIO:
    421 		return (DLADM_STATUS_IOERR);
    422 	case EBUSY:
    423 		return (DLADM_STATUS_LINKBUSY);
    424 	case EAGAIN:
    425 		return (DLADM_STATUS_TRYAGAIN);
    426 	case ENOTEMPTY:
    427 		return (DLADM_STATUS_FLOW_EXISTS);
    428 	case EOPNOTSUPP:
    429 		return (DLADM_STATUS_FLOW_INCOMPATIBLE);
    430 	case EALREADY:
    431 		return (DLADM_STATUS_FLOW_IDENTICAL);
    432 	case EADDRINUSE:
    433 		return (DLADM_STATUS_ADDRINUSE);
    434 	default:
    435 		return (DLADM_STATUS_FAILED);
    436 	}
    437 }
    438 
    439 boolean_t
    440 dladm_str2interval(char *oarg, uint32_t *interval)
    441 {
    442 	int		val;
    443 	char		*endp = NULL;
    444 
    445 	errno = 0;
    446 	val = strtol(oarg, &endp, 10);
    447 	if (errno != 0 || val <= 0 || *endp != '\0')
    448 		return (B_FALSE);
    449 
    450 	*interval = val;
    451 
    452 	return (B_TRUE);
    453 }
    454 
    455 dladm_status_t
    456 dladm_str2bw(char *oarg, uint64_t *bw)
    457 {
    458 	char		*endp = NULL;
    459 	int64_t		n;
    460 	int		mult = 1;
    461 
    462 	n = strtoull(oarg, &endp, 10);
    463 
    464 	if ((errno != 0) || (strlen(endp) > 1))
    465 		return (DLADM_STATUS_BADARG);
    466 
    467 	if (n < 0)
    468 		return (DLADM_STATUS_BADVAL);
    469 
    470 	switch (*endp) {
    471 	case 'k':
    472 	case 'K':
    473 		mult = 1000;
    474 		break;
    475 	case 'm':
    476 	case 'M':
    477 	case '\0':
    478 		mult = 1000000;
    479 		break;
    480 	case 'g':
    481 	case 'G':
    482 		mult = 1000000000;
    483 		break;
    484 	case '%':
    485 		/*
    486 		 * percentages not supported for now,
    487 		 * see RFE 6540675
    488 		 */
    489 		return (DLADM_STATUS_NOTSUP);
    490 	default:
    491 		return (DLADM_STATUS_BADVAL);
    492 	}
    493 
    494 	*bw = n * mult;
    495 
    496 	/* check for overflow */
    497 	if (*bw / mult != n)
    498 		return (DLADM_STATUS_BADARG);
    499 
    500 	return (DLADM_STATUS_OK);
    501 }
    502 
    503 /*
    504  * Convert bandwidth in bps to a string in mpbs.  For values greater
    505  * than 1mbps or 1000000, print a whole mbps value.  For values that
    506  * have fractional Mbps in whole Kbps , print the bandwidth in a manner
    507  * simlilar to a floating point format.
    508  *
    509  *        bps       string
    510  *          0            0
    511  *        100            0
    512  *       2000        0.002
    513  *     431000        0.431
    514  *    1000000            1
    515  *    1030000        1.030
    516  *  100000000          100
    517  */
    518 const char *
    519 dladm_bw2str(int64_t bw, char *buf)
    520 {
    521 	int kbps, mbps;
    522 
    523 	kbps = (bw%1000000)/1000;
    524 	mbps = bw/1000000;
    525 	if (kbps != 0) {
    526 		if (mbps == 0)
    527 			(void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
    528 		else
    529 			(void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
    530 			    kbps);
    531 	} else {
    532 		(void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
    533 	}
    534 
    535 	return (buf);
    536 }
    537 
    538 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
    539 
    540 static int
    541 i_dladm_lock_db(const char *lock_file, short type)
    542 {
    543 	int	lock_fd;
    544 	struct	flock lock;
    545 
    546 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
    547 	    LOCK_DB_PERMS)) < 0)
    548 		return (-1);
    549 
    550 	lock.l_type = type;
    551 	lock.l_whence = SEEK_SET;
    552 	lock.l_start = 0;
    553 	lock.l_len = 0;
    554 
    555 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
    556 		int err = errno;
    557 
    558 		(void) close(lock_fd);
    559 		(void) unlink(lock_file);
    560 		errno = err;
    561 		return (-1);
    562 	}
    563 	return (lock_fd);
    564 }
    565 
    566 static void
    567 i_dladm_unlock_db(const char *lock_file, int fd)
    568 {
    569 	struct flock lock;
    570 
    571 	if (fd < 0)
    572 		return;
    573 
    574 	lock.l_type = F_UNLCK;
    575 	lock.l_whence = SEEK_SET;
    576 	lock.l_start = 0;
    577 	lock.l_len = 0;
    578 
    579 	(void) fcntl(fd, F_SETLKW, &lock);
    580 	(void) close(fd);
    581 	(void) unlink(lock_file);
    582 }
    583 
    584 /*
    585  * Given a link class, returns its class string.
    586  */
    587 const char *
    588 dladm_class2str(datalink_class_t class, char *buf)
    589 {
    590 	const char *s;
    591 
    592 	switch (class) {
    593 	case DATALINK_CLASS_PHYS:
    594 		s = "phys";
    595 		break;
    596 	case DATALINK_CLASS_VLAN:
    597 		s = "vlan";
    598 		break;
    599 	case DATALINK_CLASS_AGGR:
    600 		s = "aggr";
    601 		break;
    602 	case DATALINK_CLASS_VNIC:
    603 		s = "vnic";
    604 		break;
    605 	case DATALINK_CLASS_ETHERSTUB:
    606 		s = "etherstub";
    607 		break;
    608 	case DATALINK_CLASS_IPTUN:
    609 		s = "iptun";
    610 		break;
    611 	case DATALINK_CLASS_SIMNET:
    612 		s = "simnet";
    613 		break;
    614 	case DATALINK_CLASS_BRIDGE:
    615 		s = "bridge";
    616 		break;
    617 	default:
    618 		s = "unknown";
    619 		break;
    620 	}
    621 
    622 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
    623 	return (buf);
    624 }
    625 
    626 /*
    627  * Given a physical link media type, returns its media type string.
    628  */
    629 const char *
    630 dladm_media2str(uint32_t media, char *buf)
    631 {
    632 	const char *s = "--";
    633 	media_type_t *mt;
    634 	int idx;
    635 
    636 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
    637 		mt = media_type_table + idx;
    638 		if (mt->media_type == media) {
    639 			s = mt->media_type_str;
    640 			break;
    641 		}
    642 	}
    643 
    644 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
    645 	return (buf);
    646 }
    647 
    648 /*
    649  * Given a physical link media type string, returns its media type constant.
    650  */
    651 uint32_t
    652 dladm_str2media(const char *buf)
    653 {
    654 	media_type_t *mt;
    655 	int idx;
    656 
    657 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
    658 		mt = media_type_table + idx;
    659 		if (strcasecmp(buf, mt->media_type_str) == 0)
    660 			return (mt->media_type);
    661 	}
    662 
    663 	return (DL_OTHER);
    664 }
    665 
    666 dladm_status_t
    667 i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
    668     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
    669     void *arg, boolean_t writeop)
    670 {
    671 	dladm_status_t	status = DLADM_STATUS_OK;
    672 	FILE		*fp, *nfp = NULL;
    673 	char		lock[MAXPATHLEN];
    674 	char		file[MAXPATHLEN];
    675 	char		newfile[MAXPATHLEN];
    676 	char		*db_basename;
    677 	int		nfd, lock_fd;
    678 
    679 	/*
    680 	 * If we are called from a boot script such as net-physical,
    681 	 * it's quite likely that the root fs is still not writable.
    682 	 * For this case, it's ok for the lock creation to fail since
    683 	 * no one else could be accessing our configuration file.
    684 	 */
    685 	db_basename = strrchr(db_file, '/');
    686 	if (db_basename == NULL || db_basename[1] == '\0')
    687 		return (dladm_errno2status(EINVAL));
    688 	db_basename++;
    689 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
    690 	if ((lock_fd = i_dladm_lock_db
    691 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
    692 		return (dladm_errno2status(errno));
    693 
    694 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
    695 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
    696 		int	err = errno;
    697 
    698 		i_dladm_unlock_db(lock, lock_fd);
    699 		if (err == ENOENT)
    700 			return (DLADM_STATUS_DBNOTFOUND);
    701 
    702 		return (dladm_errno2status(err));
    703 	}
    704 
    705 	if (writeop) {
    706 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
    707 		    dladm_rootdir, db_file);
    708 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
    709 		    db_perms)) < 0) {
    710 			(void) fclose(fp);
    711 			i_dladm_unlock_db(lock, lock_fd);
    712 			return (dladm_errno2status(errno));
    713 		}
    714 
    715 		if ((nfp = fdopen(nfd, "w")) == NULL) {
    716 			(void) close(nfd);
    717 			(void) fclose(fp);
    718 			(void) unlink(newfile);
    719 			i_dladm_unlock_db(lock, lock_fd);
    720 			return (dladm_errno2status(errno));
    721 		}
    722 	}
    723 	status = (*process_db)(handle, arg, fp, nfp);
    724 	if (!writeop || status != DLADM_STATUS_OK)
    725 		goto done;
    726 
    727 	/*
    728 	 * Configuration files need to be owned by the 'dladm' user.
    729 	 * If we are invoked by root, the file ownership needs to be fixed.
    730 	 */
    731 	if (getuid() == 0 || geteuid() == 0) {
    732 		if (fchown(nfd, UID_DLADM, GID_SYS) < 0) {
    733 			status = dladm_errno2status(errno);
    734 			goto done;
    735 		}
    736 	}
    737 
    738 	if (fflush(nfp) == EOF) {
    739 		status = dladm_errno2status(errno);
    740 		goto done;
    741 	}
    742 	(void) fclose(fp);
    743 	(void) fclose(nfp);
    744 
    745 	if (rename(newfile, file) < 0) {
    746 		(void) unlink(newfile);
    747 		i_dladm_unlock_db(lock, lock_fd);
    748 		return (dladm_errno2status(errno));
    749 	}
    750 
    751 	i_dladm_unlock_db(lock, lock_fd);
    752 	return (DLADM_STATUS_OK);
    753 
    754 done:
    755 	if (nfp != NULL) {
    756 		(void) fclose(nfp);
    757 		if (status != DLADM_STATUS_OK)
    758 			(void) unlink(newfile);
    759 	}
    760 	(void) fclose(fp);
    761 	i_dladm_unlock_db(lock, lock_fd);
    762 	return (status);
    763 }
    764 
    765 dladm_status_t
    766 dladm_set_rootdir(const char *rootdir)
    767 {
    768 	DIR	*dp;
    769 
    770 	if (rootdir == NULL || *rootdir != '/' ||
    771 	    (dp = opendir(rootdir)) == NULL)
    772 		return (DLADM_STATUS_BADARG);
    773 
    774 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
    775 	(void) closedir(dp);
    776 	return (DLADM_STATUS_OK);
    777 }
    778 
    779 boolean_t
    780 dladm_valid_linkname(const char *link)
    781 {
    782 	size_t		len = strlen(link);
    783 	const char	*cp;
    784 
    785 	if (len >= MAXLINKNAMELEN)
    786 		return (B_FALSE);
    787 
    788 	/*
    789 	 * The link name cannot start with a digit and must end with a digit.
    790 	 */
    791 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
    792 		return (B_FALSE);
    793 
    794 	/*
    795 	 * The legal characters in a link name are:
    796 	 * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
    797 	 */
    798 	for (cp = link; *cp != '\0'; cp++) {
    799 		if ((isalnum(*cp) == 0) && (*cp != '_') && (*cp != '.'))
    800 			return (B_FALSE);
    801 	}
    802 
    803 	return (B_TRUE);
    804 }
    805 
    806 /*
    807  * Convert priority string to a value.
    808  */
    809 dladm_status_t
    810 dladm_str2pri(char *token, mac_priority_level_t *pri)
    811 {
    812 	if (strlen(token) == strlen("low") &&
    813 	    strncasecmp(token, "low", strlen("low")) == 0) {
    814 		*pri = MPL_LOW;
    815 	} else if (strlen(token) == strlen("medium") &&
    816 	    strncasecmp(token, "medium", strlen("medium")) == 0) {
    817 		*pri = MPL_MEDIUM;
    818 	} else if (strlen(token) == strlen("high") &&
    819 	    strncasecmp(token, "high", strlen("high")) == 0) {
    820 		*pri = MPL_HIGH;
    821 	} else {
    822 		return (DLADM_STATUS_BADVAL);
    823 	}
    824 	return (DLADM_STATUS_OK);
    825 }
    826 
    827 /*
    828  * Convert priority value to a string.
    829  */
    830 const char *
    831 dladm_pri2str(mac_priority_level_t pri, char *buf)
    832 {
    833 	const char	*s;
    834 
    835 	switch (pri) {
    836 	case MPL_LOW:
    837 		s = "low";
    838 		break;
    839 	case MPL_MEDIUM:
    840 		s = "medium";
    841 		break;
    842 	case MPL_HIGH:
    843 		s = "high";
    844 		break;
    845 	default:
    846 		s = "--";
    847 		break;
    848 	}
    849 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
    850 	return (buf);
    851 }
    852 
    853 /*
    854  * Convert protect string to a value.
    855  */
    856 dladm_status_t
    857 dladm_str2protect(char *token, uint32_t *ptype)
    858 {
    859 	link_protect_t	*lp;
    860 	int		i;
    861 
    862 	for (i = 0; i < LPTYPES; i++) {
    863 		lp = &link_protect_types[i];
    864 		if (strcmp(token, lp->lp_name) == 0) {
    865 			*ptype = lp->lp_type;
    866 			return (DLADM_STATUS_OK);
    867 		}
    868 	}
    869 	return (DLADM_STATUS_BADVAL);
    870 }
    871 
    872 /*
    873  * Convert protect value to a string.
    874  */
    875 const char *
    876 dladm_protect2str(uint32_t ptype, char *buf)
    877 {
    878 	const char	*s = "--";
    879 	link_protect_t	*lp;
    880 	int		i;
    881 
    882 	for (i = 0; i < LPTYPES; i++) {
    883 		lp = &link_protect_types[i];
    884 		if (lp->lp_type == ptype) {
    885 			s = lp->lp_name;
    886 			break;
    887 		}
    888 	}
    889 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
    890 	return (buf);
    891 }
    892 
    893 /*
    894  * Convert an IPv4 address to/from a string.
    895  */
    896 const char *
    897 dladm_ipv4addr2str(void *addr, char *buf)
    898 {
    899 	if (inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN) == NULL)
    900 		buf[0] = 0;
    901 
    902 	return (buf);
    903 }
    904 
    905 dladm_status_t
    906 dladm_str2ipv4addr(char *token, void *addr)
    907 {
    908 	return (inet_pton(AF_INET, token, addr) == 1 ?
    909 	    DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
    910 }
    911 
    912 /*
    913  * Find the set bits in a mask.
    914  * This is used for expanding a bitmask into individual sub-masks
    915  * which can be used for further processing.
    916  */
    917 void
    918 dladm_find_setbits32(uint32_t mask, uint32_t *list, uint32_t *cnt)
    919 {
    920 	int	i, c = 0;
    921 
    922 	for (i = 0; i < 32; i++) {
    923 		if (((1 << i) & mask) != 0)
    924 			list[c++] = 1 << i;
    925 	}
    926 	*cnt = c;
    927 }
    928 
    929 void
    930 dladm_free_args(dladm_arg_list_t *list)
    931 {
    932 	if (list != NULL) {
    933 		free(list->al_buf);
    934 		free(list);
    935 	}
    936 }
    937 
    938 dladm_status_t
    939 dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
    940 {
    941 	dladm_arg_list_t	*list;
    942 	dladm_arg_info_t	*aip;
    943 	char			*buf, *curr;
    944 	int			len, i;
    945 
    946 	if (str == NULL)
    947 		return (DLADM_STATUS_BADVAL);
    948 
    949 	if (str[0] == '\0')
    950 		return (DLADM_STATUS_OK);
    951 
    952 	list = malloc(sizeof (dladm_arg_list_t));
    953 	if (list == NULL)
    954 		return (dladm_errno2status(errno));
    955 
    956 	list->al_count = 0;
    957 	list->al_buf = buf = strdup(str);
    958 	if (buf == NULL)
    959 		return (dladm_errno2status(errno));
    960 
    961 	curr = buf;
    962 	len = strlen(buf);
    963 	aip = NULL;
    964 	for (i = 0; i < len; i++) {
    965 		char		c = buf[i];
    966 		boolean_t	match = (c == '=' || c == ',');
    967 
    968 		if (!match && i != len - 1)
    969 			continue;
    970 
    971 		if (match) {
    972 			buf[i] = '\0';
    973 			if (*curr == '\0')
    974 				goto fail;
    975 		}
    976 
    977 		if (aip != NULL && c != '=') {
    978 			if (aip->ai_count > DLADM_MAX_ARG_VALS)
    979 				goto fail;
    980 
    981 			if (novalues)
    982 				goto fail;
    983 
    984 			aip->ai_val[aip->ai_count] = curr;
    985 			aip->ai_count++;
    986 		} else {
    987 			if (list->al_count > DLADM_MAX_ARG_VALS)
    988 				goto fail;
    989 
    990 			aip = &list->al_info[list->al_count];
    991 			aip->ai_name = curr;
    992 			aip->ai_count = 0;
    993 			list->al_count++;
    994 			if (c == ',')
    995 				aip = NULL;
    996 		}
    997 		curr = buf + i + 1;
    998 	}
    999 
   1000 	*listp = list;
   1001 	return (DLADM_STATUS_OK);
   1002 
   1003 fail:
   1004 	dladm_free_args(list);
   1005 	return (DLADM_STATUS_FAILED);
   1006 }
   1007