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