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