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