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