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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <unistd.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <assert.h> 31 #include <ctype.h> 32 #include <strings.h> 33 #include <sys/stat.h> 34 #include <sys/dld.h> 35 #include <sys/vlan.h> 36 #include <zone.h> 37 #include <librcm.h> 38 #include <libdlpi.h> 39 #include <libdevinfo.h> 40 #include <libdlaggr.h> 41 #include <libdlvlan.h> 42 #include <libdllink.h> 43 #include <libdlmgmt.h> 44 #include <libdladm_impl.h> 45 #include <libinetutil.h> 46 47 /* 48 * Return the attributes of the specified datalink from the DLD driver. 49 */ 50 static dladm_status_t 51 i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap) 52 { 53 dld_ioc_attr_t dia; 54 55 dia.dia_linkid = linkid; 56 57 if (ioctl(fd, DLDIOC_ATTR, &dia) < 0) 58 return (dladm_errno2status(errno)); 59 60 dap->da_max_sdu = dia.dia_max_sdu; 61 62 return (DLADM_STATUS_OK); 63 } 64 65 static dladm_status_t 66 dladm_usagelog(dladm_logtype_t type, dld_ioc_usagelog_t *log_info) 67 { 68 int fd; 69 70 fd = open(DLD_CONTROL_DEV, O_RDWR); 71 if (fd < 0) 72 return (DLADM_STATUS_IOERR); 73 74 if (type == DLADM_LOGTYPE_FLOW) 75 log_info->ul_type = MAC_LOGTYPE_FLOW; 76 else 77 log_info->ul_type = MAC_LOGTYPE_LINK; 78 79 if (ioctl(fd, DLDIOC_USAGELOG, log_info) < 0) { 80 (void) close(fd); 81 return (DLADM_STATUS_IOERR); 82 } 83 (void) close(fd); 84 return (DLADM_STATUS_OK); 85 } 86 87 dladm_status_t 88 dladm_start_usagelog(dladm_logtype_t type, uint_t interval) 89 { 90 dld_ioc_usagelog_t log_info; 91 92 log_info.ul_onoff = B_TRUE; 93 log_info.ul_interval = interval; 94 95 return (dladm_usagelog(type, &log_info)); 96 } 97 98 dladm_status_t 99 dladm_stop_usagelog(dladm_logtype_t type) 100 { 101 dld_ioc_usagelog_t log_info; 102 103 log_info.ul_onoff = B_FALSE; 104 log_info.ul_interval = 0; 105 106 return (dladm_usagelog(type, &log_info)); 107 } 108 109 struct i_dladm_walk_arg { 110 dladm_walkcb_t *fn; 111 void *arg; 112 }; 113 114 static int 115 i_dladm_walk(datalink_id_t linkid, void *arg) 116 { 117 struct i_dladm_walk_arg *walk_arg = arg; 118 char link[MAXLINKNAMELEN]; 119 120 if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, 121 sizeof (link)) == DLADM_STATUS_OK) { 122 return (walk_arg->fn(link, walk_arg->arg)); 123 } 124 125 return (DLADM_WALK_CONTINUE); 126 } 127 128 /* 129 * Walk all datalinks. 130 */ 131 dladm_status_t 132 dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class, 133 datalink_media_t dmedia, uint32_t flags) 134 { 135 struct i_dladm_walk_arg walk_arg; 136 137 walk_arg.fn = fn; 138 walk_arg.arg = arg; 139 return (dladm_walk_datalink_id(i_dladm_walk, &walk_arg, 140 class, dmedia, flags)); 141 } 142 143 #define MAXGRPPERLINK 64 144 145 int 146 dladm_walk_hwgrp(datalink_id_t linkid, void *arg, 147 boolean_t (*fn)(void *, dladm_hwgrp_attr_t *)) 148 { 149 int fd, bufsize, ret; 150 int nhwgrp = MAXGRPPERLINK; 151 dld_ioc_hwgrpget_t *iomp = NULL; 152 153 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 154 return (-1); 155 156 bufsize = sizeof (dld_ioc_hwgrpget_t) + 157 nhwgrp * sizeof (dld_hwgrpinfo_t); 158 159 if ((iomp = (dld_ioc_hwgrpget_t *)calloc(1, bufsize)) == NULL) 160 return (-1); 161 162 iomp->dih_size = nhwgrp * sizeof (dld_hwgrpinfo_t); 163 iomp->dih_linkid = linkid; 164 165 ret = ioctl(fd, DLDIOC_GETHWGRP, iomp); 166 if (ret == 0) { 167 int i; 168 dld_hwgrpinfo_t *dhip; 169 dladm_hwgrp_attr_t attr; 170 171 dhip = (dld_hwgrpinfo_t *)(iomp + 1); 172 for (i = 0; i < iomp->dih_n_groups; i++) { 173 bzero(&attr, sizeof (attr)); 174 175 (void) strlcpy(attr.hg_link_name, 176 dhip->dhi_link_name, sizeof (attr.hg_link_name)); 177 attr.hg_grp_num = dhip->dhi_grp_num; 178 attr.hg_grp_type = dhip->dhi_grp_type; 179 attr.hg_n_rings = dhip->dhi_n_rings; 180 attr.hg_n_clnts = dhip->dhi_n_clnts; 181 (void) strlcpy(attr.hg_client_names, 182 dhip->dhi_clnts, sizeof (attr.hg_client_names)); 183 184 if (!(*fn)(arg, &attr)) 185 break; 186 dhip++; 187 } 188 } 189 free(iomp); 190 (void) close(fd); 191 return (ret); 192 } 193 194 /* 195 * Invoke the specified callback for each MAC address entry defined on 196 * the specified device. 197 */ 198 int 199 dladm_walk_macaddr(datalink_id_t linkid, void *arg, 200 boolean_t (*fn)(void *, dladm_macaddr_attr_t *)) 201 { 202 int fd, bufsize, ret; 203 int nmacaddr = 1024; 204 dld_ioc_macaddrget_t *iomp = NULL; 205 206 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 207 return (-1); 208 209 bufsize = sizeof (dld_ioc_macaddrget_t) + 210 nmacaddr * sizeof (dld_macaddrinfo_t); 211 212 if ((iomp = (dld_ioc_macaddrget_t *)calloc(1, bufsize)) == NULL) 213 return (-1); 214 215 iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); 216 iomp->dig_linkid = linkid; 217 218 ret = ioctl(fd, DLDIOC_MACADDRGET, iomp); 219 if (ret == 0) { 220 int i; 221 dld_macaddrinfo_t *dmip; 222 dladm_macaddr_attr_t attr; 223 224 dmip = (dld_macaddrinfo_t *)(iomp + 1); 225 for (i = 0; i < iomp->dig_count; i++) { 226 bzero(&attr, sizeof (attr)); 227 228 attr.ma_slot = dmip->dmi_slot; 229 attr.ma_flags = 0; 230 if (dmip->dmi_flags & DLDIOCMACADDR_USED) 231 attr.ma_flags |= DLADM_MACADDR_USED; 232 bcopy(dmip->dmi_addr, attr.ma_addr, 233 dmip->dmi_addrlen); 234 attr.ma_addrlen = dmip->dmi_addrlen; 235 (void) strlcpy(attr.ma_client_name, 236 dmip->dmi_client_name, MAXNAMELEN); 237 attr.ma_client_linkid = dmip->dma_client_linkid; 238 239 if (!(*fn)(arg, &attr)) 240 break; 241 dmip++; 242 } 243 } 244 free(iomp); 245 (void) close(fd); 246 return (ret); 247 } 248 249 /* 250 * These routines are used by administration tools such as dladm(1M) to 251 * iterate through the list of MAC interfaces 252 */ 253 254 typedef struct dladm_mac_dev { 255 char dm_name[MAXNAMELEN]; 256 struct dladm_mac_dev *dm_next; 257 } dladm_mac_dev_t; 258 259 typedef struct macadm_walk { 260 dladm_mac_dev_t *dmd_dev_list; 261 } dladm_mac_walk_t; 262 263 /* 264 * Local callback invoked for each DDI_NT_NET node. 265 */ 266 /* ARGSUSED */ 267 static int 268 i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg) 269 { 270 dladm_mac_walk_t *dmwp = arg; 271 dladm_mac_dev_t *dmdp = dmwp->dmd_dev_list; 272 dladm_mac_dev_t **last_dmdp = &dmwp->dmd_dev_list; 273 char mac[MAXNAMELEN]; 274 275 (void) snprintf(mac, MAXNAMELEN, "%s%d", 276 di_driver_name(node), di_instance(node)); 277 278 /* 279 * Skip aggregations. 280 */ 281 if (strcmp("aggr", di_driver_name(node)) == 0) 282 return (DI_WALK_CONTINUE); 283 284 /* 285 * Skip softmacs. 286 */ 287 if (strcmp("softmac", di_driver_name(node)) == 0) 288 return (DI_WALK_CONTINUE); 289 290 while (dmdp) { 291 /* 292 * Skip duplicates. 293 */ 294 if (strcmp(dmdp->dm_name, mac) == 0) 295 return (DI_WALK_CONTINUE); 296 297 last_dmdp = &dmdp->dm_next; 298 dmdp = dmdp->dm_next; 299 } 300 301 if ((dmdp = malloc(sizeof (*dmdp))) == NULL) 302 return (DI_WALK_CONTINUE); 303 304 (void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN); 305 dmdp->dm_next = NULL; 306 *last_dmdp = dmdp; 307 308 return (DI_WALK_CONTINUE); 309 } 310 311 /* 312 * Invoke the specified callback for each DDI_NT_NET node. 313 */ 314 dladm_status_t 315 dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg) 316 { 317 di_node_t root; 318 dladm_mac_walk_t dmw; 319 dladm_mac_dev_t *dmdp, *next; 320 boolean_t done = B_FALSE; 321 322 if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 323 return (dladm_errno2status(errno)); 324 325 dmw.dmd_dev_list = NULL; 326 327 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw, 328 i_dladm_mac_walk); 329 330 di_fini(root); 331 332 dmdp = dmw.dmd_dev_list; 333 for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) { 334 next = dmdp->dm_next; 335 if (!done && 336 ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) { 337 done = B_TRUE; 338 } 339 free(dmdp); 340 } 341 342 return (DLADM_STATUS_OK); 343 } 344 345 /* 346 * Get the current attributes of the specified datalink. 347 */ 348 dladm_status_t 349 dladm_info(datalink_id_t linkid, dladm_attr_t *dap) 350 { 351 int fd; 352 dladm_status_t status; 353 354 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 355 return (dladm_errno2status(errno)); 356 357 status = i_dladm_info(fd, linkid, dap); 358 359 (void) close(fd); 360 return (status); 361 } 362 363 const char * 364 dladm_linkstate2str(link_state_t state, char *buf) 365 { 366 const char *s; 367 368 switch (state) { 369 case LINK_STATE_UP: 370 s = "up"; 371 break; 372 case LINK_STATE_DOWN: 373 s = "down"; 374 break; 375 default: 376 s = "unknown"; 377 break; 378 } 379 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 380 return (buf); 381 } 382 383 const char * 384 dladm_linkduplex2str(link_duplex_t duplex, char *buf) 385 { 386 const char *s; 387 388 switch (duplex) { 389 case LINK_DUPLEX_FULL: 390 s = "full"; 391 break; 392 case LINK_DUPLEX_HALF: 393 s = "half"; 394 break; 395 default: 396 s = "unknown"; 397 break; 398 } 399 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 400 return (buf); 401 } 402 403 /* 404 * Set zoneid of a given link. Note that this function takes a link name 405 * argument instead of a linkid, because a data-link (and its linkid) could 406 * be created implicitly as the result of this function. 407 */ 408 dladm_status_t 409 dladm_setzid(const char *dlname, char *zone_name) 410 { 411 datalink_id_t linkid; 412 dladm_status_t status = DLADM_STATUS_OK; 413 414 /* If the link does not exist, it is a ppa-hacked vlan. */ 415 status = dladm_name2info(dlname, &linkid, NULL, NULL, NULL); 416 if (status != DLADM_STATUS_OK) 417 return (status); 418 419 status = dladm_set_linkprop(linkid, "zone", &zone_name, 1, 420 DLADM_OPT_ACTIVE); 421 422 return (status); 423 } 424 425 /* 426 * Case 1: rename an existing link1 to a link2 that does not exist. 427 * Result: <linkid1, link2> 428 */ 429 static dladm_status_t 430 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1, 431 const char *link2, uint32_t flags) 432 { 433 dld_ioc_rename_t dir; 434 dladm_conf_t conf; 435 dladm_status_t status = DLADM_STATUS_OK; 436 int fd; 437 438 /* 439 * Link is currently available. Check to see whether anything is 440 * holding this link to prevent a rename operation. 441 */ 442 if (flags & DLADM_OPT_ACTIVE) { 443 dir.dir_linkid1 = linkid1; 444 dir.dir_linkid2 = DATALINK_INVALID_LINKID; 445 (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); 446 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 447 return (dladm_errno2status(errno)); 448 449 if (ioctl(fd, DLDIOC_RENAME, &dir) < 0) { 450 status = dladm_errno2status(errno); 451 (void) close(fd); 452 return (status); 453 } 454 } 455 456 status = dladm_remap_datalink_id(linkid1, link2); 457 if (status != DLADM_STATUS_OK) 458 goto done; 459 460 /* 461 * Flush the current mapping to persistent configuration. 462 */ 463 if ((flags & DLADM_OPT_PERSIST) && 464 (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) || 465 ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) { 466 (void) dladm_remap_datalink_id(linkid1, link1); 467 } 468 done: 469 if (flags & DLADM_OPT_ACTIVE) { 470 if (status != DLADM_STATUS_OK) { 471 (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); 472 (void) ioctl(fd, DLDIOC_RENAME, &dir); 473 } 474 (void) close(fd); 475 } 476 return (status); 477 } 478 479 typedef struct link_hold_arg_s { 480 datalink_id_t linkid; 481 datalink_id_t holder; 482 uint32_t flags; 483 } link_hold_arg_t; 484 485 static int 486 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg) 487 { 488 link_hold_arg_t *hold_arg = arg; 489 dladm_aggr_grp_attr_t ginfo; 490 dladm_status_t status; 491 int i; 492 493 status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags); 494 if (status != DLADM_STATUS_OK) 495 return (DLADM_WALK_CONTINUE); 496 497 for (i = 0; i < ginfo.lg_nports; i++) { 498 if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) { 499 hold_arg->holder = aggrid; 500 return (DLADM_WALK_TERMINATE); 501 } 502 } 503 return (DLADM_WALK_CONTINUE); 504 } 505 506 static int 507 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg) 508 { 509 link_hold_arg_t *hold_arg = arg; 510 dladm_vlan_attr_t vinfo; 511 dladm_status_t status; 512 513 status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags); 514 if (status != DLADM_STATUS_OK) 515 return (DLADM_WALK_CONTINUE); 516 517 if (vinfo.dv_linkid == hold_arg->linkid) { 518 hold_arg->holder = vlanid; 519 return (DLADM_WALK_TERMINATE); 520 } 521 return (DLADM_WALK_CONTINUE); 522 } 523 524 /* 525 * Case 2: rename an available physical link link1 to a REMOVED physical link 526 * link2. As a result, link1 directly inherits all datalinks configured 527 * over link2 (linkid2). 528 * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname, 529 * link2_other_attr> 530 */ 531 static dladm_status_t 532 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2) 533 { 534 rcm_handle_t *rcm_hdl = NULL; 535 nvlist_t *nvl = NULL; 536 link_hold_arg_t arg; 537 dld_ioc_rename_t dir; 538 int fd; 539 dladm_conf_t conf1, conf2; 540 char devname[MAXLINKNAMELEN]; 541 uint64_t phymaj, phyinst; 542 dladm_status_t status = DLADM_STATUS_OK; 543 544 /* 545 * First check if linkid1 is associated with any persistent 546 * aggregations or VLANs. If yes, return BUSY. 547 */ 548 arg.linkid = linkid1; 549 arg.holder = DATALINK_INVALID_LINKID; 550 arg.flags = DLADM_OPT_PERSIST; 551 (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg, 552 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 553 if (arg.holder != DATALINK_INVALID_LINKID) 554 return (DLADM_STATUS_LINKBUSY); 555 556 arg.flags = DLADM_OPT_PERSIST; 557 (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg, 558 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 559 if (arg.holder != DATALINK_INVALID_LINKID) 560 return (DLADM_STATUS_LINKBUSY); 561 562 /* 563 * Send DLDIOC_RENAME to request to rename link1's linkid to 564 * be linkid2. This will check whether link1 is used by any 565 * aggregations or VLANs, or is held by any application. If yes, 566 * return failure. 567 */ 568 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 569 return (dladm_errno2status(errno)); 570 571 dir.dir_linkid1 = linkid1; 572 dir.dir_linkid2 = linkid2; 573 if (ioctl(fd, DLDIOC_RENAME, &dir) < 0) 574 status = dladm_errno2status(errno); 575 576 if (status != DLADM_STATUS_OK) { 577 (void) close(fd); 578 return (status); 579 } 580 581 /* 582 * Now change the phymaj, phyinst and devname associated with linkid1 583 * to be associated with linkid2. Before doing that, the old active 584 * linkprop of linkid1 should be deleted. 585 */ 586 (void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE); 587 588 if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) || 589 ((status = dladm_get_conf_field(conf1, FDEVNAME, devname, 590 MAXLINKNAMELEN)) != DLADM_STATUS_OK) || 591 ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj, 592 sizeof (uint64_t))) != DLADM_STATUS_OK) || 593 ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst, 594 sizeof (uint64_t))) != DLADM_STATUS_OK) || 595 ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) { 596 dir.dir_linkid1 = linkid2; 597 dir.dir_linkid2 = linkid1; 598 (void) dladm_init_linkprop(linkid1, B_FALSE); 599 (void) ioctl(fd, DLDIOC_RENAME, &dir); 600 (void) close(fd); 601 return (status); 602 } 603 (void) close(fd); 604 605 dladm_destroy_conf(conf1); 606 (void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname); 607 (void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj); 608 (void) dladm_set_conf_field(conf2, FPHYINST, 609 DLADM_TYPE_UINT64, &phyinst); 610 (void) dladm_write_conf(conf2); 611 dladm_destroy_conf(conf2); 612 613 /* 614 * Delete link1 and mark link2 up. 615 */ 616 (void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE | 617 DLADM_OPT_PERSIST); 618 (void) dladm_remove_conf(linkid1); 619 (void) dladm_up_datalink_id(linkid2); 620 621 /* 622 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be 623 * consumed by the RCM framework to restore all the datalink and 624 * IP configuration. 625 */ 626 status = DLADM_STATUS_FAILED; 627 if ((nvlist_alloc(&nvl, 0, 0) != 0) || 628 (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) { 629 goto done; 630 } 631 632 if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS) 633 goto done; 634 635 if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) == 636 RCM_SUCCESS) { 637 status = DLADM_STATUS_OK; 638 } 639 640 done: 641 if (rcm_hdl != NULL) 642 (void) rcm_free_handle(rcm_hdl); 643 if (nvl != NULL) 644 nvlist_free(nvl); 645 return (status); 646 } 647 648 /* 649 * case 3: rename a non-existent link to a REMOVED physical link. 650 * Set the removed physical link's device name to link1, so that 651 * when link1 attaches, it inherits all the link configuration of 652 * the removed physical link. 653 */ 654 static dladm_status_t 655 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2) 656 { 657 dladm_conf_t conf; 658 dladm_status_t status; 659 660 if (!dladm_valid_linkname(link1)) 661 return (DLADM_STATUS_LINKINVAL); 662 663 status = dladm_read_conf(linkid2, &conf); 664 if (status != DLADM_STATUS_OK) 665 goto done; 666 667 if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR, 668 link1)) == DLADM_STATUS_OK) { 669 status = dladm_write_conf(conf); 670 } 671 672 dladm_destroy_conf(conf); 673 674 done: 675 return (status); 676 } 677 678 dladm_status_t 679 dladm_rename_link(const char *link1, const char *link2) 680 { 681 datalink_id_t linkid1 = DATALINK_INVALID_LINKID; 682 datalink_id_t linkid2 = DATALINK_INVALID_LINKID; 683 uint32_t flags1, flags2; 684 datalink_class_t class1, class2; 685 uint32_t media1, media2; 686 boolean_t remphy2 = B_FALSE; 687 dladm_status_t status; 688 689 (void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1); 690 if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) == 691 DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && 692 (flags2 == DLADM_OPT_PERSIST)) { 693 /* 694 * see whether link2 is a removed physical link. 695 */ 696 remphy2 = B_TRUE; 697 } 698 699 if (linkid1 != DATALINK_INVALID_LINKID) { 700 if (linkid2 == DATALINK_INVALID_LINKID) { 701 /* 702 * case 1: rename an existing link to a link that 703 * does not exist. 704 */ 705 status = i_dladm_rename_link_c1(linkid1, link1, link2, 706 flags1); 707 } else if (remphy2) { 708 /* 709 * case 2: rename an available link to a REMOVED 710 * physical link. Return failure if link1 is not 711 * an active physical link. 712 */ 713 if ((class1 != class2) || (media1 != media2) || 714 !(flags1 & DLADM_OPT_ACTIVE)) { 715 status = DLADM_STATUS_BADARG; 716 } else { 717 status = i_dladm_rename_link_c2(linkid1, 718 linkid2); 719 } 720 } else { 721 status = DLADM_STATUS_EXIST; 722 } 723 } else if (remphy2) { 724 status = i_dladm_rename_link_c3(link1, linkid2); 725 } else { 726 status = DLADM_STATUS_NOTFOUND; 727 } 728 return (status); 729 } 730 731 typedef struct consumer_del_phys_arg_s { 732 datalink_id_t linkid; 733 } consumer_del_phys_arg_t; 734 735 static int 736 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg) 737 { 738 consumer_del_phys_arg_t *del_arg = arg; 739 dladm_vlan_attr_t vinfo; 740 dladm_status_t status; 741 742 status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST); 743 if (status != DLADM_STATUS_OK) 744 return (DLADM_WALK_CONTINUE); 745 746 if (vinfo.dv_linkid == del_arg->linkid) 747 (void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST); 748 return (DLADM_WALK_CONTINUE); 749 } 750 751 static int 752 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg) 753 { 754 consumer_del_phys_arg_t *del_arg = arg; 755 dladm_aggr_grp_attr_t ginfo; 756 dladm_status_t status; 757 dladm_aggr_port_attr_db_t port[1]; 758 int i; 759 760 status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST); 761 if (status != DLADM_STATUS_OK) 762 return (DLADM_WALK_CONTINUE); 763 764 for (i = 0; i < ginfo.lg_nports; i++) 765 if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid) 766 break; 767 768 if (i != ginfo.lg_nports) { 769 if (ginfo.lg_nports == 1 && i == 0) { 770 consumer_del_phys_arg_t aggr_del_arg; 771 772 /* 773 * First delete all the VLANs on this aggregation, then 774 * delete the aggregation itself. 775 */ 776 aggr_del_arg.linkid = aggrid; 777 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, 778 &aggr_del_arg, DATALINK_CLASS_VLAN, 779 DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 780 (void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST); 781 } else { 782 port[0].lp_linkid = del_arg->linkid; 783 (void) dladm_aggr_remove(aggrid, 1, port, 784 DLADM_OPT_PERSIST); 785 } 786 } 787 return (DLADM_WALK_CONTINUE); 788 } 789 790 typedef struct del_phys_arg_s { 791 dladm_status_t rval; 792 } del_phys_arg_t; 793 794 static int 795 i_dladm_phys_delete(datalink_id_t linkid, void *arg) 796 { 797 uint32_t flags; 798 datalink_class_t class; 799 uint32_t media; 800 dladm_status_t status = DLADM_STATUS_OK; 801 del_phys_arg_t *del_phys_arg = arg; 802 consumer_del_phys_arg_t del_arg; 803 804 if ((status = dladm_datalink_id2info(linkid, &flags, &class, 805 &media, NULL, 0)) != DLADM_STATUS_OK) { 806 goto done; 807 } 808 809 /* 810 * see whether this link is a removed physical link. 811 */ 812 if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) || 813 (flags & DLADM_OPT_ACTIVE)) { 814 status = DLADM_STATUS_BADARG; 815 goto done; 816 } 817 818 if (media == DL_ETHER) { 819 del_arg.linkid = linkid; 820 (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg, 821 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 822 DLADM_OPT_PERSIST); 823 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg, 824 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 825 DLADM_OPT_PERSIST); 826 } 827 828 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); 829 (void) dladm_remove_conf(linkid); 830 831 done: 832 del_phys_arg->rval = status; 833 return (DLADM_WALK_CONTINUE); 834 } 835 836 dladm_status_t 837 dladm_phys_delete(datalink_id_t linkid) 838 { 839 del_phys_arg_t arg = {DLADM_STATUS_OK}; 840 841 if (linkid == DATALINK_ALL_LINKID) { 842 (void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg, 843 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, 844 DLADM_OPT_PERSIST); 845 return (DLADM_STATUS_OK); 846 } else { 847 (void) i_dladm_phys_delete(linkid, &arg); 848 return (arg.rval); 849 } 850 } 851 852 dladm_status_t 853 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags) 854 { 855 dladm_status_t status; 856 857 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 858 859 switch (flags) { 860 case DLADM_OPT_PERSIST: { 861 dladm_conf_t conf; 862 863 status = dladm_read_conf(linkid, &conf); 864 if (status != DLADM_STATUS_OK) 865 return (status); 866 867 status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev, 868 MAXLINKNAMELEN); 869 dladm_destroy_conf(conf); 870 return (status); 871 } 872 case DLADM_OPT_ACTIVE: { 873 dld_ioc_phys_attr_t dip; 874 int fd; 875 876 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 877 return (dladm_errno2status(errno)); 878 879 dip.dip_linkid = linkid; 880 if (ioctl(fd, DLDIOC_PHYS_ATTR, &dip) < 0) { 881 status = dladm_errno2status(errno); 882 (void) close(fd); 883 return (status); 884 } 885 (void) close(fd); 886 dpap->dp_novanity = dip.dip_novanity; 887 (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN); 888 return (DLADM_STATUS_OK); 889 } 890 default: 891 return (DLADM_STATUS_BADARG); 892 } 893 } 894 895 typedef struct i_walk_dev_state_s { 896 const char *devname; 897 datalink_id_t linkid; 898 boolean_t found; 899 } i_walk_dev_state_t; 900 901 int 902 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg) 903 { 904 dladm_phys_attr_t dpa; 905 dladm_status_t status; 906 i_walk_dev_state_t *statep = arg; 907 908 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 909 if ((status == DLADM_STATUS_OK) && 910 (strcmp(statep->devname, dpa.dp_dev) == 0)) { 911 statep->found = B_TRUE; 912 statep->linkid = linkid; 913 return (DLADM_WALK_TERMINATE); 914 } 915 return (DLADM_WALK_CONTINUE); 916 } 917 918 /* 919 * Get the linkid from the physical device name. 920 */ 921 dladm_status_t 922 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp) 923 { 924 i_walk_dev_state_t state; 925 926 state.found = B_FALSE; 927 state.devname = devname; 928 929 (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state, 930 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 931 if (state.found == B_TRUE) { 932 *linkidp = state.linkid; 933 return (DLADM_STATUS_OK); 934 } else { 935 return (dladm_errno2status(ENOENT)); 936 } 937 } 938 939 static int 940 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen) 941 { 942 char *cp, *tp; 943 int len; 944 945 /* 946 * device name length must not be 0, and it must end with digit. 947 */ 948 if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1])) 949 return (EINVAL); 950 951 (void) strlcpy(driver, devname, maxlen); 952 cp = (char *)&driver[len - 1]; 953 954 for (tp = cp; isdigit(*tp); tp--) { 955 if (tp <= driver) 956 return (EINVAL); 957 } 958 959 *ppa = atoi(tp + 1); 960 *(tp + 1) = '\0'; 961 return (0); 962 } 963 964 dladm_status_t 965 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len) 966 { 967 char devname[MAXLINKNAMELEN]; 968 uint16_t vid = VLAN_ID_NONE; 969 datalink_class_t class; 970 dladm_status_t status; 971 972 status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0); 973 if (status != DLADM_STATUS_OK) 974 goto done; 975 976 /* 977 * If this is a VLAN, we must first determine the class and linkid of 978 * the link the VLAN has been created over. 979 */ 980 if (class == DATALINK_CLASS_VLAN) { 981 dladm_vlan_attr_t dva; 982 983 status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE); 984 if (status != DLADM_STATUS_OK) 985 goto done; 986 linkid = dva.dv_linkid; 987 vid = dva.dv_vid; 988 989 if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL, 990 NULL, 0)) != DLADM_STATUS_OK) { 991 goto done; 992 } 993 } 994 995 switch (class) { 996 case DATALINK_CLASS_AGGR: { 997 dladm_aggr_grp_attr_t dga; 998 999 status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE); 1000 if (status != DLADM_STATUS_OK) 1001 goto done; 1002 1003 if (dga.lg_key == 0) { 1004 /* 1005 * If the key was not specified when the aggregation 1006 * is created, we cannot guess its /dev node name. 1007 */ 1008 status = DLADM_STATUS_BADARG; 1009 goto done; 1010 } 1011 (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key); 1012 break; 1013 } 1014 case DATALINK_CLASS_PHYS: { 1015 dladm_phys_attr_t dpa; 1016 1017 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 1018 if (status != DLADM_STATUS_OK) 1019 goto done; 1020 1021 (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN); 1022 break; 1023 } 1024 default: 1025 status = DLADM_STATUS_BADARG; 1026 goto done; 1027 } 1028 1029 if (vid != VLAN_ID_NONE) { 1030 char drv[MAXNAMELEN]; 1031 uint_t ppa; 1032 1033 if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) { 1034 status = DLADM_STATUS_BADARG; 1035 goto done; 1036 } 1037 if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len) 1038 status = DLADM_STATUS_TOOSMALL; 1039 } else { 1040 if (strlcpy(dev, devname, len) >= len) 1041 status = DLADM_STATUS_TOOSMALL; 1042 } 1043 1044 done: 1045 return (status); 1046 } 1047 1048 dladm_status_t 1049 dladm_parselink(const char *dev, char *provider, uint_t *ppa) 1050 { 1051 ifspec_t ifsp; 1052 1053 if (dev == NULL || !ifparse_ifspec(dev, &ifsp)) 1054 return (DLADM_STATUS_LINKINVAL); 1055 1056 if (provider != NULL) 1057 (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX); 1058 1059 if (ppa != NULL) 1060 *ppa = ifsp.ifsp_ppa; 1061 1062 return (DLADM_STATUS_OK); 1063 } 1064