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