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