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