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 #include <libdllink.h>
  42 
  43 static char     dladm_rootdir[MAXPATHLEN] = "/";
  44 
  45 typedef struct media_type_desc {
  46         uint32_t        media_type;
  47 #define MAX_MEDIA_TYPE_STRING   32
  48         const char      media_type_str[MAX_MEDIA_TYPE_STRING];
  49 } media_type_t;
  50 
  51 static media_type_t media_type_table[] =  {
  52         { DL_ETHER,     "Ethernet" },
  53         { DL_WIFI,      "WiFi" },
  54         { DL_IB,        "Infiniband" },
  55         { DL_IPV4,      "IPv4Tunnel" },
  56         { DL_IPV6,      "IPv6Tunnel" },
  57         { DL_6TO4,      "6to4Tunnel" },
  58         { DL_CSMACD,    "CSMA/CD" },
  59         { DL_TPB,       "TokenBus" },
  60         { DL_TPR,       "TokenRing" },
  61         { DL_METRO,     "MetroNet" },
  62         { DL_HDLC,      "HDLC" },
  63         { DL_CHAR,      "SyncCharacter" },
  64         { DL_CTCA,      "CTCA" },
  65         { DL_FDDI,      "FDDI" },
  66         { DL_FC,        "FiberChannel" },
  67         { DL_ATM,       "ATM" },
  68         { DL_IPATM,     "ATM(ClassicIP)" },
  69         { DL_X25,       "X.25" },
  70         { DL_IPX25,     "X.25(ClassicIP)" },
  71         { DL_ISDN,      "ISDN" },
  72         { DL_HIPPI,     "HIPPI" },
  73         { DL_100VG,     "100BaseVGEthernet" },
  74         { DL_100VGTPR,  "100BaseVGTokenRing" },
  75         { DL_ETH_CSMA,  "IEEE802.3" },
  76         { DL_100BT,     "100BaseT" },
  77         { DL_FRAME,     "FrameRelay" },
  78         { DL_MPFRAME,   "MPFrameRelay" },
  79         { DL_ASYNC,     "AsyncCharacter" },
  80         { DL_IPNET,     "IPNET" },
  81         { DL_OTHER,     "Other" }
  82 };
  83 #define MEDIATYPECOUNT  (sizeof (media_type_table) / sizeof (media_type_t))
  84 
  85 typedef struct {
  86         uint32_t        lp_type;
  87         char            *lp_name;
  88 } link_protect_t;
  89 
  90 static link_protect_t link_protect_types[] = {
  91         { MPT_MACNOSPOOF, "mac-nospoof" },
  92         { MPT_RESTRICTED, "restricted" },
  93         { MPT_IPNOSPOOF, "ip-nospoof" },
  94         { MPT_DHCPNOSPOOF, "dhcp-nospoof" }
  95 };
  96 #define LPTYPES (sizeof (link_protect_types) / sizeof (link_protect_t))
  97 
  98 dladm_status_t
  99 dladm_open(dladm_handle_t *handle)
 100 {
 101         int dld_fd;
 102 
 103         if (handle == NULL)
 104                 return (DLADM_STATUS_BADARG);
 105 
 106         if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
 107                 return (dladm_errno2status(errno));
 108 
 109         /*
 110          * Don't open DLMGMT_DOOR now.  dlmgmtd(1M) is not able to
 111          * open the door when the dladm handle is opened because the
 112          * door hasn't been created yet at that time.  Thus, we must
 113          * open it on-demand in dladm_door_fd().  Move the open()
 114          * to dladm_door_fd() for all cases.
 115          */
 116 
 117         if ((*handle = malloc(sizeof (struct dladm_handle))) == NULL) {
 118                 (void) close(dld_fd);
 119                 return (DLADM_STATUS_NOMEM);
 120         }
 121 
 122         (*handle)->dld_fd = dld_fd;
 123         (*handle)->door_fd = -1;
 124 
 125         return (DLADM_STATUS_OK);
 126 }
 127 
 128 void
 129 dladm_close(dladm_handle_t handle)
 130 {
 131         if (handle != NULL) {
 132                 (void) close(handle->dld_fd);
 133                 if (handle->door_fd != -1)
 134                         (void) close(handle->door_fd);
 135                 free(handle);
 136         }
 137 }
 138 
 139 int
 140 dladm_dld_fd(dladm_handle_t handle)
 141 {
 142         return (handle->dld_fd);
 143 }
 144 
 145 /*
 146  * If DLMGMT_DOOR hasn't been opened in the handle yet, open it.
 147  */
 148 dladm_status_t
 149 dladm_door_fd(dladm_handle_t handle, int *door_fd)
 150 {
 151         int fd;
 152 
 153         if (handle->door_fd == -1) {
 154                 if ((fd = open(DLMGMT_DOOR, O_RDONLY)) < 0)
 155                         return (dladm_errno2status(errno));
 156                 handle->door_fd = fd;
 157         }
 158         *door_fd = handle->door_fd;
 159 
 160         return (DLADM_STATUS_OK);
 161 }
 162 
 163 const char *
 164 dladm_status2str(dladm_status_t status, char *buf)
 165 {
 166         const char      *s;
 167 
 168         switch (status) {
 169         case DLADM_STATUS_OK:
 170                 s = "ok";
 171                 break;
 172         case DLADM_STATUS_BADARG:
 173                 s = "invalid argument";
 174                 break;
 175         case DLADM_STATUS_FAILED:
 176                 s = "operation failed";
 177                 break;
 178         case DLADM_STATUS_TOOSMALL:
 179                 s = "buffer size too small";
 180                 break;
 181         case DLADM_STATUS_NOTSUP:
 182                 s = "operation not supported";
 183                 break;
 184         case DLADM_STATUS_NOTFOUND:
 185                 s = "object not found";
 186                 break;
 187         case DLADM_STATUS_BADVAL:
 188                 s = "invalid value";
 189                 break;
 190         case DLADM_STATUS_NOMEM:
 191                 s = "insufficient memory";
 192                 break;
 193         case DLADM_STATUS_EXIST:
 194                 s = "object already exists";
 195                 break;
 196         case DLADM_STATUS_LINKINVAL:
 197                 s = "invalid link";
 198                 break;
 199         case DLADM_STATUS_PROPRDONLY:
 200                 s = "read-only property";
 201                 break;
 202         case DLADM_STATUS_BADVALCNT:
 203                 s = "invalid number of values";
 204                 break;
 205         case DLADM_STATUS_DBNOTFOUND:
 206                 s = "database not found";
 207                 break;
 208         case DLADM_STATUS_DENIED:
 209                 s = "permission denied";
 210                 break;
 211         case DLADM_STATUS_IOERR:
 212                 s = "I/O error";
 213                 break;
 214         case DLADM_STATUS_TEMPONLY:
 215                 s = "change cannot be persistent";
 216                 break;
 217         case DLADM_STATUS_TIMEDOUT:
 218                 s = "operation timed out";
 219                 break;
 220         case DLADM_STATUS_ISCONN:
 221                 s = "already connected";
 222                 break;
 223         case DLADM_STATUS_NOTCONN:
 224                 s = "not connected";
 225                 break;
 226         case DLADM_STATUS_REPOSITORYINVAL:
 227                 s = "invalid configuration repository";
 228                 break;
 229         case DLADM_STATUS_MACADDRINVAL:
 230                 s = "invalid MAC address";
 231                 break;
 232         case DLADM_STATUS_KEYINVAL:
 233                 s = "invalid key";
 234                 break;
 235         case DLADM_STATUS_INVALIDMACADDRLEN:
 236                 s = "invalid MAC address length";
 237                 break;
 238         case DLADM_STATUS_INVALIDMACADDRTYPE:
 239                 s = "invalid MAC address type";
 240                 break;
 241         case DLADM_STATUS_LINKBUSY:
 242                 s = "link busy";
 243                 break;
 244         case DLADM_STATUS_VIDINVAL:
 245                 s = "invalid VLAN identifier";
 246                 break;
 247         case DLADM_STATUS_TRYAGAIN:
 248                 s = "try again later";
 249                 break;
 250         case DLADM_STATUS_NONOTIF:
 251                 s = "link notification is not supported";
 252                 break;
 253         case DLADM_STATUS_BADTIMEVAL:
 254                 s = "invalid time range";
 255                 break;
 256         case DLADM_STATUS_INVALIDMACADDR:
 257                 s = "invalid MAC address value";
 258                 break;
 259         case DLADM_STATUS_INVALIDMACADDRNIC:
 260                 s = "MAC address reserved for use by underlying data-link";
 261                 break;
 262         case DLADM_STATUS_INVALIDMACADDRINUSE:
 263                 s = "MAC address is already in use";
 264                 break;
 265         case DLADM_STATUS_MACFACTORYSLOTINVALID:
 266                 s = "invalid factory MAC address slot";
 267                 break;
 268         case DLADM_STATUS_MACFACTORYSLOTUSED:
 269                 s = "factory MAC address slot already used";
 270                 break;
 271         case DLADM_STATUS_MACFACTORYSLOTALLUSED:
 272                 s = "all factory MAC address slots are in use";
 273                 break;
 274         case DLADM_STATUS_MACFACTORYNOTSUP:
 275                 s = "factory MAC address slots not supported";
 276                 break;
 277         case DLADM_STATUS_INVALIDMACPREFIX:
 278                 s = "Invalid MAC address prefix value";
 279                 break;
 280         case DLADM_STATUS_INVALIDMACPREFIXLEN:
 281                 s = "Invalid MAC address prefix length";
 282                 break;
 283         case DLADM_STATUS_BADCPUID:
 284                 s = "non-existent processor ID";
 285                 break;
 286         case DLADM_STATUS_CPUERR:
 287                 s = "could not determine processor status";
 288                 break;
 289         case DLADM_STATUS_CPUNOTONLINE:
 290                 s = "processor not online";
 291                 break;
 292         case DLADM_STATUS_TOOMANYELEMENTS:
 293                 s = "too many elements specified";
 294                 break;
 295         case DLADM_STATUS_BADRANGE:
 296                 s = "invalid range";
 297                 break;
 298         case DLADM_STATUS_DB_NOTFOUND:
 299                 s = "database not found";
 300                 break;
 301         case DLADM_STATUS_DB_PARSE_ERR:
 302                 s = "database parse error";
 303                 break;
 304         case DLADM_STATUS_PROP_PARSE_ERR:
 305                 s = "property parse error";
 306                 break;
 307         case DLADM_STATUS_ATTR_PARSE_ERR:
 308                 s = "attribute parse error";
 309                 break;
 310         case DLADM_STATUS_FLOW_DB_ERR:
 311                 s = "flow database error";
 312                 break;
 313         case DLADM_STATUS_FLOW_DB_OPEN_ERR:
 314                 s = "flow database open error";
 315                 break;
 316         case DLADM_STATUS_FLOW_DB_PARSE_ERR:
 317                 s = "flow database parse error";
 318                 break;
 319         case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
 320                 s = "flow property database parse error";
 321                 break;
 322         case DLADM_STATUS_FLOW_ADD_ERR:
 323                 s = "flow add error";
 324                 break;
 325         case DLADM_STATUS_FLOW_WALK_ERR:
 326                 s = "flow walk error";
 327                 break;
 328         case DLADM_STATUS_FLOW_IDENTICAL:
 329                 s = "a flow with identical attributes exists";
 330                 break;
 331         case DLADM_STATUS_FLOW_INCOMPATIBLE:
 332                 s = "flow(s) with incompatible attributes exists";
 333                 break;
 334         case DLADM_STATUS_FLOW_EXISTS:
 335                 s = "link still has flows";
 336                 break;
 337         case DLADM_STATUS_PERSIST_FLOW_EXISTS:
 338                 s = "persistent flow with the same name exists";
 339                 break;
 340         case DLADM_STATUS_INVALID_IP:
 341                 s = "invalid IP address";
 342                 break;
 343         case DLADM_STATUS_INVALID_PREFIXLEN:
 344                 s = "invalid IP prefix length";
 345                 break;
 346         case DLADM_STATUS_INVALID_PROTOCOL:
 347                 s = "invalid IP protocol";
 348                 break;
 349         case DLADM_STATUS_INVALID_PORT:
 350                 s = "invalid port number";
 351                 break;
 352         case DLADM_STATUS_INVALID_DSF:
 353                 s = "invalid dsfield";
 354                 break;
 355         case DLADM_STATUS_INVALID_DSFMASK:
 356                 s = "invalid dsfield mask";
 357                 break;
 358         case DLADM_STATUS_INVALID_MACMARGIN:
 359                 s = "MTU check failed, use lower MTU or -f option";
 360                 break;
 361         case DLADM_STATUS_BADPROP:
 362                 s = "invalid property";
 363                 break;
 364         case DLADM_STATUS_MINMAXBW:
 365                 s = "minimum value for maxbw is 1200K";
 366                 break;
 367         case DLADM_STATUS_NO_HWRINGS:
 368                 s = "request hw rings failed";
 369                 break;
 370         case DLADM_STATUS_PERMONLY:
 371                 s = "change must be persistent";
 372                 break;
 373         case DLADM_STATUS_OPTMISSING:
 374                 s = "optional software not installed";
 375                 break;
 376         case DLADM_STATUS_IPTUNTYPE:
 377                 s = "invalid IP tunnel type";
 378                 break;
 379         case DLADM_STATUS_IPTUNTYPEREQD:
 380                 s = "IP tunnel type required";
 381                 break;
 382         case DLADM_STATUS_BADIPTUNLADDR:
 383                 s = "invalid local IP tunnel address";
 384                 break;
 385         case DLADM_STATUS_BADIPTUNRADDR:
 386                 s = "invalid remote IP tunnel address";
 387                 break;
 388         case DLADM_STATUS_ADDRINUSE:
 389                 s = "address already in use";
 390                 break;
 391         case DLADM_STATUS_POOLCPU:
 392                 s = "pool and cpus property are mutually exclusive";
 393                 break;
 394         case DLADM_STATUS_INVALID_PORT_INSTANCE:
 395                 s = "invalid IB phys link";
 396                 break;
 397         case DLADM_STATUS_PORT_IS_DOWN:
 398                 s = "port is down";
 399                 break;
 400         case DLADM_STATUS_PARTITION_EXISTS:
 401                 s = "partition already exists";
 402                 break;
 403         case DLADM_STATUS_PKEY_NOT_PRESENT:
 404                 s = "PKEY is not present on the port";
 405                 break;
 406         case DLADM_STATUS_INVALID_PKEY:
 407                 s = "invalid PKEY";
 408                 break;
 409         case DLADM_STATUS_NO_IB_HW_RESOURCE:
 410                 s = "IB internal resource not available";
 411                 break;
 412         case DLADM_STATUS_INVALID_PKEY_TBL_SIZE:
 413                 s = "invalid PKEY table size";
 414                 break;
 415         case DLADM_STATUS_PORT_NOPROTO:
 416                 s = "local or remote port requires transport";
 417                 break;
 418         case DLADM_STATUS_INVALID_MTU:
 419                 s = "MTU check failed, MTU outside of device's supported range";
 420                 break;
 421         default:
 422                 s = "<unknown error>";
 423                 break;
 424         }
 425         (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
 426         return (buf);
 427 }
 428 
 429 /*
 430  * Convert a unix errno to a dladm_status_t.
 431  * We only convert errnos that are likely to be encountered. All others
 432  * are mapped to DLADM_STATUS_FAILED.
 433  */
 434 dladm_status_t
 435 dladm_errno2status(int err)
 436 {
 437         switch (err) {
 438         case 0:
 439                 return (DLADM_STATUS_OK);
 440         case EINVAL:
 441                 return (DLADM_STATUS_BADARG);
 442         case EEXIST:
 443                 return (DLADM_STATUS_EXIST);
 444         case ENOENT:
 445                 return (DLADM_STATUS_NOTFOUND);
 446         case ENOSPC:
 447                 return (DLADM_STATUS_TOOSMALL);
 448         case ENOMEM:
 449                 return (DLADM_STATUS_NOMEM);
 450         case ENOTSUP:
 451                 return (DLADM_STATUS_NOTSUP);
 452         case ENETDOWN:
 453                 return (DLADM_STATUS_NONOTIF);
 454         case EACCES:
 455         case EPERM:
 456                 return (DLADM_STATUS_DENIED);
 457         case EIO:
 458                 return (DLADM_STATUS_IOERR);
 459         case EBUSY:
 460                 return (DLADM_STATUS_LINKBUSY);
 461         case EAGAIN:
 462                 return (DLADM_STATUS_TRYAGAIN);
 463         case ENOTEMPTY:
 464                 return (DLADM_STATUS_FLOW_EXISTS);
 465         case EOPNOTSUPP:
 466                 return (DLADM_STATUS_FLOW_INCOMPATIBLE);
 467         case EALREADY:
 468                 return (DLADM_STATUS_FLOW_IDENTICAL);
 469         case EADDRINUSE:
 470                 return (DLADM_STATUS_ADDRINUSE);
 471         default:
 472                 return (DLADM_STATUS_FAILED);
 473         }
 474 }
 475 
 476 boolean_t
 477 dladm_str2interval(char *oarg, uint32_t *interval)
 478 {
 479         int             val;
 480         char            *endp = NULL;
 481 
 482         errno = 0;
 483         val = strtol(oarg, &endp, 10);
 484         if (errno != 0 || val <= 0 || *endp != '\0')
 485                 return (B_FALSE);
 486 
 487         *interval = val;
 488 
 489         return (B_TRUE);
 490 }
 491 
 492 dladm_status_t
 493 dladm_str2bw(char *oarg, uint64_t *bw)
 494 {
 495         char            *endp = NULL;
 496         int64_t         n;
 497         int             mult = 1;
 498 
 499         n = strtoull(oarg, &endp, 10);
 500 
 501         if ((errno != 0) || (strlen(endp) > 1))
 502                 return (DLADM_STATUS_BADARG);
 503 
 504         if (n < 0)
 505                 return (DLADM_STATUS_BADVAL);
 506 
 507         switch (*endp) {
 508         case 'k':
 509         case 'K':
 510                 mult = 1000;
 511                 break;
 512         case 'm':
 513         case 'M':
 514         case '\0':
 515                 mult = 1000000;
 516                 break;
 517         case 'g':
 518         case 'G':
 519                 mult = 1000000000;
 520                 break;
 521         case '%':
 522                 /*
 523                  * percentages not supported for now,
 524                  * see RFE 6540675
 525                  */
 526                 return (DLADM_STATUS_NOTSUP);
 527         default:
 528                 return (DLADM_STATUS_BADVAL);
 529         }
 530 
 531         *bw = n * mult;
 532 
 533         /* check for overflow */
 534         if (*bw / mult != n)
 535                 return (DLADM_STATUS_BADARG);
 536 
 537         return (DLADM_STATUS_OK);
 538 }
 539 
 540 /*
 541  * Convert bandwidth in bps to a string in Mbps.  For values greater
 542  * than 1Mbps or 1000000, print a whole Mbps value.  For values that
 543  * have fractional Mbps in whole Kbps, print the bandwidth in a manner
 544  * similar to a floating point format.
 545  *
 546  *        bps       string
 547  *          0            0
 548  *        100            0
 549  *       2000        0.002
 550  *     431000        0.431
 551  *    1000000            1
 552  *    1030000        1.030
 553  *  100000000          100
 554  */
 555 const char *
 556 dladm_bw2str(int64_t bw, char *buf)
 557 {
 558         int kbps, mbps;
 559 
 560         kbps = (bw%1000000)/1000;
 561         mbps = bw/1000000;
 562         if (kbps != 0) {
 563                 if (mbps == 0)
 564                         (void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
 565                 else
 566                         (void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
 567                             kbps);
 568         } else {
 569                 (void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
 570         }
 571 
 572         return (buf);
 573 }
 574 
 575 #define LOCK_DB_PERMS   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
 576 
 577 static int
 578 i_dladm_lock_db(const char *lock_file, short type)
 579 {
 580         int     lock_fd;
 581         struct  flock lock;
 582 
 583         if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
 584             LOCK_DB_PERMS)) < 0)
 585                 return (-1);
 586 
 587         lock.l_type = type;
 588         lock.l_whence = SEEK_SET;
 589         lock.l_start = 0;
 590         lock.l_len = 0;
 591 
 592         if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
 593                 int err = errno;
 594 
 595                 (void) close(lock_fd);
 596                 (void) unlink(lock_file);
 597                 errno = err;
 598                 return (-1);
 599         }
 600         return (lock_fd);
 601 }
 602 
 603 static void
 604 i_dladm_unlock_db(const char *lock_file, int fd)
 605 {
 606         struct flock lock;
 607 
 608         if (fd < 0)
 609                 return;
 610 
 611         lock.l_type = F_UNLCK;
 612         lock.l_whence = SEEK_SET;
 613         lock.l_start = 0;
 614         lock.l_len = 0;
 615 
 616         (void) fcntl(fd, F_SETLKW, &lock);
 617         (void) close(fd);
 618         (void) unlink(lock_file);
 619 }
 620 
 621 /*
 622  * Given a link class, returns its class string.
 623  */
 624 const char *
 625 dladm_class2str(datalink_class_t class, char *buf)
 626 {
 627         const char *s;
 628 
 629         switch (class) {
 630         case DATALINK_CLASS_PHYS:
 631                 s = "phys";
 632                 break;
 633         case DATALINK_CLASS_VLAN:
 634                 s = "vlan";
 635                 break;
 636         case DATALINK_CLASS_AGGR:
 637                 s = "aggr";
 638                 break;
 639         case DATALINK_CLASS_VNIC:
 640                 s = "vnic";
 641                 break;
 642         case DATALINK_CLASS_ETHERSTUB:
 643                 s = "etherstub";
 644                 break;
 645         case DATALINK_CLASS_IPTUN:
 646                 s = "iptun";
 647                 break;
 648         case DATALINK_CLASS_SIMNET:
 649                 s = "simnet";
 650                 break;
 651         case DATALINK_CLASS_BRIDGE:
 652                 s = "bridge";
 653                 break;
 654         case DATALINK_CLASS_PART:
 655                 s = "part";
 656                 break;
 657         default:
 658                 s = "unknown";
 659                 break;
 660         }
 661 
 662         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 663         return (buf);
 664 }
 665 
 666 /*
 667  * Given a physical link media type, returns its media type string.
 668  */
 669 const char *
 670 dladm_media2str(uint32_t media, char *buf)
 671 {
 672         const char *s = "--";
 673         media_type_t *mt;
 674         int idx;
 675 
 676         for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
 677                 mt = media_type_table + idx;
 678                 if (mt->media_type == media) {
 679                         s = mt->media_type_str;
 680                         break;
 681                 }
 682         }
 683 
 684         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 685         return (buf);
 686 }
 687 
 688 /*
 689  * Given a physical link media type string, returns its media type constant.
 690  */
 691 uint32_t
 692 dladm_str2media(const char *buf)
 693 {
 694         media_type_t *mt;
 695         int idx;
 696 
 697         for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
 698                 mt = media_type_table + idx;
 699                 if (strcasecmp(buf, mt->media_type_str) == 0)
 700                         return (mt->media_type);
 701         }
 702 
 703         return (DL_OTHER);
 704 }
 705 
 706 dladm_status_t
 707 i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
 708     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
 709     void *arg, boolean_t writeop)
 710 {
 711         dladm_status_t  status = DLADM_STATUS_OK;
 712         FILE            *fp, *nfp = NULL;
 713         char            lock[MAXPATHLEN];
 714         char            file[MAXPATHLEN];
 715         char            newfile[MAXPATHLEN];
 716         char            *db_basename;
 717         int             nfd, lock_fd;
 718 
 719         /*
 720          * If we are called from a boot script such as net-physical,
 721          * it's quite likely that the root fs is still not writable.
 722          * For this case, it's ok for the lock creation to fail since
 723          * no one else could be accessing our configuration file.
 724          */
 725         db_basename = strrchr(db_file, '/');
 726         if (db_basename == NULL || db_basename[1] == '\0')
 727                 return (dladm_errno2status(EINVAL));
 728         db_basename++;
 729         (void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
 730         if ((lock_fd = i_dladm_lock_db
 731             (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
 732                 return (dladm_errno2status(errno));
 733 
 734         (void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
 735         if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
 736                 int     err = errno;
 737 
 738                 i_dladm_unlock_db(lock, lock_fd);
 739                 if (err == ENOENT)
 740                         return (DLADM_STATUS_DBNOTFOUND);
 741 
 742                 return (dladm_errno2status(err));
 743         }
 744 
 745         if (writeop) {
 746                 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
 747                     dladm_rootdir, db_file);
 748                 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
 749                     db_perms)) < 0) {
 750                         (void) fclose(fp);
 751                         i_dladm_unlock_db(lock, lock_fd);
 752                         return (dladm_errno2status(errno));
 753                 }
 754 
 755                 if ((nfp = fdopen(nfd, "w")) == NULL) {
 756                         (void) close(nfd);
 757                         (void) fclose(fp);
 758                         (void) unlink(newfile);
 759                         i_dladm_unlock_db(lock, lock_fd);
 760                         return (dladm_errno2status(errno));
 761                 }
 762         }
 763         status = (*process_db)(handle, arg, fp, nfp);
 764         if (!writeop || status != DLADM_STATUS_OK)
 765                 goto done;
 766 
 767         /* Set permissions on file to db_perms */
 768         if (fchmod(nfd, db_perms) < 0) {
 769                 status = dladm_errno2status(errno);
 770                 goto done;
 771         }
 772 
 773         /*
 774          * Configuration files need to be owned by the 'dladm' user and
 775          * 'netadm' group.
 776          */
 777         if (fchown(nfd, UID_DLADM, GID_NETADM) < 0) {
 778                 status = dladm_errno2status(errno);
 779                 goto done;
 780         }
 781 
 782         if (fflush(nfp) == EOF) {
 783                 status = dladm_errno2status(errno);
 784                 goto done;
 785         }
 786         (void) fclose(fp);
 787         (void) fclose(nfp);
 788 
 789         if (rename(newfile, file) < 0) {
 790                 (void) unlink(newfile);
 791                 i_dladm_unlock_db(lock, lock_fd);
 792                 return (dladm_errno2status(errno));
 793         }
 794 
 795         i_dladm_unlock_db(lock, lock_fd);
 796         return (DLADM_STATUS_OK);
 797 
 798 done:
 799         if (nfp != NULL) {
 800                 (void) fclose(nfp);
 801                 if (status != DLADM_STATUS_OK)
 802                         (void) unlink(newfile);
 803         }
 804         (void) fclose(fp);
 805         i_dladm_unlock_db(lock, lock_fd);
 806         return (status);
 807 }
 808 
 809 dladm_status_t
 810 dladm_set_rootdir(const char *rootdir)
 811 {
 812         DIR     *dp;
 813 
 814         if (rootdir == NULL || *rootdir != '/' ||
 815             (dp = opendir(rootdir)) == NULL)
 816                 return (DLADM_STATUS_BADARG);
 817 
 818         (void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
 819         (void) closedir(dp);
 820         return (DLADM_STATUS_OK);
 821 }
 822 
 823 boolean_t
 824 dladm_valid_linkname(const char *link)
 825 {
 826         size_t          len = strlen(link);
 827         const char      *cp;
 828         int             nd = 0;
 829 
 830         if (len >= MAXLINKNAMELEN)
 831                 return (B_FALSE);
 832 
 833         /* Link name cannot start with a digit */
 834         if (isdigit(link[0]))
 835                 return (B_FALSE);
 836         /* Link name must end with a number without leading zeroes */
 837         cp = link + len - 1;
 838         while (isdigit(*cp)) {
 839                 cp--;
 840                 nd++;
 841         }
 842         if (nd == 0 || (nd > 1 && *(cp + 1) == '0'))
 843                 return (B_FALSE);
 844 
 845         /*
 846          * The legal characters in a link name are:
 847          * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
 848          */
 849         for (cp = link; *cp != '\0'; cp++) {
 850                 if ((isalnum(*cp) == 0) && (*cp != '_') && (*cp != '.'))
 851                         return (B_FALSE);
 852         }
 853 
 854         return (B_TRUE);
 855 }
 856 
 857 /*
 858  * Convert priority string to a value.
 859  */
 860 dladm_status_t
 861 dladm_str2pri(char *token, mac_priority_level_t *pri)
 862 {
 863         if (strlen(token) == strlen("low") &&
 864             strncasecmp(token, "low", strlen("low")) == 0) {
 865                 *pri = MPL_LOW;
 866         } else if (strlen(token) == strlen("medium") &&
 867             strncasecmp(token, "medium", strlen("medium")) == 0) {
 868                 *pri = MPL_MEDIUM;
 869         } else if (strlen(token) == strlen("high") &&
 870             strncasecmp(token, "high", strlen("high")) == 0) {
 871                 *pri = MPL_HIGH;
 872         } else {
 873                 return (DLADM_STATUS_BADVAL);
 874         }
 875         return (DLADM_STATUS_OK);
 876 }
 877 
 878 /*
 879  * Convert priority value to a string.
 880  */
 881 const char *
 882 dladm_pri2str(mac_priority_level_t pri, char *buf)
 883 {
 884         const char      *s;
 885 
 886         switch (pri) {
 887         case MPL_LOW:
 888                 s = "low";
 889                 break;
 890         case MPL_MEDIUM:
 891                 s = "medium";
 892                 break;
 893         case MPL_HIGH:
 894                 s = "high";
 895                 break;
 896         default:
 897                 s = "--";
 898                 break;
 899         }
 900         (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
 901         return (buf);
 902 }
 903 
 904 /*
 905  * Convert protect string to a value.
 906  */
 907 dladm_status_t
 908 dladm_str2protect(char *token, uint32_t *ptype)
 909 {
 910         link_protect_t  *lp;
 911         int             i;
 912 
 913         for (i = 0; i < LPTYPES; i++) {
 914                 lp = &link_protect_types[i];
 915                 if (strcmp(token, lp->lp_name) == 0) {
 916                         *ptype = lp->lp_type;
 917                         return (DLADM_STATUS_OK);
 918                 }
 919         }
 920         return (DLADM_STATUS_BADVAL);
 921 }
 922 
 923 /*
 924  * Convert protect value to a string.
 925  */
 926 const char *
 927 dladm_protect2str(uint32_t ptype, char *buf)
 928 {
 929         const char      *s = "--";
 930         link_protect_t  *lp;
 931         int             i;
 932 
 933         for (i = 0; i < LPTYPES; i++) {
 934                 lp = &link_protect_types[i];
 935                 if (lp->lp_type == ptype) {
 936                         s = lp->lp_name;
 937                         break;
 938                 }
 939         }
 940         (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
 941         return (buf);
 942 }
 943 
 944 /*
 945  * Convert an IPv4 address to/from a string.
 946  */
 947 const char *
 948 dladm_ipv4addr2str(void *addr, char *buf)
 949 {
 950         if (inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN) == NULL)
 951                 buf[0] = '\0';
 952 
 953         return (buf);
 954 }
 955 
 956 dladm_status_t
 957 dladm_str2ipv4addr(char *token, void *addr)
 958 {
 959         return (inet_pton(AF_INET, token, addr) == 1 ?
 960             DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
 961 }
 962 
 963 const char *
 964 dladm_ipv6addr2str(void *addr, char *buf)
 965 {
 966         if (inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN) == NULL)
 967                 buf[0] = '\0';
 968 
 969         return (buf);
 970 }
 971 
 972 dladm_status_t
 973 dladm_str2ipv6addr(char *token, void *addr)
 974 {
 975         return (inet_pton(AF_INET6, token, addr) == 1 ?
 976             DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
 977 }
 978 
 979 /*
 980  * Find the set bits in a mask.
 981  * This is used for expanding a bitmask into individual sub-masks
 982  * which can be used for further processing.
 983  */
 984 void
 985 dladm_find_setbits32(uint32_t mask, uint32_t *list, uint32_t *cnt)
 986 {
 987         int     i, c = 0;
 988 
 989         for (i = 0; i < 32; i++) {
 990                 if (((1 << i) & mask) != 0)
 991                         list[c++] = 1 << i;
 992         }
 993         *cnt = c;
 994 }
 995 
 996 void
 997 dladm_free_args(dladm_arg_list_t *list)
 998 {
 999         if (list != NULL) {
1000                 free(list->al_buf);
1001                 free(list);
1002         }
1003 }
1004 
1005 dladm_status_t
1006 dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
1007 {
1008         dladm_arg_list_t        *list;
1009         dladm_arg_info_t        *aip;
1010         char                    *buf, *curr;
1011         int                     len, i;
1012 
1013         if (str == NULL)
1014                 return (DLADM_STATUS_BADVAL);
1015 
1016         if (str[0] == '\0')
1017                 return (DLADM_STATUS_OK);
1018 
1019         list = malloc(sizeof (dladm_arg_list_t));
1020         if (list == NULL)
1021                 return (dladm_errno2status(errno));
1022 
1023         list->al_count = 0;
1024         list->al_buf = buf = strdup(str);
1025         if (buf == NULL)
1026                 return (dladm_errno2status(errno));
1027 
1028         curr = buf;
1029         len = strlen(buf);
1030         aip = NULL;
1031         for (i = 0; i < len; i++) {
1032                 char            c = buf[i];
1033                 boolean_t       match = (c == '=' || c == ',');
1034 
1035                 if (!match && i != len - 1)
1036                         continue;
1037 
1038                 if (match) {
1039                         buf[i] = '\0';
1040                         if (*curr == '\0')
1041                                 goto fail;
1042                 }
1043 
1044                 if (aip != NULL && c != '=') {
1045                         if (aip->ai_count > DLADM_MAX_ARG_VALS)
1046                                 goto fail;
1047 
1048                         if (novalues)
1049                                 goto fail;
1050 
1051                         aip->ai_val[aip->ai_count] = curr;
1052                         aip->ai_count++;
1053                 } else {
1054                         if (list->al_count > DLADM_MAX_ARG_VALS)
1055                                 goto fail;
1056 
1057                         aip = &list->al_info[list->al_count];
1058                         aip->ai_name = curr;
1059                         aip->ai_count = 0;
1060                         list->al_count++;
1061                         if (c == ',')
1062                                 aip = NULL;
1063                 }
1064                 curr = buf + i + 1;
1065         }
1066 
1067         *listp = list;
1068         return (DLADM_STATUS_OK);
1069 
1070 fail:
1071         dladm_free_args(list);
1072         return (DLADM_STATUS_FAILED);
1073 }
1074 
1075 /*
1076  * mac_propval_range_t functions.  Currently implemented for only
1077  * ranges of uint32_t elements, but can be expanded as required.
1078  */
1079 /*
1080  * Convert an array of strings (which can be ranges or individual
1081  * elements) into a single mac_propval_range_t structure which
1082  * is allocated here but should be freed by the caller.
1083  */
1084 dladm_status_t
1085 dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type,
1086     mac_propval_range_t **range)
1087 {
1088         int                     i;
1089         char                    *endp;
1090         mac_propval_range_t     *rangep;
1091         dladm_status_t          status = DLADM_STATUS_OK;
1092 
1093         switch (type) {
1094         case MAC_PROPVAL_UINT32: {
1095                 mac_propval_uint32_range_t      *ur;
1096 
1097                 /* Allocate range structure */
1098                 rangep = malloc(sizeof (mac_propval_range_t) +
1099                     (val_cnt-1)*(sizeof (mac_propval_uint32_range_t)));
1100                 if (rangep == NULL)
1101                         return (DLADM_STATUS_NOMEM);
1102 
1103                 rangep->mpr_count = 0;
1104                 ur = &rangep->mpr_range_uint32[0];
1105                 for (i = 0; i < val_cnt; i++, ur++) {
1106                         errno = 0;
1107                         if (strchr(prop_val[i], '-') == NULL) {
1108                                 /* single element */
1109                                 ur->mpur_min = ur->mpur_max =
1110                                     strtol(prop_val[i], &endp, 10);
1111                                 if ((endp != NULL) && (*endp != '\0')) {
1112                                         return (DLADM_STATUS_BADRANGE);
1113                                 }
1114                         } else {
1115                                 /* range of elements */
1116                                 ur->mpur_min = strtol(prop_val[i], &endp, 10);
1117                                 if (*endp++ != '-')
1118                                         return (DLADM_STATUS_BADRANGE);
1119                                 ur->mpur_max = strtol(endp, &endp, 10);
1120                                 if (endp != NULL && *endp != '\0' ||
1121                                     ur->mpur_max < ur->mpur_min)
1122                                         return (DLADM_STATUS_BADRANGE);
1123                         }
1124                         rangep->mpr_count++;
1125                 }
1126                 break;
1127         }
1128         default:
1129                 return (DLADM_STATUS_BADVAL);
1130         }
1131 
1132         rangep->mpr_type = type;
1133         *range = rangep;
1134 
1135         return (status);
1136 }
1137 
1138 /*
1139  * Convert a mac_propval_range_t structure into an array of elements.
1140  */
1141 dladm_status_t
1142 dladm_range2list(mac_propval_range_t *rangep, void *elem, uint_t *nelem)
1143 {
1144         int             i, j, k;
1145         dladm_status_t  status = DLADM_STATUS_OK;
1146 
1147         switch (rangep->mpr_type) {
1148         case MAC_PROPVAL_UINT32: {
1149                 mac_propval_uint32_range_t      *ur;
1150                 uint32_t                        *elem32 = elem;
1151 
1152                 k = 0;
1153                 ur = &rangep->mpr_range_uint32[0];
1154                 for (i = 0; i < rangep->mpr_count; i++, ur++) {
1155                         for (j = 0; j <= ur->mpur_max - ur->mpur_min; j++) {
1156                                 elem32[k++] = ur->mpur_min + j;
1157                                 if (k > *nelem) {
1158                                         status = DLADM_STATUS_TOOMANYELEMENTS;
1159                                         break;
1160                                 }
1161                         }
1162                 }
1163                 *nelem = k;
1164                 break;
1165         }
1166         default:
1167                 status = DLADM_STATUS_BADVAL;
1168                 break;
1169         }
1170         return (status);
1171 }
1172 
1173 /*
1174  * Convert a mac_propval_range_t structure into an array of strings
1175  * of single elements or ranges.
1176  */
1177 int
1178 dladm_range2strs(mac_propval_range_t *rangep, char **prop_val)
1179 {
1180         int     i;
1181 
1182         switch (rangep->mpr_type) {
1183         case MAC_PROPVAL_UINT32: {
1184                 mac_propval_uint32_range_t      *ur;
1185 
1186                 /* Write ranges and individual elements */
1187                 ur = &rangep->mpr_range_uint32[0];
1188                 for (i = 0; i < rangep->mpr_count; i++, ur++) {
1189                         if (ur->mpur_min == ur->mpur_max) {
1190                                 /* single element */
1191                                 (void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1192                                     "%u", ur->mpur_min);
1193                         } else {
1194                                 /* range of elements */
1195                                 (void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1196                                     "%u-%u", ur->mpur_min, ur->mpur_max);
1197                         }
1198                 }
1199                 return (0);
1200         }
1201         default:
1202                 break;
1203         }
1204         return (EINVAL);
1205 }
1206 
1207 static int
1208 uint32cmp(const void *a, const void *b)
1209 {
1210         return (*(uint32_t *)a - *(uint32_t *)b);
1211 }
1212 
1213 /*
1214  * Sort and convert an array of elements into a single
1215  * mac_propval_range_t structure which is allocated here but
1216  * should be freed by the caller.
1217  */
1218 dladm_status_t
1219 dladm_list2range(void *elem, uint_t nelem, mac_propval_type_t type,
1220     mac_propval_range_t **range)
1221 {
1222         int                     i;
1223         uint_t                  nr = 0;
1224         mac_propval_range_t     *rangep;
1225         dladm_status_t          status = DLADM_STATUS_OK;
1226 
1227         switch (type) {
1228         case MAC_PROPVAL_UINT32: {
1229                 mac_propval_uint32_range_t      *ur;
1230                 uint32_t                        *elem32 = elem;
1231                 uint32_t                        *sort32;
1232 
1233                 /* Allocate range structure */
1234                 rangep = malloc(sizeof (mac_propval_range_t) +
1235                     (nelem-1)*(sizeof (mac_propval_uint32_range_t)));
1236                 if (rangep == NULL)
1237                         return (DLADM_STATUS_NOMEM);
1238 
1239                 /* Allocate array for sorting */
1240                 sort32 = malloc(nelem * sizeof (uint32_t));
1241                 if (sort32 == NULL) {
1242                         free(rangep);
1243                         return (DLADM_STATUS_NOMEM);
1244                 }
1245 
1246                 /* Copy and sort list */
1247                 for (i = 0; i < nelem; i++)
1248                         sort32[i] =  elem32[i];
1249                 if (nelem > 1)
1250                         qsort(sort32, nelem, sizeof (uint32_t), uint32cmp);
1251 
1252                 /* Convert list to ranges */
1253                 ur = &rangep->mpr_range_uint32[0];
1254                 ur->mpur_min = ur->mpur_max = sort32[0];
1255                 for (i = 1; i < nelem; i++) {
1256                         if (sort32[i]-sort32[i-1] == 1) {
1257                                 /* part of current range */
1258                                 ur->mpur_max = sort32[i];
1259                         } else {
1260                                 /* start a new range */
1261                                 nr++; ur++;
1262                                 ur->mpur_min = ur->mpur_max = sort32[i];
1263                         }
1264                 }
1265                 free(sort32);
1266                 break;
1267         }
1268         default:
1269                 return (DLADM_STATUS_BADRANGE);
1270         }
1271 
1272         rangep->mpr_type = type;
1273         rangep->mpr_count = nr + 1;
1274         *range = rangep;
1275 
1276         return (status);
1277 }