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         errno = 0;
 500         n = strtoull(oarg, &endp, 10);
 501 
 502         if ((errno != 0) || (strlen(endp) > 1))
 503                 return (DLADM_STATUS_BADARG);
 504 
 505         if (n < 0)
 506                 return (DLADM_STATUS_BADVAL);
 507 
 508         switch (*endp) {
 509         case 'k':
 510         case 'K':
 511                 mult = 1000;
 512                 break;
 513         case 'm':
 514         case 'M':
 515         case '\0':
 516                 mult = 1000000;
 517                 break;
 518         case 'g':
 519         case 'G':
 520                 mult = 1000000000;
 521                 break;
 522         case '%':
 523                 /*
 524                  * percentages not supported for now,
 525                  * see RFE 6540675
 526                  */
 527                 return (DLADM_STATUS_NOTSUP);
 528         default:
 529                 return (DLADM_STATUS_BADVAL);
 530         }
 531 
 532         *bw = n * mult;
 533 
 534         /* check for overflow */
 535         if (*bw / mult != n)
 536                 return (DLADM_STATUS_BADARG);
 537 
 538         return (DLADM_STATUS_OK);
 539 }
 540 
 541 /*
 542  * Convert bandwidth in bps to a string in Mbps.  For values greater
 543  * than 1Mbps or 1000000, print a whole Mbps value.  For values that
 544  * have fractional Mbps in whole Kbps, print the bandwidth in a manner
 545  * similar to a floating point format.
 546  *
 547  *        bps       string
 548  *          0            0
 549  *        100            0
 550  *       2000        0.002
 551  *     431000        0.431
 552  *    1000000            1
 553  *    1030000        1.030
 554  *  100000000          100
 555  */
 556 const char *
 557 dladm_bw2str(int64_t bw, char *buf)
 558 {
 559         int kbps, mbps;
 560 
 561         kbps = (bw%1000000)/1000;
 562         mbps = bw/1000000;
 563         if (kbps != 0) {
 564                 if (mbps == 0)
 565                         (void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
 566                 else
 567                         (void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
 568                             kbps);
 569         } else {
 570                 (void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
 571         }
 572 
 573         return (buf);
 574 }
 575 
 576 #define LOCK_DB_PERMS   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
 577 
 578 static int
 579 i_dladm_lock_db(const char *lock_file, short type)
 580 {
 581         int     lock_fd;
 582         struct  flock lock;
 583 
 584         if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
 585             LOCK_DB_PERMS)) < 0)
 586                 return (-1);
 587 
 588         lock.l_type = type;
 589         lock.l_whence = SEEK_SET;
 590         lock.l_start = 0;
 591         lock.l_len = 0;
 592 
 593         if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
 594                 int err = errno;
 595 
 596                 (void) close(lock_fd);
 597                 (void) unlink(lock_file);
 598                 errno = err;
 599                 return (-1);
 600         }
 601         return (lock_fd);
 602 }
 603 
 604 static void
 605 i_dladm_unlock_db(const char *lock_file, int fd)
 606 {
 607         struct flock lock;
 608 
 609         if (fd < 0)
 610                 return;
 611 
 612         lock.l_type = F_UNLCK;
 613         lock.l_whence = SEEK_SET;
 614         lock.l_start = 0;
 615         lock.l_len = 0;
 616 
 617         (void) fcntl(fd, F_SETLKW, &lock);
 618         (void) close(fd);
 619         (void) unlink(lock_file);
 620 }
 621 
 622 /*
 623  * Given a link class, returns its class string.
 624  */
 625 const char *
 626 dladm_class2str(datalink_class_t class, char *buf)
 627 {
 628         const char *s;
 629 
 630         switch (class) {
 631         case DATALINK_CLASS_PHYS:
 632                 s = "phys";
 633                 break;
 634         case DATALINK_CLASS_VLAN:
 635                 s = "vlan";
 636                 break;
 637         case DATALINK_CLASS_AGGR:
 638                 s = "aggr";
 639                 break;
 640         case DATALINK_CLASS_VNIC:
 641                 s = "vnic";
 642                 break;
 643         case DATALINK_CLASS_ETHERSTUB:
 644                 s = "etherstub";
 645                 break;
 646         case DATALINK_CLASS_IPTUN:
 647                 s = "iptun";
 648                 break;
 649         case DATALINK_CLASS_SIMNET:
 650                 s = "simnet";
 651                 break;
 652         case DATALINK_CLASS_BRIDGE:
 653                 s = "bridge";
 654                 break;
 655         case DATALINK_CLASS_PART:
 656                 s = "part";
 657                 break;
 658         default:
 659                 s = "unknown";
 660                 break;
 661         }
 662 
 663         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 664         return (buf);
 665 }
 666 
 667 /*
 668  * Given a physical link media type, returns its media type string.
 669  */
 670 const char *
 671 dladm_media2str(uint32_t media, char *buf)
 672 {
 673         const char *s = "--";
 674         media_type_t *mt;
 675         int idx;
 676 
 677         for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
 678                 mt = media_type_table + idx;
 679                 if (mt->media_type == media) {
 680                         s = mt->media_type_str;
 681                         break;
 682                 }
 683         }
 684 
 685         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 686         return (buf);
 687 }
 688 
 689 /*
 690  * Given a physical link media type string, returns its media type constant.
 691  */
 692 uint32_t
 693 dladm_str2media(const char *buf)
 694 {
 695         media_type_t *mt;
 696         int idx;
 697 
 698         for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
 699                 mt = media_type_table + idx;
 700                 if (strcasecmp(buf, mt->media_type_str) == 0)
 701                         return (mt->media_type);
 702         }
 703 
 704         return (DL_OTHER);
 705 }
 706 
 707 dladm_status_t
 708 i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
 709     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
 710     void *arg, boolean_t writeop)
 711 {
 712         dladm_status_t  status = DLADM_STATUS_OK;
 713         FILE            *fp, *nfp = NULL;
 714         char            lock[MAXPATHLEN];
 715         char            file[MAXPATHLEN];
 716         char            newfile[MAXPATHLEN];
 717         char            *db_basename;
 718         int             nfd, lock_fd;
 719 
 720         /*
 721          * If we are called from a boot script such as net-physical,
 722          * it's quite likely that the root fs is still not writable.
 723          * For this case, it's ok for the lock creation to fail since
 724          * no one else could be accessing our configuration file.
 725          */
 726         db_basename = strrchr(db_file, '/');
 727         if (db_basename == NULL || db_basename[1] == '\0')
 728                 return (dladm_errno2status(EINVAL));
 729         db_basename++;
 730         (void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
 731         if ((lock_fd = i_dladm_lock_db
 732             (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
 733                 return (dladm_errno2status(errno));
 734 
 735         (void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
 736         if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
 737                 int     err = errno;
 738 
 739                 i_dladm_unlock_db(lock, lock_fd);
 740                 if (err == ENOENT)
 741                         return (DLADM_STATUS_DBNOTFOUND);
 742 
 743                 return (dladm_errno2status(err));
 744         }
 745 
 746         if (writeop) {
 747                 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
 748                     dladm_rootdir, db_file);
 749                 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
 750                     db_perms)) < 0) {
 751                         (void) fclose(fp);
 752                         i_dladm_unlock_db(lock, lock_fd);
 753                         return (dladm_errno2status(errno));
 754                 }
 755 
 756                 if ((nfp = fdopen(nfd, "w")) == NULL) {
 757                         (void) close(nfd);
 758                         (void) fclose(fp);
 759                         (void) unlink(newfile);
 760                         i_dladm_unlock_db(lock, lock_fd);
 761                         return (dladm_errno2status(errno));
 762                 }
 763         }
 764         status = (*process_db)(handle, arg, fp, nfp);
 765         if (!writeop || status != DLADM_STATUS_OK)
 766                 goto done;
 767 
 768         /* Set permissions on file to db_perms */
 769         if (fchmod(nfd, db_perms) < 0) {
 770                 status = dladm_errno2status(errno);
 771                 goto done;
 772         }
 773 
 774         /*
 775          * Configuration files need to be owned by the 'dladm' user and
 776          * 'netadm' group.
 777          */
 778         if (fchown(nfd, UID_DLADM, GID_NETADM) < 0) {
 779                 status = dladm_errno2status(errno);
 780                 goto done;
 781         }
 782 
 783         if (fflush(nfp) == EOF) {
 784                 status = dladm_errno2status(errno);
 785                 goto done;
 786         }
 787         (void) fclose(fp);
 788         (void) fclose(nfp);
 789 
 790         if (rename(newfile, file) < 0) {
 791                 (void) unlink(newfile);
 792                 i_dladm_unlock_db(lock, lock_fd);
 793                 return (dladm_errno2status(errno));
 794         }
 795 
 796         i_dladm_unlock_db(lock, lock_fd);
 797         return (DLADM_STATUS_OK);
 798 
 799 done:
 800         if (nfp != NULL) {
 801                 (void) fclose(nfp);
 802                 if (status != DLADM_STATUS_OK)
 803                         (void) unlink(newfile);
 804         }
 805         (void) fclose(fp);
 806         i_dladm_unlock_db(lock, lock_fd);
 807         return (status);
 808 }
 809 
 810 dladm_status_t
 811 dladm_set_rootdir(const char *rootdir)
 812 {
 813         DIR     *dp;
 814 
 815         if (rootdir == NULL || *rootdir != '/' ||
 816             (dp = opendir(rootdir)) == NULL)
 817                 return (DLADM_STATUS_BADARG);
 818 
 819         (void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
 820         (void) closedir(dp);
 821         return (DLADM_STATUS_OK);
 822 }
 823 
 824 boolean_t
 825 dladm_valid_linkname(const char *link)
 826 {
 827         size_t          len = strlen(link);
 828         const char      *cp;
 829         int             nd = 0;
 830 
 831         if (len >= MAXLINKNAMELEN)
 832                 return (B_FALSE);
 833 
 834         /* Link name cannot start with a digit */
 835         if (isdigit(link[0]))
 836                 return (B_FALSE);
 837         /* Link name must end with a number without leading zeroes */
 838         cp = link + len - 1;
 839         while (isdigit(*cp)) {
 840                 cp--;
 841                 nd++;
 842         }
 843         if (nd == 0 || (nd > 1 && *(cp + 1) == '0'))
 844                 return (B_FALSE);
 845 
 846         /*
 847          * The legal characters in a link name are:
 848          * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
 849          */
 850         for (cp = link; *cp != '\0'; cp++) {
 851                 if ((isalnum(*cp) == 0) && (*cp != '_') && (*cp != '.'))
 852                         return (B_FALSE);
 853         }
 854 
 855         return (B_TRUE);
 856 }
 857 
 858 /*
 859  * Convert priority string to a value.
 860  */
 861 dladm_status_t
 862 dladm_str2pri(char *token, mac_priority_level_t *pri)
 863 {
 864         if (strlen(token) == strlen("low") &&
 865             strncasecmp(token, "low", strlen("low")) == 0) {
 866                 *pri = MPL_LOW;
 867         } else if (strlen(token) == strlen("medium") &&
 868             strncasecmp(token, "medium", strlen("medium")) == 0) {
 869                 *pri = MPL_MEDIUM;
 870         } else if (strlen(token) == strlen("high") &&
 871             strncasecmp(token, "high", strlen("high")) == 0) {
 872                 *pri = MPL_HIGH;
 873         } else {
 874                 return (DLADM_STATUS_BADVAL);
 875         }
 876         return (DLADM_STATUS_OK);
 877 }
 878 
 879 /*
 880  * Convert priority value to a string.
 881  */
 882 const char *
 883 dladm_pri2str(mac_priority_level_t pri, char *buf)
 884 {
 885         const char      *s;
 886 
 887         switch (pri) {
 888         case MPL_LOW:
 889                 s = "low";
 890                 break;
 891         case MPL_MEDIUM:
 892                 s = "medium";
 893                 break;
 894         case MPL_HIGH:
 895                 s = "high";
 896                 break;
 897         default:
 898                 s = "--";
 899                 break;
 900         }
 901         (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
 902         return (buf);
 903 }
 904 
 905 /*
 906  * Convert protect string to a value.
 907  */
 908 dladm_status_t
 909 dladm_str2protect(char *token, uint32_t *ptype)
 910 {
 911         link_protect_t  *lp;
 912         int             i;
 913 
 914         for (i = 0; i < LPTYPES; i++) {
 915                 lp = &link_protect_types[i];
 916                 if (strcmp(token, lp->lp_name) == 0) {
 917                         *ptype = lp->lp_type;
 918                         return (DLADM_STATUS_OK);
 919                 }
 920         }
 921         return (DLADM_STATUS_BADVAL);
 922 }
 923 
 924 /*
 925  * Convert protect value to a string.
 926  */
 927 const char *
 928 dladm_protect2str(uint32_t ptype, char *buf)
 929 {
 930         const char      *s = "--";
 931         link_protect_t  *lp;
 932         int             i;
 933 
 934         for (i = 0; i < LPTYPES; i++) {
 935                 lp = &link_protect_types[i];
 936                 if (lp->lp_type == ptype) {
 937                         s = lp->lp_name;
 938                         break;
 939                 }
 940         }
 941         (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
 942         return (buf);
 943 }
 944 
 945 /*
 946  * Convert an IPv4 address to/from a string.
 947  */
 948 const char *
 949 dladm_ipv4addr2str(void *addr, char *buf)
 950 {
 951         if (inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN) == NULL)
 952                 buf[0] = '\0';
 953 
 954         return (buf);
 955 }
 956 
 957 dladm_status_t
 958 dladm_str2ipv4addr(char *token, void *addr)
 959 {
 960         return (inet_pton(AF_INET, token, addr) == 1 ?
 961             DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
 962 }
 963 
 964 const char *
 965 dladm_ipv6addr2str(void *addr, char *buf)
 966 {
 967         if (inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN) == NULL)
 968                 buf[0] = '\0';
 969 
 970         return (buf);
 971 }
 972 
 973 dladm_status_t
 974 dladm_str2ipv6addr(char *token, void *addr)
 975 {
 976         return (inet_pton(AF_INET6, token, addr) == 1 ?
 977             DLADM_STATUS_OK : DLADM_STATUS_INVALID_IP);
 978 }
 979 
 980 /*
 981  * Find the set bits in a mask.
 982  * This is used for expanding a bitmask into individual sub-masks
 983  * which can be used for further processing.
 984  */
 985 void
 986 dladm_find_setbits32(uint32_t mask, uint32_t *list, uint32_t *cnt)
 987 {
 988         int     i, c = 0;
 989 
 990         for (i = 0; i < 32; i++) {
 991                 if (((1 << i) & mask) != 0)
 992                         list[c++] = 1 << i;
 993         }
 994         *cnt = c;
 995 }
 996 
 997 void
 998 dladm_free_args(dladm_arg_list_t *list)
 999 {
1000         if (list != NULL) {
1001                 free(list->al_buf);
1002                 free(list);
1003         }
1004 }
1005 
1006 dladm_status_t
1007 dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
1008 {
1009         dladm_arg_list_t        *list;
1010         dladm_arg_info_t        *aip;
1011         char                    *buf, *curr;
1012         int                     len, i;
1013 
1014         if (str == NULL)
1015                 return (DLADM_STATUS_BADVAL);
1016 
1017         if (str[0] == '\0')
1018                 return (DLADM_STATUS_OK);
1019 
1020         list = malloc(sizeof (dladm_arg_list_t));
1021         if (list == NULL)
1022                 return (dladm_errno2status(errno));
1023 
1024         list->al_count = 0;
1025         list->al_buf = buf = strdup(str);
1026         if (buf == NULL)
1027                 return (dladm_errno2status(errno));
1028 
1029         curr = buf;
1030         len = strlen(buf);
1031         aip = NULL;
1032         for (i = 0; i < len; i++) {
1033                 char            c = buf[i];
1034                 boolean_t       match = (c == '=' || c == ',');
1035 
1036                 if (!match && i != len - 1)
1037                         continue;
1038 
1039                 if (match) {
1040                         buf[i] = '\0';
1041                         if (*curr == '\0')
1042                                 goto fail;
1043                 }
1044 
1045                 if (aip != NULL && c != '=') {
1046                         if (aip->ai_count > DLADM_MAX_ARG_VALS)
1047                                 goto fail;
1048 
1049                         if (novalues)
1050                                 goto fail;
1051 
1052                         aip->ai_val[aip->ai_count] = curr;
1053                         aip->ai_count++;
1054                 } else {
1055                         if (list->al_count > DLADM_MAX_ARG_VALS)
1056                                 goto fail;
1057 
1058                         aip = &list->al_info[list->al_count];
1059                         aip->ai_name = curr;
1060                         aip->ai_count = 0;
1061                         list->al_count++;
1062                         if (c == ',')
1063                                 aip = NULL;
1064                 }
1065                 curr = buf + i + 1;
1066         }
1067 
1068         *listp = list;
1069         return (DLADM_STATUS_OK);
1070 
1071 fail:
1072         dladm_free_args(list);
1073         return (DLADM_STATUS_FAILED);
1074 }
1075 
1076 /*
1077  * mac_propval_range_t functions.  Currently implemented for only
1078  * ranges of uint32_t elements, but can be expanded as required.
1079  */
1080 /*
1081  * Convert an array of strings (which can be ranges or individual
1082  * elements) into a single mac_propval_range_t structure which
1083  * is allocated here but should be freed by the caller.
1084  */
1085 dladm_status_t
1086 dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type,
1087     mac_propval_range_t **range)
1088 {
1089         int                     i;
1090         char                    *endp;
1091         mac_propval_range_t     *rangep;
1092         dladm_status_t          status = DLADM_STATUS_OK;
1093 
1094         switch (type) {
1095         case MAC_PROPVAL_UINT32: {
1096                 mac_propval_uint32_range_t      *ur;
1097 
1098                 /* Allocate range structure */
1099                 rangep = malloc(sizeof (mac_propval_range_t) +
1100                     (val_cnt-1)*(sizeof (mac_propval_uint32_range_t)));
1101                 if (rangep == NULL)
1102                         return (DLADM_STATUS_NOMEM);
1103 
1104                 rangep->mpr_count = 0;
1105                 ur = &rangep->mpr_range_uint32[0];
1106                 for (i = 0; i < val_cnt; i++, ur++) {
1107                         errno = 0;
1108                         if (strchr(prop_val[i], '-') == NULL) {
1109                                 /* single element */
1110                                 ur->mpur_min = ur->mpur_max =
1111                                     strtol(prop_val[i], &endp, 10);
1112                                 if ((endp != NULL) && (*endp != '\0')) {
1113                                         return (DLADM_STATUS_BADRANGE);
1114                                 }
1115                         } else {
1116                                 /* range of elements */
1117                                 ur->mpur_min = strtol(prop_val[i], &endp, 10);
1118                                 if (*endp++ != '-')
1119                                         return (DLADM_STATUS_BADRANGE);
1120                                 ur->mpur_max = strtol(endp, &endp, 10);
1121                                 if (endp != NULL && *endp != '\0' ||
1122                                     ur->mpur_max < ur->mpur_min)
1123                                         return (DLADM_STATUS_BADRANGE);
1124                         }
1125                         rangep->mpr_count++;
1126                 }
1127                 break;
1128         }
1129         default:
1130                 return (DLADM_STATUS_BADVAL);
1131         }
1132 
1133         rangep->mpr_type = type;
1134         *range = rangep;
1135 
1136         return (status);
1137 }
1138 
1139 /*
1140  * Convert a mac_propval_range_t structure into an array of elements.
1141  */
1142 dladm_status_t
1143 dladm_range2list(mac_propval_range_t *rangep, void *elem, uint_t *nelem)
1144 {
1145         int             i, j, k;
1146         dladm_status_t  status = DLADM_STATUS_OK;
1147 
1148         switch (rangep->mpr_type) {
1149         case MAC_PROPVAL_UINT32: {
1150                 mac_propval_uint32_range_t      *ur;
1151                 uint32_t                        *elem32 = elem;
1152 
1153                 k = 0;
1154                 ur = &rangep->mpr_range_uint32[0];
1155                 for (i = 0; i < rangep->mpr_count; i++, ur++) {
1156                         for (j = 0; j <= ur->mpur_max - ur->mpur_min; j++) {
1157                                 elem32[k++] = ur->mpur_min + j;
1158                                 if (k > *nelem) {
1159                                         status = DLADM_STATUS_TOOMANYELEMENTS;
1160                                         break;
1161                                 }
1162                         }
1163                 }
1164                 *nelem = k;
1165                 break;
1166         }
1167         default:
1168                 status = DLADM_STATUS_BADVAL;
1169                 break;
1170         }
1171         return (status);
1172 }
1173 
1174 /*
1175  * Convert a mac_propval_range_t structure into an array of strings
1176  * of single elements or ranges.
1177  */
1178 int
1179 dladm_range2strs(mac_propval_range_t *rangep, char **prop_val)
1180 {
1181         int     i;
1182 
1183         switch (rangep->mpr_type) {
1184         case MAC_PROPVAL_UINT32: {
1185                 mac_propval_uint32_range_t      *ur;
1186 
1187                 /* Write ranges and individual elements */
1188                 ur = &rangep->mpr_range_uint32[0];
1189                 for (i = 0; i < rangep->mpr_count; i++, ur++) {
1190                         if (ur->mpur_min == ur->mpur_max) {
1191                                 /* single element */
1192                                 (void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1193                                     "%u", ur->mpur_min);
1194                         } else {
1195                                 /* range of elements */
1196                                 (void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1197                                     "%u-%u", ur->mpur_min, ur->mpur_max);
1198                         }
1199                 }
1200                 return (0);
1201         }
1202         default:
1203                 break;
1204         }
1205         return (EINVAL);
1206 }
1207 
1208 static int
1209 uint32cmp(const void *a, const void *b)
1210 {
1211         return (*(uint32_t *)a - *(uint32_t *)b);
1212 }
1213 
1214 /*
1215  * Sort and convert an array of elements into a single
1216  * mac_propval_range_t structure which is allocated here but
1217  * should be freed by the caller.
1218  */
1219 dladm_status_t
1220 dladm_list2range(void *elem, uint_t nelem, mac_propval_type_t type,
1221     mac_propval_range_t **range)
1222 {
1223         int                     i;
1224         uint_t                  nr = 0;
1225         mac_propval_range_t     *rangep;
1226         dladm_status_t          status = DLADM_STATUS_OK;
1227 
1228         switch (type) {
1229         case MAC_PROPVAL_UINT32: {
1230                 mac_propval_uint32_range_t      *ur;
1231                 uint32_t                        *elem32 = elem;
1232                 uint32_t                        *sort32;
1233 
1234                 /* Allocate range structure */
1235                 rangep = malloc(sizeof (mac_propval_range_t) +
1236                     (nelem-1)*(sizeof (mac_propval_uint32_range_t)));
1237                 if (rangep == NULL)
1238                         return (DLADM_STATUS_NOMEM);
1239 
1240                 /* Allocate array for sorting */
1241                 sort32 = malloc(nelem * sizeof (uint32_t));
1242                 if (sort32 == NULL) {
1243                         free(rangep);
1244                         return (DLADM_STATUS_NOMEM);
1245                 }
1246 
1247                 /* Copy and sort list */
1248                 for (i = 0; i < nelem; i++)
1249                         sort32[i] =  elem32[i];
1250                 if (nelem > 1)
1251                         qsort(sort32, nelem, sizeof (uint32_t), uint32cmp);
1252 
1253                 /* Convert list to ranges */
1254                 ur = &rangep->mpr_range_uint32[0];
1255                 ur->mpur_min = ur->mpur_max = sort32[0];
1256                 for (i = 1; i < nelem; i++) {
1257                         if (sort32[i]-sort32[i-1] == 1) {
1258                                 /* part of current range */
1259                                 ur->mpur_max = sort32[i];
1260                         } else {
1261                                 /* start a new range */
1262                                 nr++; ur++;
1263                                 ur->mpur_min = ur->mpur_max = sort32[i];
1264                         }
1265                 }
1266                 free(sort32);
1267                 break;
1268         }
1269         default:
1270                 return (DLADM_STATUS_BADRANGE);
1271         }
1272 
1273         rangep->mpr_type = type;
1274         rangep->mpr_count = nr + 1;
1275         *range = rangep;
1276 
1277         return (status);
1278 }