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 2009 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 * Case 1: rename an existing link1 to a link2 that does not exist. 383 * Result: <linkid1, link2> 384 */ 385 static dladm_status_t 386 i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1, 387 const char *link1, const char *link2, uint32_t flags) 388 { 389 dld_ioc_rename_t dir; 390 dladm_status_t status = DLADM_STATUS_OK; 391 392 /* 393 * Link is currently available. Check to see whether anything is 394 * holding this link to prevent a rename operation. 395 */ 396 if (flags & DLADM_OPT_ACTIVE) { 397 dir.dir_linkid1 = linkid1; 398 dir.dir_linkid2 = DATALINK_INVALID_LINKID; 399 (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); 400 401 if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) { 402 status = dladm_errno2status(errno); 403 return (status); 404 } 405 } 406 407 status = dladm_remap_datalink_id(handle, linkid1, link2); 408 if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) { 409 (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); 410 (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir); 411 } 412 return (status); 413 } 414 415 typedef struct link_hold_arg_s { 416 datalink_id_t linkid; 417 datalink_id_t holder; 418 uint32_t flags; 419 } link_hold_arg_t; 420 421 static int 422 i_dladm_aggr_link_hold(dladm_handle_t handle, datalink_id_t aggrid, void *arg) 423 { 424 link_hold_arg_t *hold_arg = arg; 425 dladm_aggr_grp_attr_t ginfo; 426 dladm_status_t status; 427 int i; 428 429 status = dladm_aggr_info(handle, aggrid, &ginfo, hold_arg->flags); 430 if (status != DLADM_STATUS_OK) 431 return (DLADM_WALK_CONTINUE); 432 433 for (i = 0; i < ginfo.lg_nports; i++) { 434 if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) { 435 hold_arg->holder = aggrid; 436 return (DLADM_WALK_TERMINATE); 437 } 438 } 439 return (DLADM_WALK_CONTINUE); 440 } 441 442 static int 443 i_dladm_vlan_link_hold(dladm_handle_t handle, datalink_id_t vlanid, void *arg) 444 { 445 link_hold_arg_t *hold_arg = arg; 446 dladm_vlan_attr_t vinfo; 447 dladm_status_t status; 448 449 status = dladm_vlan_info(handle, vlanid, &vinfo, hold_arg->flags); 450 if (status != DLADM_STATUS_OK) 451 return (DLADM_WALK_CONTINUE); 452 453 if (vinfo.dv_linkid == hold_arg->linkid) { 454 hold_arg->holder = vlanid; 455 return (DLADM_WALK_TERMINATE); 456 } 457 return (DLADM_WALK_CONTINUE); 458 } 459 460 /* 461 * Case 2: rename an available physical link link1 to a REMOVED physical link 462 * link2. As a result, link1 directly inherits all datalinks configured 463 * over link2 (linkid2). 464 * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname, 465 * link2_other_attr> 466 */ 467 static dladm_status_t 468 i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1, 469 datalink_id_t linkid2) 470 { 471 rcm_handle_t *rcm_hdl = NULL; 472 nvlist_t *nvl = NULL; 473 link_hold_arg_t arg; 474 dld_ioc_rename_t dir; 475 dladm_conf_t conf1, conf2; 476 char devname[MAXLINKNAMELEN]; 477 uint64_t phymaj, phyinst; 478 dladm_status_t status = DLADM_STATUS_OK; 479 480 /* 481 * First check if linkid1 is associated with any persistent 482 * aggregations or VLANs. If yes, return BUSY. 483 */ 484 arg.linkid = linkid1; 485 arg.holder = DATALINK_INVALID_LINKID; 486 arg.flags = DLADM_OPT_PERSIST; 487 (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, handle, &arg, 488 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 489 if (arg.holder != DATALINK_INVALID_LINKID) 490 return (DLADM_STATUS_LINKBUSY); 491 492 arg.flags = DLADM_OPT_PERSIST; 493 (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, handle, &arg, 494 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 495 if (arg.holder != DATALINK_INVALID_LINKID) 496 return (DLADM_STATUS_LINKBUSY); 497 498 /* 499 * Send DLDIOC_RENAME to request to rename link1's linkid to 500 * be linkid2. This will check whether link1 is used by any 501 * aggregations or VLANs, or is held by any application. If yes, 502 * return failure. 503 */ 504 dir.dir_linkid1 = linkid1; 505 dir.dir_linkid2 = linkid2; 506 if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) 507 status = dladm_errno2status(errno); 508 509 if (status != DLADM_STATUS_OK) { 510 return (status); 511 } 512 513 /* 514 * Now change the phymaj, phyinst and devname associated with linkid1 515 * to be associated with linkid2. Before doing that, the old active 516 * linkprop of linkid1 should be deleted. 517 */ 518 (void) dladm_set_linkprop(handle, linkid1, NULL, NULL, 0, 519 DLADM_OPT_ACTIVE); 520 521 if (((status = dladm_read_conf(handle, linkid1, &conf1)) != 522 DLADM_STATUS_OK) || 523 ((status = dladm_get_conf_field(handle, conf1, FDEVNAME, devname, 524 MAXLINKNAMELEN)) != DLADM_STATUS_OK) || 525 ((status = dladm_get_conf_field(handle, conf1, FPHYMAJ, &phymaj, 526 sizeof (uint64_t))) != DLADM_STATUS_OK) || 527 ((status = dladm_get_conf_field(handle, conf1, FPHYINST, &phyinst, 528 sizeof (uint64_t))) != DLADM_STATUS_OK) || 529 ((status = dladm_read_conf(handle, linkid2, &conf2)) != 530 DLADM_STATUS_OK)) { 531 dir.dir_linkid1 = linkid2; 532 dir.dir_linkid2 = linkid1; 533 (void) dladm_init_linkprop(handle, linkid1, B_FALSE); 534 (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir); 535 return (status); 536 } 537 538 dladm_destroy_conf(handle, conf1); 539 (void) dladm_set_conf_field(handle, conf2, FDEVNAME, DLADM_TYPE_STR, 540 devname); 541 (void) dladm_set_conf_field(handle, conf2, FPHYMAJ, DLADM_TYPE_UINT64, 542 &phymaj); 543 (void) dladm_set_conf_field(handle, conf2, FPHYINST, 544 DLADM_TYPE_UINT64, &phyinst); 545 (void) dladm_write_conf(handle, conf2); 546 dladm_destroy_conf(handle, conf2); 547 548 /* 549 * Delete link1 and mark link2 up. 550 */ 551 (void) dladm_remove_conf(handle, linkid1); 552 (void) dladm_destroy_datalink_id(handle, linkid1, DLADM_OPT_ACTIVE | 553 DLADM_OPT_PERSIST); 554 (void) dladm_up_datalink_id(handle, linkid2); 555 556 /* 557 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be 558 * consumed by the RCM framework to restore all the datalink and 559 * IP configuration. 560 */ 561 status = DLADM_STATUS_FAILED; 562 if ((nvlist_alloc(&nvl, 0, 0) != 0) || 563 (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) { 564 goto done; 565 } 566 567 if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS) 568 goto done; 569 570 if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) == 571 RCM_SUCCESS) { 572 status = DLADM_STATUS_OK; 573 } 574 575 done: 576 if (rcm_hdl != NULL) 577 (void) rcm_free_handle(rcm_hdl); 578 if (nvl != NULL) 579 nvlist_free(nvl); 580 return (status); 581 } 582 583 /* 584 * case 3: rename a non-existent link to a REMOVED physical link. 585 * Set the removed physical link's device name to link1, so that 586 * when link1 attaches, it inherits all the link configuration of 587 * the removed physical link. 588 */ 589 static dladm_status_t 590 i_dladm_rename_link_c3(dladm_handle_t handle, const char *link1, 591 datalink_id_t linkid2) 592 { 593 dladm_conf_t conf; 594 dladm_status_t status; 595 596 if (!dladm_valid_linkname(link1)) 597 return (DLADM_STATUS_LINKINVAL); 598 599 status = dladm_read_conf(handle, linkid2, &conf); 600 if (status != DLADM_STATUS_OK) 601 goto done; 602 603 if ((status = dladm_set_conf_field(handle, conf, FDEVNAME, 604 DLADM_TYPE_STR, link1)) == DLADM_STATUS_OK) { 605 status = dladm_write_conf(handle, conf); 606 } 607 608 dladm_destroy_conf(handle, conf); 609 610 done: 611 return (status); 612 } 613 614 dladm_status_t 615 dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2) 616 { 617 datalink_id_t linkid1 = DATALINK_INVALID_LINKID; 618 datalink_id_t linkid2 = DATALINK_INVALID_LINKID; 619 uint32_t flags1, flags2; 620 datalink_class_t class1, class2; 621 uint32_t media1, media2; 622 boolean_t remphy2 = B_FALSE; 623 dladm_status_t status; 624 625 (void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1, 626 &media1); 627 if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2, 628 &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && 629 (flags2 == DLADM_OPT_PERSIST)) { 630 /* 631 * see whether link2 is a removed physical link. 632 */ 633 remphy2 = B_TRUE; 634 } 635 636 if (linkid1 != DATALINK_INVALID_LINKID) { 637 if (linkid2 == DATALINK_INVALID_LINKID) { 638 /* 639 * case 1: rename an existing link to a link that 640 * does not exist. 641 */ 642 status = i_dladm_rename_link_c1(handle, linkid1, link1, 643 link2, flags1); 644 } else if (remphy2) { 645 /* 646 * case 2: rename an available link to a REMOVED 647 * physical link. Return failure if link1 is not 648 * an active physical link. 649 */ 650 if ((class1 != class2) || (media1 != media2) || 651 !(flags1 & DLADM_OPT_ACTIVE)) { 652 status = DLADM_STATUS_BADARG; 653 } else { 654 status = i_dladm_rename_link_c2(handle, linkid1, 655 linkid2); 656 } 657 } else { 658 status = DLADM_STATUS_EXIST; 659 } 660 } else if (remphy2) { 661 status = i_dladm_rename_link_c3(handle, link1, linkid2); 662 } else { 663 status = DLADM_STATUS_NOTFOUND; 664 } 665 return (status); 666 } 667 668 typedef struct consumer_del_phys_arg_s { 669 datalink_id_t linkid; 670 } consumer_del_phys_arg_t; 671 672 static int 673 i_dladm_vlan_link_del(dladm_handle_t handle, datalink_id_t vlanid, void *arg) 674 { 675 consumer_del_phys_arg_t *del_arg = arg; 676 dladm_vlan_attr_t vinfo; 677 dladm_status_t status; 678 679 status = dladm_vlan_info(handle, vlanid, &vinfo, DLADM_OPT_PERSIST); 680 if (status != DLADM_STATUS_OK) 681 return (DLADM_WALK_CONTINUE); 682 683 if (vinfo.dv_linkid == del_arg->linkid) 684 (void) dladm_vlan_delete(handle, vlanid, DLADM_OPT_PERSIST); 685 return (DLADM_WALK_CONTINUE); 686 } 687 688 static int 689 i_dladm_aggr_link_del(dladm_handle_t handle, datalink_id_t aggrid, void *arg) 690 { 691 consumer_del_phys_arg_t *del_arg = arg; 692 dladm_aggr_grp_attr_t ginfo; 693 dladm_status_t status; 694 dladm_aggr_port_attr_db_t port[1]; 695 int i; 696 697 status = dladm_aggr_info(handle, aggrid, &ginfo, DLADM_OPT_PERSIST); 698 if (status != DLADM_STATUS_OK) 699 return (DLADM_WALK_CONTINUE); 700 701 for (i = 0; i < ginfo.lg_nports; i++) 702 if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid) 703 break; 704 705 if (i != ginfo.lg_nports) { 706 if (ginfo.lg_nports == 1 && i == 0) { 707 consumer_del_phys_arg_t aggr_del_arg; 708 709 /* 710 * First delete all the VLANs on this aggregation, then 711 * delete the aggregation itself. 712 */ 713 aggr_del_arg.linkid = aggrid; 714 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, 715 handle, &aggr_del_arg, DATALINK_CLASS_VLAN, 716 DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 717 (void) dladm_aggr_delete(handle, aggrid, 718 DLADM_OPT_PERSIST); 719 } else { 720 port[0].lp_linkid = del_arg->linkid; 721 (void) dladm_aggr_remove(handle, aggrid, 1, port, 722 DLADM_OPT_PERSIST); 723 } 724 } 725 return (DLADM_WALK_CONTINUE); 726 } 727 728 typedef struct del_phys_arg_s { 729 dladm_status_t rval; 730 } del_phys_arg_t; 731 732 static int 733 i_dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid, void *arg) 734 { 735 uint32_t flags; 736 datalink_class_t class; 737 uint32_t media; 738 dladm_status_t status = DLADM_STATUS_OK; 739 del_phys_arg_t *del_phys_arg = arg; 740 consumer_del_phys_arg_t del_arg; 741 742 if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class, 743 &media, NULL, 0)) != DLADM_STATUS_OK) { 744 goto done; 745 } 746 747 /* 748 * see whether this link is a removed physical link. 749 */ 750 if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) || 751 (flags & DLADM_OPT_ACTIVE)) { 752 status = DLADM_STATUS_BADARG; 753 goto done; 754 } 755 756 if (media == DL_ETHER) { 757 del_arg.linkid = linkid; 758 (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, handle, 759 &del_arg, DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 760 DLADM_OPT_PERSIST); 761 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, handle, 762 &del_arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 763 DLADM_OPT_PERSIST); 764 } 765 766 (void) dladm_remove_conf(handle, linkid); 767 (void) dladm_destroy_datalink_id(handle, linkid, DLADM_OPT_PERSIST); 768 done: 769 del_phys_arg->rval = status; 770 return (DLADM_WALK_CONTINUE); 771 } 772 773 dladm_status_t 774 dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid) 775 { 776 del_phys_arg_t arg = {DLADM_STATUS_OK}; 777 778 if (linkid == DATALINK_ALL_LINKID) { 779 (void) dladm_walk_datalink_id(i_dladm_phys_delete, handle, &arg, 780 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, 781 DLADM_OPT_PERSIST); 782 return (DLADM_STATUS_OK); 783 } else { 784 (void) i_dladm_phys_delete(handle, linkid, &arg); 785 return (arg.rval); 786 } 787 } 788 789 dladm_status_t 790 dladm_phys_info(dladm_handle_t handle, datalink_id_t linkid, 791 dladm_phys_attr_t *dpap, uint32_t flags) 792 { 793 dladm_status_t status; 794 795 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 796 797 switch (flags) { 798 case DLADM_OPT_PERSIST: { 799 dladm_conf_t conf; 800 801 status = dladm_read_conf(handle, linkid, &conf); 802 if (status != DLADM_STATUS_OK) 803 return (status); 804 805 status = dladm_get_conf_field(handle, conf, FDEVNAME, 806 dpap->dp_dev, MAXLINKNAMELEN); 807 dladm_destroy_conf(handle, conf); 808 return (status); 809 } 810 case DLADM_OPT_ACTIVE: { 811 dld_ioc_phys_attr_t dip; 812 813 dip.dip_linkid = linkid; 814 if (ioctl(dladm_dld_fd(handle), DLDIOC_PHYS_ATTR, &dip) < 0) { 815 status = dladm_errno2status(errno); 816 return (status); 817 } 818 dpap->dp_novanity = dip.dip_novanity; 819 (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN); 820 return (DLADM_STATUS_OK); 821 } 822 default: 823 return (DLADM_STATUS_BADARG); 824 } 825 } 826 827 typedef struct i_walk_dev_state_s { 828 const char *devname; 829 datalink_id_t linkid; 830 boolean_t found; 831 } i_walk_dev_state_t; 832 833 int 834 i_dladm_walk_dev2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg) 835 { 836 dladm_phys_attr_t dpa; 837 dladm_status_t status; 838 i_walk_dev_state_t *statep = arg; 839 840 status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_PERSIST); 841 if ((status == DLADM_STATUS_OK) && 842 (strcmp(statep->devname, dpa.dp_dev) == 0)) { 843 statep->found = B_TRUE; 844 statep->linkid = linkid; 845 return (DLADM_WALK_TERMINATE); 846 } 847 return (DLADM_WALK_CONTINUE); 848 } 849 850 /* 851 * Get the linkid from the physical device name. 852 */ 853 dladm_status_t 854 dladm_dev2linkid(dladm_handle_t handle, const char *devname, 855 datalink_id_t *linkidp) 856 { 857 i_walk_dev_state_t state; 858 859 state.found = B_FALSE; 860 state.devname = devname; 861 862 (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, handle, &state, 863 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 864 if (state.found == B_TRUE) { 865 *linkidp = state.linkid; 866 return (DLADM_STATUS_OK); 867 } else { 868 return (dladm_errno2status(ENOENT)); 869 } 870 } 871 872 static int 873 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen) 874 { 875 char *cp, *tp; 876 int len; 877 878 /* 879 * device name length must not be 0, and it must end with digit. 880 */ 881 if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1])) 882 return (EINVAL); 883 884 (void) strlcpy(driver, devname, maxlen); 885 cp = (char *)&driver[len - 1]; 886 887 for (tp = cp; isdigit(*tp); tp--) { 888 if (tp <= driver) 889 return (EINVAL); 890 } 891 892 *ppa = atoi(tp + 1); 893 *(tp + 1) = '\0'; 894 return (0); 895 } 896 897 dladm_status_t 898 dladm_linkid2legacyname(dladm_handle_t handle, datalink_id_t linkid, char *dev, 899 size_t len) 900 { 901 char devname[MAXLINKNAMELEN]; 902 uint16_t vid = VLAN_ID_NONE; 903 datalink_class_t class; 904 dladm_status_t status; 905 906 status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, 907 NULL, 0); 908 if (status != DLADM_STATUS_OK) 909 goto done; 910 911 /* 912 * If this is a VLAN, we must first determine the class and linkid of 913 * the link the VLAN has been created over. 914 */ 915 if (class == DATALINK_CLASS_VLAN) { 916 dladm_vlan_attr_t dva; 917 918 status = dladm_vlan_info(handle, linkid, &dva, 919 DLADM_OPT_ACTIVE); 920 if (status != DLADM_STATUS_OK) 921 goto done; 922 linkid = dva.dv_linkid; 923 vid = dva.dv_vid; 924 925 if ((status = dladm_datalink_id2info(handle, linkid, NULL, 926 &class, NULL, NULL, 0)) != DLADM_STATUS_OK) { 927 goto done; 928 } 929 } 930 931 switch (class) { 932 case DATALINK_CLASS_AGGR: { 933 dladm_aggr_grp_attr_t dga; 934 935 status = dladm_aggr_info(handle, linkid, &dga, 936 DLADM_OPT_ACTIVE); 937 if (status != DLADM_STATUS_OK) 938 goto done; 939 940 if (dga.lg_key == 0) { 941 /* 942 * If the key was not specified when the aggregation 943 * is created, we cannot guess its /dev node name. 944 */ 945 status = DLADM_STATUS_BADARG; 946 goto done; 947 } 948 (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key); 949 break; 950 } 951 case DATALINK_CLASS_PHYS: { 952 dladm_phys_attr_t dpa; 953 954 status = dladm_phys_info(handle, linkid, &dpa, 955 DLADM_OPT_PERSIST); 956 if (status != DLADM_STATUS_OK) 957 goto done; 958 959 (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN); 960 break; 961 } 962 default: 963 status = DLADM_STATUS_BADARG; 964 goto done; 965 } 966 967 if (vid != VLAN_ID_NONE) { 968 char drv[MAXNAMELEN]; 969 uint_t ppa; 970 971 if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) { 972 status = DLADM_STATUS_BADARG; 973 goto done; 974 } 975 if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len) 976 status = DLADM_STATUS_TOOSMALL; 977 } else { 978 if (strlcpy(dev, devname, len) >= len) 979 status = DLADM_STATUS_TOOSMALL; 980 } 981 982 done: 983 return (status); 984 } 985 986 dladm_status_t 987 dladm_parselink(const char *dev, char *provider, uint_t *ppa) 988 { 989 ifspec_t ifsp; 990 991 if (dev == NULL || !ifparse_ifspec(dev, &ifsp)) 992 return (DLADM_STATUS_LINKINVAL); 993 994 if (provider != NULL) 995 (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX); 996 997 if (ppa != NULL) 998 *ppa = ifsp.ifsp_ppa; 999 1000 return (DLADM_STATUS_OK); 1001 } 1002