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