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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <string.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <stropts.h> 36 #include <stdlib.h> 37 #include <errno.h> 38 #include <libdevinfo.h> 39 #include <libdlpi.h> 40 #include <libdladm.h> 41 #include <libintl.h> 42 #include <sys/dld.h> 43 #include <net/if.h> 44 45 #define DLADM_DB "/etc/datalink.conf" 46 #define DLADM_DB_TMP "/etc/datalink.conf.new" 47 #define DLADM_DB_LOCK "/tmp/datalink.conf.lock" 48 #define DLADM_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 49 50 #define MAXLINELEN 1024 51 #define LISTSZ 1024 52 #define MAXPATHLEN 1024 53 54 #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) 55 56 typedef struct i_dladm_walk { 57 int fd; 58 boolean_t found; 59 const char *name; 60 } i_dladm_walk_t; 61 62 /* 63 * Open and lock the aggregation configuration file lock. The lock is 64 * acquired as a reader (F_RDLCK) or writer (F_WRLCK). 65 */ 66 static int 67 i_dladm_lock_db(short type) 68 { 69 int lock_fd; 70 struct flock lock; 71 72 if ((lock_fd = open(DLADM_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC, 73 DLADM_DB_PERMS)) < 0) 74 return (-1); 75 76 lock.l_type = type; 77 lock.l_whence = SEEK_SET; 78 lock.l_start = 0; 79 lock.l_len = 0; 80 81 if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 82 (void) close(lock_fd); 83 (void) unlink(DLADM_DB_LOCK); 84 return (-1); 85 } 86 return (lock_fd); 87 } 88 89 /* 90 * Unlock and close the specified file. 91 */ 92 static void 93 i_dladm_unlock_db(int fd) 94 { 95 struct flock lock; 96 97 if (fd < 0) 98 return; 99 100 lock.l_type = F_UNLCK; 101 lock.l_whence = SEEK_SET; 102 lock.l_start = 0; 103 lock.l_len = 0; 104 105 (void) fcntl(fd, F_SETLKW, &lock); 106 (void) close(fd); 107 (void) unlink(DLADM_DB_LOCK); 108 } 109 110 /* 111 * Parse a line of the configuration file, returns -1 if an error 112 * occured. 113 */ 114 static int 115 i_dladm_db_decode(char *buf, char *name, dladm_attr_t *dap) 116 { 117 char *attr[DLADM_NATTR + 1]; 118 char *endp = NULL; 119 char *lasts = NULL; 120 uint_t i; 121 122 attr[0] = strtok_r(buf, " \t\n", &lasts); 123 for (i = 1; i < DLADM_NATTR + 1; i++) { 124 if ((attr[i] = strtok_r(NULL, " \t\n", &lasts)) == NULL) 125 return (-1); 126 } 127 128 if (i != DLADM_NATTR + 1) { 129 errno = EINVAL; 130 return (-1); 131 } 132 133 (void) strlcpy(name, attr[0], IFNAMSIZ); 134 (void) strlcpy(dap->da_dev, attr[1], MAXNAMELEN); 135 136 errno = 0; 137 dap->da_port = (int)strtol(attr[2], &endp, 10); 138 if (errno != 0 || *endp != '\0') { 139 return (-1); 140 } 141 142 errno = 0; 143 dap->da_vid = (int)strtol(attr[3], &endp, 10); 144 if (errno != 0 || *endp != '\0') { 145 return (-1); 146 } 147 148 return (0); 149 } 150 151 /* 152 * Add a datalink of the specified name and attributes to 153 * the configuration repository. 154 */ 155 static int 156 i_dladm_db_add(const char *name, dladm_attr_t *dap, const char *root, 157 dladm_diag_t *diag) 158 { 159 FILE *fp; 160 int lock_fd, retval = -1; 161 char line[MAXLINELEN]; 162 char dl_name[IFNAMSIZ]; 163 dladm_attr_t da; 164 char *db_file; 165 char db_file_buf[MAXPATHLEN]; 166 167 if (root == NULL) { 168 db_file = DLADM_DB; 169 } else { 170 (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 171 DLADM_DB); 172 db_file = db_file_buf; 173 } 174 175 if ((lock_fd = i_dladm_lock_db(F_WRLCK)) < 0) 176 return (-1); 177 178 if ((fp = fopen(db_file, "r+")) == NULL && 179 (fp = fopen(db_file, "w")) == NULL) { 180 *diag = DLADM_DIAG_REPOSITORY_OPENFAIL; 181 i_dladm_unlock_db(lock_fd); 182 return (-1); 183 } 184 185 while (fgets(line, MAXLINELEN, fp) != NULL) { 186 /* skip comments and blank lines */ 187 if (BLANK_LINE(line)) 188 continue; 189 190 /* skip corrupted lines */ 191 if (i_dladm_db_decode(line, dl_name, &da) < 0) 192 continue; 193 194 if (strcmp(dl_name, name) == 0) { 195 errno = EEXIST; 196 goto failed; 197 } 198 199 if (strcmp(da.da_dev, dap->da_dev) == 0 && 200 da.da_port == dap->da_port && 201 da.da_vid == dap->da_vid) { 202 errno = EEXIST; 203 goto failed; 204 } 205 } 206 207 (void) snprintf(line, MAXPATHLEN, "%s\t%s\t%u\t%u\n", 208 name, dap->da_dev, dap->da_port, dap->da_vid); 209 210 if (fputs(line, fp) == EOF) { 211 *diag = DLADM_DIAG_REPOSITORY_WRITEFAIL; 212 goto failed; 213 } 214 215 if (fflush(fp) == EOF) 216 goto failed; 217 218 retval = 0; 219 220 failed: 221 (void) fclose(fp); 222 i_dladm_unlock_db(lock_fd); 223 return (retval); 224 } 225 226 /* 227 * Remove the datalink of the specified name from the configuration repository. 228 */ 229 static int 230 i_dladm_db_remove(const char *name, const char *root) 231 { 232 FILE *fp; 233 FILE *nfp; 234 int nfd, lock_fd; 235 char line[MAXLINELEN]; 236 char copy[MAXLINELEN]; 237 char dl_name[IFNAMSIZ]; 238 dladm_attr_t da; 239 boolean_t found = B_FALSE; 240 char *db_file, *tmp_db_file; 241 char db_file_buf[MAXPATHLEN]; 242 char tmp_db_file_buf[MAXPATHLEN]; 243 244 if (root == NULL) { 245 db_file = DLADM_DB; 246 tmp_db_file = DLADM_DB_TMP; 247 } else { 248 (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 249 DLADM_DB); 250 (void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root, 251 DLADM_DB_TMP); 252 db_file = db_file_buf; 253 tmp_db_file = tmp_db_file_buf; 254 } 255 256 if ((lock_fd = i_dladm_lock_db(F_WRLCK)) < 0) 257 return (-1); 258 259 if ((fp = fopen(db_file, "r")) == NULL) { 260 i_dladm_unlock_db(lock_fd); 261 return (-1); 262 } 263 264 if ((nfd = open(tmp_db_file, O_WRONLY | O_CREAT | O_TRUNC, 265 DLADM_DB_PERMS)) < 0) { 266 (void) fclose(fp); 267 i_dladm_unlock_db(lock_fd); 268 return (-1); 269 } 270 271 if ((nfp = fdopen(nfd, "w")) == NULL) { 272 (void) close(nfd); 273 (void) fclose(fp); 274 (void) unlink(tmp_db_file); 275 i_dladm_unlock_db(lock_fd); 276 return (-1); 277 } 278 279 while (fgets(line, MAXLINELEN, fp) != NULL) { 280 (void) strlcpy(copy, line, MAXLINELEN); 281 282 /* skip comments */ 283 if (!BLANK_LINE(line)) { 284 if (i_dladm_db_decode(line, dl_name, &da) < 0) { 285 continue; 286 } 287 288 if (strcmp(dl_name, name) == 0) { 289 found = B_TRUE; 290 continue; 291 } 292 } 293 294 if (fputs(copy, nfp) == EOF) 295 goto failed; 296 } 297 298 if (!found) { 299 errno = ENOENT; 300 goto failed; 301 } 302 303 if (fflush(nfp) == EOF) 304 goto failed; 305 306 (void) fclose(fp); 307 (void) fclose(nfp); 308 if (rename(tmp_db_file, db_file) < 0) { 309 (void) unlink(tmp_db_file); 310 i_dladm_unlock_db(lock_fd); 311 return (-1); 312 } 313 314 i_dladm_unlock_db(lock_fd); 315 return (0); 316 317 failed: 318 (void) fclose(fp); 319 (void) fclose(nfp); 320 (void) unlink(tmp_db_file); 321 i_dladm_unlock_db(lock_fd); 322 323 return (-1); 324 } 325 326 /* 327 * For each datalink in the configuration repository, invoke the specified 328 * callback. If the datalink name is specified, the callback is invoked 329 * only for datalink of the matching name. 330 */ 331 static void 332 i_dladm_db_walk(void (*fn)(void *, const char *, dladm_attr_t *), 333 const char *name, void *arg) 334 { 335 FILE *fp; 336 int lock_fd; 337 char line[MAXLINELEN]; 338 char dl_name[IFNAMSIZ]; 339 dladm_attr_t da; 340 341 lock_fd = i_dladm_lock_db(F_RDLCK); 342 343 if ((fp = fopen(DLADM_DB, "r")) == NULL) { 344 i_dladm_unlock_db(lock_fd); 345 return; 346 } 347 348 while (fgets(line, MAXLINELEN, fp) != NULL) { 349 /* skip comments */ 350 if (BLANK_LINE(line)) 351 continue; 352 353 if (i_dladm_db_decode(line, dl_name, &da) < 0) 354 continue; 355 356 if (name != NULL && strcmp(name, dl_name) != 0) 357 continue; 358 359 fn(arg, dl_name, &da); 360 } 361 362 (void) fclose(fp); 363 i_dladm_unlock_db(lock_fd); 364 } 365 366 /* 367 * For each datalink in the configuration repository, invoke the 368 * specified callback. 369 */ 370 void 371 dladm_db_walk(void (*fn)(void *, const char *, dladm_attr_t *), 372 void *arg) 373 { 374 i_dladm_db_walk(fn, NULL, arg); 375 } 376 377 /* 378 * Issue an ioctl to the specified file descriptor attached to the 379 * DLD control driver interface. 380 */ 381 static int 382 i_dladm_ioctl(int fd, char *ic_dp, int ic_cmd, int ic_len) 383 { 384 struct strioctl iocb; 385 386 iocb.ic_cmd = ic_cmd; 387 iocb.ic_timout = 0; 388 iocb.ic_len = ic_len; 389 iocb.ic_dp = ic_dp; 390 391 return (ioctl(fd, I_STR, &iocb)); 392 } 393 394 /* 395 * Issue a DLDIOCCREATE ioctl command. 396 */ 397 static int 398 i_dladm_create(int fd, const char *name, dladm_attr_t *dap) 399 { 400 dld_ioc_create_t dic; 401 402 if (strlen(name) >= IFNAMSIZ) { 403 errno = EINVAL; 404 return (-1); 405 } 406 407 (void) strlcpy(dic.dic_name, name, IFNAMSIZ); 408 (void) strlcpy(dic.dic_dev, dap->da_dev, MAXNAMELEN); 409 dic.dic_port = dap->da_port; 410 dic.dic_vid = dap->da_vid; 411 412 return (i_dladm_ioctl(fd, (char *)&dic, DLDIOCCREATE, sizeof (dic))); 413 } 414 415 /* 416 * Datalink bringup callback. Brings up the specified datalink. 417 */ 418 static void 419 i_dladm_up(void *arg, const char *name, dladm_attr_t *dap) 420 { 421 i_dladm_walk_t *wp = arg; 422 423 wp->found = B_TRUE; 424 (void) i_dladm_create(wp->fd, name, dap); 425 } 426 427 /* 428 * Bring down the datalink of the specified name. 429 */ 430 static int 431 i_dladm_destroy(int fd, const char *name) 432 { 433 dld_ioc_destroy_t did; 434 435 if (strlen(name) >= IFNAMSIZ) { 436 errno = EINVAL; 437 return (-1); 438 } 439 440 (void) strlcpy(did.did_name, name, IFNAMSIZ); 441 442 return (i_dladm_ioctl(fd, (char *)&did, DLDIOCDESTROY, sizeof (did))); 443 } 444 445 /* 446 * Bring down one or all currently active datalinks. 447 */ 448 /*ARGSUSED*/ 449 static void 450 i_dladm_down(void *arg, const char *name) 451 { 452 i_dladm_walk_t *wp = (i_dladm_walk_t *)arg; 453 454 wp->found = B_TRUE; 455 456 if (wp->name != NULL && strcmp(name, wp->name) != 0) 457 return; 458 459 (void) i_dladm_destroy(wp->fd, name); 460 } 461 462 /* 463 * Return the attributes of the specified datalink from the DLD driver. 464 */ 465 static int 466 i_dladm_info(int fd, const char *name, dladm_attr_t *dap) 467 { 468 dld_ioc_attr_t dia; 469 470 if (strlen(name) >= IFNAMSIZ) { 471 errno = EINVAL; 472 return (-1); 473 } 474 475 (void) strlcpy(dia.dia_name, name, IFNAMSIZ); 476 477 if (i_dladm_ioctl(fd, (char *)&dia, DLDIOCATTR, sizeof (dia)) < 0) 478 return (-1); 479 480 (void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN); 481 dap->da_port = dia.dia_port; 482 dap->da_vid = dia.dia_vid; 483 484 return (0); 485 } 486 487 /* 488 * Callback function used to count the number of DDI_NT_NET. 489 */ 490 /* ARGSUSED */ 491 static int 492 i_dladm_nt_net_count(di_node_t node, di_minor_t minor, void *arg) 493 { 494 uint_t *countp = arg; 495 496 (*countp)++; 497 return (DI_WALK_CONTINUE); 498 } 499 500 /* 501 * Adds a datalink to the array corresponding to arg. 502 */ 503 static void 504 i_dladm_nt_net_add(void *arg, char *name) 505 { 506 char **array = arg; 507 char *elem; 508 509 for (;;) { 510 elem = *(array++); 511 if (elem[0] == '\0') 512 break; 513 if (strcmp(elem, name) == 0) 514 return; 515 } 516 517 (void) strlcpy(elem, name, MAXNAMELEN); 518 } 519 520 /* 521 * Walker callback invoked for each DDI_NT_NET node. 522 */ 523 static int 524 i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg) 525 { 526 dl_info_ack_t dlia; 527 char name[IFNAMSIZ]; 528 int fd; 529 char *provider; 530 uint_t ppa; 531 532 provider = di_minor_name(minor); 533 534 if ((fd = dlpi_open(provider)) < 0) 535 return (DI_WALK_CONTINUE); 536 537 if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0) { 538 (void) dlpi_close(fd); 539 return (DI_WALK_CONTINUE); 540 } 541 542 if (dlia.dl_provider_style == DL_STYLE1) { 543 i_dladm_nt_net_add(arg, provider); 544 (void) dlpi_close(fd); 545 return (DI_WALK_CONTINUE); 546 } 547 548 ppa = di_instance(node); 549 550 if (dlpi_attach(fd, -1, ppa) < 0) { 551 (void) dlpi_close(fd); 552 return (DI_WALK_CONTINUE); 553 } 554 555 (void) snprintf(name, IFNAMSIZ - 1, "%s%d", provider, ppa); 556 i_dladm_nt_net_add(arg, name); 557 (void) dlpi_close(fd); 558 return (DI_WALK_CONTINUE); 559 } 560 561 /* 562 * Invoke the specified callback function for each active DDI_NT_NET 563 * node. 564 */ 565 int 566 dladm_walk(void (*fn)(void *, const char *), void *arg) 567 { 568 di_node_t root; 569 uint_t count; 570 char **array; 571 char *elem; 572 int i; 573 574 if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { 575 errno = EFAULT; 576 return (-1); 577 } 578 579 count = 0; 580 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, (void *)&count, 581 i_dladm_nt_net_count); 582 583 if (count == 0) 584 return (0); 585 586 if ((array = malloc(count * sizeof (char *))) == NULL) 587 goto done; 588 589 for (i = 0; i < count; i++) { 590 if ((array[i] = malloc(IFNAMSIZ)) != NULL) { 591 (void) memset(array[i], '\0', IFNAMSIZ); 592 continue; 593 } 594 595 while (--i >= 0) 596 free(array[i]); 597 goto done; 598 } 599 600 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, (void *)array, 601 i_dladm_nt_net_walk); 602 di_fini(root); 603 604 for (i = 0; i < count; i++) { 605 elem = array[i]; 606 if (elem[0] != '\0') 607 fn(arg, (const char *)elem); 608 free(elem); 609 } 610 611 done: 612 free(array); 613 return (0); 614 } 615 616 /* 617 * Create the link of specified name and attributes. Adds it to the 618 * configuration repository if DLADM_LINK_TEMP is not set. Errors 619 * will be ignored if DLADM_LINK_FORCED is set. 620 */ 621 int 622 dladm_link(const char *name, dladm_attr_t *dap, int flags, 623 const char *root, dladm_diag_t *diag) 624 { 625 int fd; 626 boolean_t tempop = (flags & DLADM_LINK_TEMP); 627 boolean_t forced = (flags & DLADM_LINK_FORCED); 628 629 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 630 *diag = DLADM_DIAG_DEVICE_OPENFAIL; 631 return (-1); 632 } 633 634 if (!tempop) { 635 if (i_dladm_db_add(name, dap, root, diag) < 0 && !forced) 636 goto failed; 637 } 638 639 if (i_dladm_create(fd, name, dap) < 0 && !forced) { 640 if (errno == EINVAL) { 641 *diag = DLADM_DIAG_INVALID_INTFNAME; 642 } 643 if (!tempop) 644 (void) i_dladm_db_remove(name, root); 645 goto failed; 646 } 647 648 (void) close(fd); 649 return (0); 650 651 failed: 652 (void) close(fd); 653 return (-1); 654 } 655 656 /* 657 * Instantiate the datalink of specified name. Brings up all datalinks 658 * if name is NULL. 659 */ 660 int 661 dladm_up(const char *name, dladm_diag_t *diag) 662 { 663 i_dladm_walk_t walk; 664 665 if ((walk.fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 666 *diag = 667 DLADM_DIAG_DEVICE_OPENFAIL; 668 return (-1); 669 } 670 671 walk.found = B_FALSE; 672 i_dladm_db_walk(i_dladm_up, name, (void *)&walk); 673 if (name != NULL && !walk.found) { 674 (void) close(walk.fd); 675 errno = ENOENT; 676 return (-1); 677 } 678 679 (void) close(walk.fd); 680 return (0); 681 } 682 683 /* 684 * Deletes the link of specified name. 685 */ 686 int 687 dladm_unlink(const char *name, boolean_t tempop, const char *root, 688 dladm_diag_t *diag) 689 { 690 int fd; 691 692 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 693 *diag = DLADM_DIAG_DEVICE_OPENFAIL; 694 return (-1); 695 } 696 697 if (i_dladm_destroy(fd, name) < 0) { 698 if (errno == EINVAL) 699 *diag = DLADM_DIAG_INVALID_LINKNAME; 700 goto failed; 701 } 702 703 if (!tempop) 704 (void) i_dladm_db_remove(name, root); 705 (void) close(fd); 706 return (0); 707 708 failed: 709 (void) close(fd); 710 return (-1); 711 } 712 713 /* 714 * Brings down the datalink of specified name. Brings down all datalinks 715 * if name == NULL. 716 */ 717 int 718 dladm_down(const char *name, dladm_diag_t *diag) 719 { 720 i_dladm_walk_t walk; 721 722 if ((walk.fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 723 *diag = DLADM_DIAG_DEVICE_OPENFAIL; 724 return (-1); 725 } 726 walk.found = B_FALSE; 727 walk.name = name; 728 729 if (dladm_walk(i_dladm_down, (void *)&walk) < 0) { 730 (void) close(walk.fd); 731 return (-1); 732 } 733 734 if (name != NULL && !walk.found) { 735 (void) close(walk.fd); 736 errno = ENOENT; 737 return (-1); 738 } 739 740 (void) close(walk.fd); 741 return (0); 742 } 743 744 /* 745 * Returns the current attributes of the specified datalink. 746 */ 747 int 748 dladm_info(const char *name, dladm_attr_t *dap) 749 { 750 int fd; 751 752 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 753 return (-1); 754 755 if (i_dladm_info(fd, name, dap) < 0) 756 goto failed; 757 758 (void) close(fd); 759 return (0); 760 761 failed: 762 (void) close(fd); 763 return (-1); 764 } 765 766 /* 767 * Causes the nodes corresponding to created or deleted datalinks to 768 * be created or deleted. 769 */ 770 int 771 dladm_sync(void) 772 { 773 di_devlink_handle_t hdl; 774 775 if ((hdl = di_devlink_init(DLD_DRIVER_NAME, DI_MAKE_LINK)) == NULL) 776 return (-1); 777 778 if (di_devlink_fini(&hdl) < 0) 779 return (-1); 780 781 return (0); 782 } 783 784 const char * 785 dladm_diag(dladm_diag_t diag) { 786 switch (diag) { 787 case DLADM_DIAG_INVALID_LINKNAME: 788 return (gettext("invalid datalink name")); 789 case DLADM_DIAG_INVALID_INTFNAME: 790 return (gettext("invalid interface name")); 791 case DLADM_DIAG_CORRUPT_REPOSITORY: 792 return (gettext("configuration repository corrupt")); 793 case DLADM_DIAG_REPOSITORY_OPENFAIL: 794 return (gettext("configuration repository open failed")); 795 case DLADM_DIAG_REPOSITORY_WRITEFAIL: 796 return (gettext("write to configuration repository failed")); 797 case DLADM_DIAG_REPOSITORY_CLOSEFAIL: 798 return (gettext("configuration repository close failed")); 799 case DLADM_DIAG_DEVICE_OPENFAIL: 800 return (gettext("dld device open fail")); 801 default: 802 return (gettext("unknown diagnostic")); 803 } 804 } 805