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