xref: /illumos-gate/usr/src/lib/libdladm/common/libdladm.c (revision 801093905f15f0f1a50095474e8a3f0e5b0cb126)
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <unistd.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <strings.h>
30 #include <dirent.h>
31 #include <stdlib.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <sys/dld.h>
37 #include <sys/dld_ioc.h>
38 #include <libdladm_impl.h>
39 #include <libintl.h>
40 #include <libdlpi.h>
41 
42 static char	dladm_rootdir[MAXPATHLEN] = "/";
43 
44 typedef struct media_type_desc {
45 	uint32_t	media_type;
46 #define	MAX_MEDIA_TYPE_STRING	32
47 	const char	media_type_str[MAX_MEDIA_TYPE_STRING];
48 } media_type_t;
49 
50 static media_type_t media_type_table[] =  {
51 	{ DL_ETHER,	"Ethernet" },
52 	{ DL_WIFI,	"WiFi" },
53 	{ DL_IB,	"Infiniband" },
54 	{ DL_IPV4,	"IPv4Tunnel" },
55 	{ DL_IPV6,	"IPv6Tunnel" },
56 	{ DL_6TO4,	"6to4Tunnel" },
57 	{ DL_CSMACD,	"CSMA/CD" },
58 	{ DL_TPB,	"TokenBus" },
59 	{ DL_TPR,	"TokenRing" },
60 	{ DL_METRO,	"MetroNet" },
61 	{ DL_HDLC,	"HDLC" },
62 	{ DL_CHAR,	"SyncCharacter" },
63 	{ DL_CTCA,	"CTCA" },
64 	{ DL_FDDI, 	"FDDI" },
65 	{ DL_FC, 	"FiberChannel" },
66 	{ DL_ATM, 	"ATM" },
67 	{ DL_IPATM, 	"ATM(ClassicIP)" },
68 	{ DL_X25, 	"X.25" },
69 	{ DL_IPX25, 	"X.25(ClassicIP)" },
70 	{ DL_ISDN, 	"ISDN" },
71 	{ DL_HIPPI, 	"HIPPI" },
72 	{ DL_100VG, 	"100BaseVGEthernet" },
73 	{ DL_100VGTPR, 	"100BaseVGTokenRing" },
74 	{ DL_ETH_CSMA, 	"IEEE802.3" },
75 	{ DL_100BT, 	"100BaseT" },
76 	{ DL_FRAME, 	"FrameRelay" },
77 	{ DL_MPFRAME, 	"MPFrameRelay" },
78 	{ DL_ASYNC, 	"AsyncCharacter" },
79 	{ DL_IPNET, 	"IPNET" },
80 	{ DL_OTHER, 	"Other" }
81 };
82 #define	MEDIATYPECOUNT	(sizeof (media_type_table) / sizeof (media_type_t))
83 
84 typedef struct {
85 	uint32_t	lp_type;
86 	char		*lp_name;
87 } link_protect_t;
88 
89 static link_protect_t link_protect_types[] = {
90 	{ MPT_MACNOSPOOF, "mac-nospoof" },
91 	{ MPT_RESTRICTED, "restricted" },
92 	{ MPT_IPNOSPOOF, "ip-nospoof" },
93 	{ MPT_DHCPNOSPOOF, "dhcp-nospoof" }
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 1200K";
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 	case DLADM_STATUS_POOLCPU:
385 		s = "pool and cpus property are mutually exclusive";
386 		break;
387 	case DLADM_STATUS_INVALID_PORT_INSTANCE:
388 		s = "invalid IB phys link";
389 		break;
390 	case DLADM_STATUS_PORT_IS_DOWN:
391 		s = "port is down";
392 		break;
393 	case DLADM_STATUS_PARTITION_EXISTS:
394 		s = "partition already exists";
395 		break;
396 	case DLADM_STATUS_PKEY_NOT_PRESENT:
397 		s = "PKEY is not present on the port";
398 		break;
399 	case DLADM_STATUS_INVALID_PKEY:
400 		s = "invalid PKEY";
401 		break;
402 	case DLADM_STATUS_NO_IB_HW_RESOURCE:
403 		s = "IB internal resource not available";
404 		break;
405 	case DLADM_STATUS_INVALID_PKEY_TBL_SIZE:
406 		s = "invalid PKEY table size";
407 		break;
408 	case DLADM_STATUS_PORT_NOPROTO:
409 		s = "local or remote port requires transport";
410 		break;
411 	default:
412 		s = "<unknown error>";
413 		break;
414 	}
415 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
416 	return (buf);
417 }
418 
419 /*
420  * Convert a unix errno to a dladm_status_t.
421  * We only convert errnos that are likely to be encountered. All others
422  * are mapped to DLADM_STATUS_FAILED.
423  */
424 dladm_status_t
425 dladm_errno2status(int err)
426 {
427 	switch (err) {
428 	case 0:
429 		return (DLADM_STATUS_OK);
430 	case EINVAL:
431 		return (DLADM_STATUS_BADARG);
432 	case EEXIST:
433 		return (DLADM_STATUS_EXIST);
434 	case ENOENT:
435 		return (DLADM_STATUS_NOTFOUND);
436 	case ENOSPC:
437 		return (DLADM_STATUS_TOOSMALL);
438 	case ENOMEM:
439 		return (DLADM_STATUS_NOMEM);
440 	case ENOTSUP:
441 		return (DLADM_STATUS_NOTSUP);
442 	case ENETDOWN:
443 		return (DLADM_STATUS_NONOTIF);
444 	case EACCES:
445 	case EPERM:
446 		return (DLADM_STATUS_DENIED);
447 	case EIO:
448 		return (DLADM_STATUS_IOERR);
449 	case EBUSY:
450 		return (DLADM_STATUS_LINKBUSY);
451 	case EAGAIN:
452 		return (DLADM_STATUS_TRYAGAIN);
453 	case ENOTEMPTY:
454 		return (DLADM_STATUS_FLOW_EXISTS);
455 	case EOPNOTSUPP:
456 		return (DLADM_STATUS_FLOW_INCOMPATIBLE);
457 	case EALREADY:
458 		return (DLADM_STATUS_FLOW_IDENTICAL);
459 	case EADDRINUSE:
460 		return (DLADM_STATUS_ADDRINUSE);
461 	default:
462 		return (DLADM_STATUS_FAILED);
463 	}
464 }
465 
466 boolean_t
467 dladm_str2interval(char *oarg, uint32_t *interval)
468 {
469 	int		val;
470 	char		*endp = NULL;
471 
472 	errno = 0;
473 	val = strtol(oarg, &endp, 10);
474 	if (errno != 0 || val <= 0 || *endp != '\0')
475 		return (B_FALSE);
476 
477 	*interval = val;
478 
479 	return (B_TRUE);
480 }
481 
482 dladm_status_t
483 dladm_str2bw(char *oarg, uint64_t *bw)
484 {
485 	char		*endp = NULL;
486 	int64_t		n;
487 	int		mult = 1;
488 
489 	n = strtoull(oarg, &endp, 10);
490 
491 	if ((errno != 0) || (strlen(endp) > 1))
492 		return (DLADM_STATUS_BADARG);
493 
494 	if (n < 0)
495 		return (DLADM_STATUS_BADVAL);
496 
497 	switch (*endp) {
498 	case 'k':
499 	case 'K':
500 		mult = 1000;
501 		break;
502 	case 'm':
503 	case 'M':
504 	case '\0':
505 		mult = 1000000;
506 		break;
507 	case 'g':
508 	case 'G':
509 		mult = 1000000000;
510 		break;
511 	case '%':
512 		/*
513 		 * percentages not supported for now,
514 		 * see RFE 6540675
515 		 */
516 		return (DLADM_STATUS_NOTSUP);
517 	default:
518 		return (DLADM_STATUS_BADVAL);
519 	}
520 
521 	*bw = n * mult;
522 
523 	/* check for overflow */
524 	if (*bw / mult != n)
525 		return (DLADM_STATUS_BADARG);
526 
527 	return (DLADM_STATUS_OK);
528 }
529 
530 /*
531  * Convert bandwidth in bps to a string in mpbs.  For values greater
532  * than 1mbps or 1000000, print a whole mbps value.  For values that
533  * have fractional Mbps in whole Kbps , print the bandwidth in a manner
534  * simlilar to a floating point format.
535  *
536  *        bps       string
537  *          0            0
538  *        100            0
539  *       2000        0.002
540  *     431000        0.431
541  *    1000000            1
542  *    1030000        1.030
543  *  100000000          100
544  */
545 const char *
546 dladm_bw2str(int64_t bw, char *buf)
547 {
548 	int kbps, mbps;
549 
550 	kbps = (bw%1000000)/1000;
551 	mbps = bw/1000000;
552 	if (kbps != 0) {
553 		if (mbps == 0)
554 			(void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
555 		else
556 			(void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
557 			    kbps);
558 	} else {
559 		(void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
560 	}
561 
562 	return (buf);
563 }
564 
565 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
566 
567 static int
568 i_dladm_lock_db(const char *lock_file, short type)
569 {
570 	int	lock_fd;
571 	struct	flock lock;
572 
573 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
574 	    LOCK_DB_PERMS)) < 0)
575 		return (-1);
576 
577 	lock.l_type = type;
578 	lock.l_whence = SEEK_SET;
579 	lock.l_start = 0;
580 	lock.l_len = 0;
581 
582 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
583 		int err = errno;
584 
585 		(void) close(lock_fd);
586 		(void) unlink(lock_file);
587 		errno = err;
588 		return (-1);
589 	}
590 	return (lock_fd);
591 }
592 
593 static void
594 i_dladm_unlock_db(const char *lock_file, int fd)
595 {
596 	struct flock lock;
597 
598 	if (fd < 0)
599 		return;
600 
601 	lock.l_type = F_UNLCK;
602 	lock.l_whence = SEEK_SET;
603 	lock.l_start = 0;
604 	lock.l_len = 0;
605 
606 	(void) fcntl(fd, F_SETLKW, &lock);
607 	(void) close(fd);
608 	(void) unlink(lock_file);
609 }
610 
611 /*
612  * Given a link class, returns its class string.
613  */
614 const char *
615 dladm_class2str(datalink_class_t class, char *buf)
616 {
617 	const char *s;
618 
619 	switch (class) {
620 	case DATALINK_CLASS_PHYS:
621 		s = "phys";
622 		break;
623 	case DATALINK_CLASS_VLAN:
624 		s = "vlan";
625 		break;
626 	case DATALINK_CLASS_AGGR:
627 		s = "aggr";
628 		break;
629 	case DATALINK_CLASS_VNIC:
630 		s = "vnic";
631 		break;
632 	case DATALINK_CLASS_ETHERSTUB:
633 		s = "etherstub";
634 		break;
635 	case DATALINK_CLASS_IPTUN:
636 		s = "iptun";
637 		break;
638 	case DATALINK_CLASS_SIMNET:
639 		s = "simnet";
640 		break;
641 	case DATALINK_CLASS_BRIDGE:
642 		s = "bridge";
643 		break;
644 	case DATALINK_CLASS_PART:
645 		s = "part";
646 		break;
647 	default:
648 		s = "unknown";
649 		break;
650 	}
651 
652 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
653 	return (buf);
654 }
655 
656 /*
657  * Given a physical link media type, returns its media type string.
658  */
659 const char *
660 dladm_media2str(uint32_t media, char *buf)
661 {
662 	const char *s = "--";
663 	media_type_t *mt;
664 	int idx;
665 
666 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
667 		mt = media_type_table + idx;
668 		if (mt->media_type == media) {
669 			s = mt->media_type_str;
670 			break;
671 		}
672 	}
673 
674 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
675 	return (buf);
676 }
677 
678 /*
679  * Given a physical link media type string, returns its media type constant.
680  */
681 uint32_t
682 dladm_str2media(const char *buf)
683 {
684 	media_type_t *mt;
685 	int idx;
686 
687 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
688 		mt = media_type_table + idx;
689 		if (strcasecmp(buf, mt->media_type_str) == 0)
690 			return (mt->media_type);
691 	}
692 
693 	return (DL_OTHER);
694 }
695 
696 dladm_status_t
697 i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
698     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
699     void *arg, boolean_t writeop)
700 {
701 	dladm_status_t	status = DLADM_STATUS_OK;
702 	FILE		*fp, *nfp = NULL;
703 	char		lock[MAXPATHLEN];
704 	char		file[MAXPATHLEN];
705 	char		newfile[MAXPATHLEN];
706 	char		*db_basename;
707 	int		nfd, lock_fd;
708 
709 	/*
710 	 * If we are called from a boot script such as net-physical,
711 	 * it's quite likely that the root fs is still not writable.
712 	 * For this case, it's ok for the lock creation to fail since
713 	 * no one else could be accessing our configuration file.
714 	 */
715 	db_basename = strrchr(db_file, '/');
716 	if (db_basename == NULL || db_basename[1] == '\0')
717 		return (dladm_errno2status(EINVAL));
718 	db_basename++;
719 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
720 	if ((lock_fd = i_dladm_lock_db
721 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
722 		return (dladm_errno2status(errno));
723 
724 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
725 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
726 		int	err = errno;
727 
728 		i_dladm_unlock_db(lock, lock_fd);
729 		if (err == ENOENT)
730 			return (DLADM_STATUS_DBNOTFOUND);
731 
732 		return (dladm_errno2status(err));
733 	}
734 
735 	if (writeop) {
736 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
737 		    dladm_rootdir, db_file);
738 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
739 		    db_perms)) < 0) {
740 			(void) fclose(fp);
741 			i_dladm_unlock_db(lock, lock_fd);
742 			return (dladm_errno2status(errno));
743 		}
744 
745 		if ((nfp = fdopen(nfd, "w")) == NULL) {
746 			(void) close(nfd);
747 			(void) fclose(fp);
748 			(void) unlink(newfile);
749 			i_dladm_unlock_db(lock, lock_fd);
750 			return (dladm_errno2status(errno));
751 		}
752 	}
753 	status = (*process_db)(handle, arg, fp, nfp);
754 	if (!writeop || status != DLADM_STATUS_OK)
755 		goto done;
756 
757 	/* Set permissions on file to db_perms */
758 	if (fchmod(nfd, db_perms) < 0) {
759 		status = dladm_errno2status(errno);
760 		goto done;
761 	}
762 
763 	/*
764 	 * Configuration files need to be owned by the 'dladm' user and
765 	 * 'netadm' group.
766 	 */
767 	if (fchown(nfd, UID_DLADM, GID_NETADM) < 0) {
768 		status = dladm_errno2status(errno);
769 		goto done;
770 	}
771 
772 	if (fflush(nfp) == EOF) {
773 		status = dladm_errno2status(errno);
774 		goto done;
775 	}
776 	(void) fclose(fp);
777 	(void) fclose(nfp);
778 
779 	if (rename(newfile, file) < 0) {
780 		(void) unlink(newfile);
781 		i_dladm_unlock_db(lock, lock_fd);
782 		return (dladm_errno2status(errno));
783 	}
784 
785 	i_dladm_unlock_db(lock, lock_fd);
786 	return (DLADM_STATUS_OK);
787 
788 done:
789 	if (nfp != NULL) {
790 		(void) fclose(nfp);
791 		if (status != DLADM_STATUS_OK)
792 			(void) unlink(newfile);
793 	}
794 	(void) fclose(fp);
795 	i_dladm_unlock_db(lock, lock_fd);
796 	return (status);
797 }
798 
799 dladm_status_t
800 dladm_set_rootdir(const char *rootdir)
801 {
802 	DIR	*dp;
803 
804 	if (rootdir == NULL || *rootdir != '/' ||
805 	    (dp = opendir(rootdir)) == NULL)
806 		return (DLADM_STATUS_BADARG);
807 
808 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
809 	(void) closedir(dp);
810 	return (DLADM_STATUS_OK);
811 }
812 
813 boolean_t
814 dladm_valid_linkname(const char *link)
815 {
816 	size_t		len = strlen(link);
817 	const char	*cp;
818 
819 	if (len >= MAXLINKNAMELEN)
820 		return (B_FALSE);
821 
822 	/*
823 	 * The link name cannot start with a digit and must end with a digit.
824 	 */
825 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
826 		return (B_FALSE);
827 
828 	/*
829 	 * The legal characters in a link name are:
830 	 * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
831 	 */
832 	for (cp = link; *cp != '\0'; cp++) {
833 		if ((isalnum(*cp) == 0) && (*cp != '_') && (*cp != '.'))
834 			return (B_FALSE);
835 	}
836 
837 	return (B_TRUE);
838 }
839 
840 /*
841  * Convert priority string to a value.
842  */
843 dladm_status_t
844 dladm_str2pri(char *token, mac_priority_level_t *pri)
845 {
846 	if (strlen(token) == strlen("low") &&
847 	    strncasecmp(token, "low", strlen("low")) == 0) {
848 		*pri = MPL_LOW;
849 	} else if (strlen(token) == strlen("medium") &&
850 	    strncasecmp(token, "medium", strlen("medium")) == 0) {
851 		*pri = MPL_MEDIUM;
852 	} else if (strlen(token) == strlen("high") &&
853 	    strncasecmp(token, "high", strlen("high")) == 0) {
854 		*pri = MPL_HIGH;
855 	} else {
856 		return (DLADM_STATUS_BADVAL);
857 	}
858 	return (DLADM_STATUS_OK);
859 }
860 
861 /*
862  * Convert priority value to a string.
863  */
864 const char *
865 dladm_pri2str(mac_priority_level_t pri, char *buf)
866 {
867 	const char	*s;
868 
869 	switch (pri) {
870 	case MPL_LOW:
871 		s = "low";
872 		break;
873 	case MPL_MEDIUM:
874 		s = "medium";
875 		break;
876 	case MPL_HIGH:
877 		s = "high";
878 		break;
879 	default:
880 		s = "--";
881 		break;
882 	}
883 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
884 	return (buf);
885 }
886 
887 /*
888  * Convert protect string to a value.
889  */
890 dladm_status_t
891 dladm_str2protect(char *token, uint32_t *ptype)
892 {
893 	link_protect_t	*lp;
894 	int		i;
895 
896 	for (i = 0; i < LPTYPES; i++) {
897 		lp = &link_protect_types[i];
898 		if (strcmp(token, lp->lp_name) == 0) {
899 			*ptype = lp->lp_type;
900 			return (DLADM_STATUS_OK);
901 		}
902 	}
903 	return (DLADM_STATUS_BADVAL);
904 }
905 
906 /*
907  * Convert protect value to a string.
908  */
909 const char *
910 dladm_protect2str(uint32_t ptype, char *buf)
911 {
912 	const char	*s = "--";
913 	link_protect_t	*lp;
914 	int		i;
915 
916 	for (i = 0; i < LPTYPES; i++) {
917 		lp = &link_protect_types[i];
918 		if (lp->lp_type == ptype) {
919 			s = lp->lp_name;
920 			break;
921 		}
922 	}
923 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
924 	return (buf);
925 }
926 
927 /*
928  * Convert an IPv4 address to/from a string.
929  */
930 const char *
931 dladm_ipv4addr2str(void *addr, char *buf)
932 {
933 	if (inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN) == NULL)
934 		buf[0] = '\0';
935 
936 	return (buf);
937 }
938 
939 dladm_status_t
940 dladm_str2ipv4addr(char *token, void *addr)
941 {
942 	return (inet_pton(AF_INET, token, addr) == 1 ?
943 	    DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
944 }
945 
946 const char *
947 dladm_ipv6addr2str(void *addr, char *buf)
948 {
949 	if (inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN) == NULL)
950 		buf[0] = '\0';
951 
952 	return (buf);
953 }
954 
955 dladm_status_t
956 dladm_str2ipv6addr(char *token, void *addr)
957 {
958 	return (inet_pton(AF_INET6, token, addr) == 1 ?
959 	    DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
960 }
961 
962 /*
963  * Find the set bits in a mask.
964  * This is used for expanding a bitmask into individual sub-masks
965  * which can be used for further processing.
966  */
967 void
968 dladm_find_setbits32(uint32_t mask, uint32_t *list, uint32_t *cnt)
969 {
970 	int	i, c = 0;
971 
972 	for (i = 0; i < 32; i++) {
973 		if (((1 << i) & mask) != 0)
974 			list[c++] = 1 << i;
975 	}
976 	*cnt = c;
977 }
978 
979 void
980 dladm_free_args(dladm_arg_list_t *list)
981 {
982 	if (list != NULL) {
983 		free(list->al_buf);
984 		free(list);
985 	}
986 }
987 
988 dladm_status_t
989 dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
990 {
991 	dladm_arg_list_t	*list;
992 	dladm_arg_info_t	*aip;
993 	char			*buf, *curr;
994 	int			len, i;
995 
996 	if (str == NULL)
997 		return (DLADM_STATUS_BADVAL);
998 
999 	if (str[0] == '\0')
1000 		return (DLADM_STATUS_OK);
1001 
1002 	list = malloc(sizeof (dladm_arg_list_t));
1003 	if (list == NULL)
1004 		return (dladm_errno2status(errno));
1005 
1006 	list->al_count = 0;
1007 	list->al_buf = buf = strdup(str);
1008 	if (buf == NULL)
1009 		return (dladm_errno2status(errno));
1010 
1011 	curr = buf;
1012 	len = strlen(buf);
1013 	aip = NULL;
1014 	for (i = 0; i < len; i++) {
1015 		char		c = buf[i];
1016 		boolean_t	match = (c == '=' || c == ',');
1017 
1018 		if (!match && i != len - 1)
1019 			continue;
1020 
1021 		if (match) {
1022 			buf[i] = '\0';
1023 			if (*curr == '\0')
1024 				goto fail;
1025 		}
1026 
1027 		if (aip != NULL && c != '=') {
1028 			if (aip->ai_count > DLADM_MAX_ARG_VALS)
1029 				goto fail;
1030 
1031 			if (novalues)
1032 				goto fail;
1033 
1034 			aip->ai_val[aip->ai_count] = curr;
1035 			aip->ai_count++;
1036 		} else {
1037 			if (list->al_count > DLADM_MAX_ARG_VALS)
1038 				goto fail;
1039 
1040 			aip = &list->al_info[list->al_count];
1041 			aip->ai_name = curr;
1042 			aip->ai_count = 0;
1043 			list->al_count++;
1044 			if (c == ',')
1045 				aip = NULL;
1046 		}
1047 		curr = buf + i + 1;
1048 	}
1049 
1050 	*listp = list;
1051 	return (DLADM_STATUS_OK);
1052 
1053 fail:
1054 	dladm_free_args(list);
1055 	return (DLADM_STATUS_FAILED);
1056 }
1057