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 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. Note that this function takes a link name 255 * argument instead of a linkid, because a data-link (and its linkid) could 256 * be created implicitly as the result of this function. For example, a VLAN 257 * could be created if a VLAN PPA hack name is assigned to an exclusive 258 * non-global zone. 259 */ 260 dladm_status_t 261 dladm_setzid(const char *dlname, char *zone_name) 262 { 263 datalink_id_t linkid; 264 char *val; 265 char **prop_val; 266 char link[MAXLINKNAMELEN]; 267 uint_t ppa; 268 char dev[DLPI_LINKNAME_MAX]; 269 int valsize; 270 dladm_status_t status = DLADM_STATUS_OK; 271 char *prop_name = "zone"; 272 boolean_t needfree = B_FALSE; 273 char delim = ':'; 274 275 /* If the link does not exist, it is a ppa-hacked vlan. */ 276 status = dladm_name2info(dlname, &linkid, NULL, NULL, NULL); 277 switch (status) { 278 case DLADM_STATUS_NOTFOUND: 279 if (strlen(dlname) > MAXLINKNAMELEN) 280 return (DLADM_STATUS_BADVAL); 281 282 if (strlen(zone_name) > ZONENAME_MAX) 283 return (DLADM_STATUS_BADVAL); 284 285 status = dladm_parselink(dlname, dev, &ppa); 286 if (status != DLADM_STATUS_OK) 287 return (status); 288 289 ppa = (uint_t)DLS_PPA2INST(ppa); 290 (void) snprintf(link, sizeof (link), "%s%d", dev, ppa); 291 292 status = dladm_name2info(link, &linkid, NULL, NULL, NULL); 293 if (status != DLADM_STATUS_OK) 294 return (status); 295 296 /* 297 * Since the link does not exist as yet, we've to pass the 298 * link name too as part of data, so that the kernel can 299 * create the link. Hence, we're packing the zone_name and 300 * the link name into val. 301 */ 302 valsize = ZONENAME_MAX + MAXLINKNAMELEN + 1; 303 val = malloc(valsize); 304 if (val == NULL) 305 return (DLADM_STATUS_NOMEM); 306 needfree = B_TRUE; 307 308 (void) snprintf(val, valsize, "%s%c%s", zone_name, 309 delim, dlname); 310 311 break; 312 case DLADM_STATUS_OK: 313 /* 314 * The link exists, so only the zone_name is being passed as 315 * val. We could also pass zone_name + linkname like in the 316 * previous case just to maintain consistency, but other calls 317 * like set_linkprop() in dladm.c [which is called when we run 318 * 'dladm set-linkprop -p zone <linkname>' at the command line] 319 * pass in the value entered at the command line [which is zone 320 * name] as val. 321 */ 322 val = zone_name; 323 break; 324 default: 325 return (DLADM_STATUS_FAILED); 326 } 327 328 prop_val = &val; 329 status = dladm_set_linkprop(linkid, prop_name, prop_val, 1, 330 DLADM_OPT_ACTIVE); 331 332 if (needfree) 333 free(val); 334 return (status); 335 } 336 337 /* 338 * Case 1: rename an existing link1 to a link2 that does not exist. 339 * Result: <linkid1, link2> 340 */ 341 static dladm_status_t 342 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1, 343 const char *link2, uint32_t flags) 344 { 345 dld_ioc_rename_t dir; 346 dladm_conf_t conf; 347 dladm_status_t status = DLADM_STATUS_OK; 348 int fd; 349 350 /* 351 * Link is currently available. Check to see whether anything is 352 * holding this link to prevent a rename operation. 353 */ 354 if (flags & DLADM_OPT_ACTIVE) { 355 dir.dir_linkid1 = linkid1; 356 dir.dir_linkid2 = DATALINK_INVALID_LINKID; 357 (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); 358 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 359 return (dladm_errno2status(errno)); 360 361 if (ioctl(fd, DLDIOC_RENAME, &dir) < 0) { 362 status = dladm_errno2status(errno); 363 (void) close(fd); 364 return (status); 365 } 366 } 367 368 status = dladm_remap_datalink_id(linkid1, link2); 369 if (status != DLADM_STATUS_OK) 370 goto done; 371 372 /* 373 * Flush the current mapping to persistent configuration. 374 */ 375 if ((flags & DLADM_OPT_PERSIST) && 376 (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) || 377 ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) { 378 (void) dladm_remap_datalink_id(linkid1, link1); 379 } 380 done: 381 if (flags & DLADM_OPT_ACTIVE) { 382 if (status != DLADM_STATUS_OK) { 383 (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); 384 (void) ioctl(fd, DLDIOC_RENAME, &dir); 385 } 386 (void) close(fd); 387 } 388 return (status); 389 } 390 391 typedef struct link_hold_arg_s { 392 datalink_id_t linkid; 393 datalink_id_t holder; 394 uint32_t flags; 395 } link_hold_arg_t; 396 397 static int 398 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg) 399 { 400 link_hold_arg_t *hold_arg = arg; 401 dladm_aggr_grp_attr_t ginfo; 402 dladm_status_t status; 403 int i; 404 405 status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags); 406 if (status != DLADM_STATUS_OK) 407 return (DLADM_WALK_CONTINUE); 408 409 for (i = 0; i < ginfo.lg_nports; i++) { 410 if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) { 411 hold_arg->holder = aggrid; 412 return (DLADM_WALK_TERMINATE); 413 } 414 } 415 return (DLADM_WALK_CONTINUE); 416 } 417 418 static int 419 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg) 420 { 421 link_hold_arg_t *hold_arg = arg; 422 dladm_vlan_attr_t vinfo; 423 dladm_status_t status; 424 425 status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags); 426 if (status != DLADM_STATUS_OK) 427 return (DLADM_WALK_CONTINUE); 428 429 if (vinfo.dv_linkid == hold_arg->linkid) { 430 hold_arg->holder = vlanid; 431 return (DLADM_WALK_TERMINATE); 432 } 433 return (DLADM_WALK_CONTINUE); 434 } 435 436 /* 437 * Case 2: rename an available physical link link1 to a REMOVED physical link 438 * link2. As a result, link1 directly inherits all datalinks configured 439 * over link2 (linkid2). 440 * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname, 441 * link2_other_attr> 442 */ 443 static dladm_status_t 444 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2) 445 { 446 rcm_handle_t *rcm_hdl = NULL; 447 nvlist_t *nvl = NULL; 448 link_hold_arg_t arg; 449 dld_ioc_rename_t dir; 450 int fd; 451 dladm_conf_t conf1, conf2; 452 char devname[MAXLINKNAMELEN]; 453 uint64_t phymaj, phyinst; 454 dladm_status_t status = DLADM_STATUS_OK; 455 456 /* 457 * First check if linkid1 is associated with any persistent 458 * aggregations or VLANs. If yes, return BUSY. 459 */ 460 arg.linkid = linkid1; 461 arg.holder = DATALINK_INVALID_LINKID; 462 arg.flags = DLADM_OPT_PERSIST; 463 (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg, 464 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 465 if (arg.holder != DATALINK_INVALID_LINKID) 466 return (DLADM_STATUS_LINKBUSY); 467 468 arg.flags = DLADM_OPT_PERSIST; 469 (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg, 470 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 471 if (arg.holder != DATALINK_INVALID_LINKID) 472 return (DLADM_STATUS_LINKBUSY); 473 474 /* 475 * Send DLDIOC_RENAME to request to rename link1's linkid to 476 * be linkid2. This will check whether link1 is used by any 477 * aggregations or VLANs, or is held by any application. If yes, 478 * return failure. 479 */ 480 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 481 return (dladm_errno2status(errno)); 482 483 dir.dir_linkid1 = linkid1; 484 dir.dir_linkid2 = linkid2; 485 if (ioctl(fd, DLDIOC_RENAME, &dir) < 0) 486 status = dladm_errno2status(errno); 487 488 if (status != DLADM_STATUS_OK) { 489 (void) close(fd); 490 return (status); 491 } 492 493 /* 494 * Now change the phymaj, phyinst and devname associated with linkid1 495 * to be associated with linkid2. Before doing that, the old active 496 * linkprop of linkid1 should be deleted. 497 */ 498 (void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE); 499 500 if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) || 501 ((status = dladm_get_conf_field(conf1, FDEVNAME, devname, 502 MAXLINKNAMELEN)) != DLADM_STATUS_OK) || 503 ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj, 504 sizeof (uint64_t))) != DLADM_STATUS_OK) || 505 ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst, 506 sizeof (uint64_t))) != DLADM_STATUS_OK) || 507 ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) { 508 dir.dir_linkid1 = linkid2; 509 dir.dir_linkid2 = linkid1; 510 (void) dladm_init_linkprop(linkid1, B_FALSE); 511 (void) ioctl(fd, DLDIOC_RENAME, &dir); 512 (void) close(fd); 513 return (status); 514 } 515 (void) close(fd); 516 517 dladm_destroy_conf(conf1); 518 (void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname); 519 (void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj); 520 (void) dladm_set_conf_field(conf2, FPHYINST, 521 DLADM_TYPE_UINT64, &phyinst); 522 (void) dladm_write_conf(conf2); 523 dladm_destroy_conf(conf2); 524 525 /* 526 * Delete link1 and mark link2 up. 527 */ 528 (void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE | 529 DLADM_OPT_PERSIST); 530 (void) dladm_remove_conf(linkid1); 531 (void) dladm_up_datalink_id(linkid2); 532 533 /* 534 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be 535 * consumed by the RCM framework to restore all the datalink and 536 * IP configuration. 537 */ 538 status = DLADM_STATUS_FAILED; 539 if ((nvlist_alloc(&nvl, 0, 0) != 0) || 540 (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) { 541 goto done; 542 } 543 544 if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS) 545 goto done; 546 547 if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) == 548 RCM_SUCCESS) { 549 status = DLADM_STATUS_OK; 550 } 551 552 done: 553 if (rcm_hdl != NULL) 554 (void) rcm_free_handle(rcm_hdl); 555 if (nvl != NULL) 556 nvlist_free(nvl); 557 return (status); 558 } 559 560 /* 561 * case 3: rename a non-existent link to a REMOVED physical link. 562 * Set the removed physical link's device name to link1, so that 563 * when link1 attaches, it inherits all the link configuration of 564 * the removed physical link. 565 */ 566 static dladm_status_t 567 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2) 568 { 569 dladm_conf_t conf; 570 dladm_status_t status; 571 572 if (!dladm_valid_linkname(link1)) 573 return (DLADM_STATUS_LINKINVAL); 574 575 status = dladm_read_conf(linkid2, &conf); 576 if (status != DLADM_STATUS_OK) 577 goto done; 578 579 if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR, 580 link1)) == DLADM_STATUS_OK) { 581 status = dladm_write_conf(conf); 582 } 583 584 dladm_destroy_conf(conf); 585 586 done: 587 return (status); 588 } 589 590 dladm_status_t 591 dladm_rename_link(const char *link1, const char *link2) 592 { 593 datalink_id_t linkid1 = DATALINK_INVALID_LINKID; 594 datalink_id_t linkid2 = DATALINK_INVALID_LINKID; 595 uint32_t flags1, flags2; 596 datalink_class_t class1, class2; 597 uint32_t media1, media2; 598 boolean_t remphy2 = B_FALSE; 599 dladm_status_t status; 600 601 (void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1); 602 if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) == 603 DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && 604 (flags2 == DLADM_OPT_PERSIST)) { 605 /* 606 * see whether link2 is a removed physical link. 607 */ 608 remphy2 = B_TRUE; 609 } 610 611 if (linkid1 != DATALINK_INVALID_LINKID) { 612 if (linkid2 == DATALINK_INVALID_LINKID) { 613 /* 614 * case 1: rename an existing link to a link that 615 * does not exist. 616 */ 617 status = i_dladm_rename_link_c1(linkid1, link1, link2, 618 flags1); 619 } else if (remphy2) { 620 /* 621 * case 2: rename an available link to a REMOVED 622 * physical link. Return failure if link1 is not 623 * an active physical link. 624 */ 625 if ((class1 != class2) || (media1 != media2) || 626 !(flags1 & DLADM_OPT_ACTIVE)) { 627 status = DLADM_STATUS_BADARG; 628 } else { 629 status = i_dladm_rename_link_c2(linkid1, 630 linkid2); 631 } 632 } else { 633 status = DLADM_STATUS_EXIST; 634 } 635 } else if (remphy2) { 636 status = i_dladm_rename_link_c3(link1, linkid2); 637 } else { 638 status = DLADM_STATUS_NOTFOUND; 639 } 640 return (status); 641 } 642 643 typedef struct consumer_del_phys_arg_s { 644 datalink_id_t linkid; 645 } consumer_del_phys_arg_t; 646 647 static int 648 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg) 649 { 650 consumer_del_phys_arg_t *del_arg = arg; 651 dladm_vlan_attr_t vinfo; 652 dladm_status_t status; 653 654 status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST); 655 if (status != DLADM_STATUS_OK) 656 return (DLADM_WALK_CONTINUE); 657 658 if (vinfo.dv_linkid == del_arg->linkid) 659 (void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST); 660 return (DLADM_WALK_CONTINUE); 661 } 662 663 static int 664 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg) 665 { 666 consumer_del_phys_arg_t *del_arg = arg; 667 dladm_aggr_grp_attr_t ginfo; 668 dladm_status_t status; 669 dladm_aggr_port_attr_db_t port[1]; 670 int i; 671 672 status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST); 673 if (status != DLADM_STATUS_OK) 674 return (DLADM_WALK_CONTINUE); 675 676 for (i = 0; i < ginfo.lg_nports; i++) 677 if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid) 678 break; 679 680 if (i != ginfo.lg_nports) { 681 if (ginfo.lg_nports == 1 && i == 0) { 682 consumer_del_phys_arg_t aggr_del_arg; 683 684 /* 685 * First delete all the VLANs on this aggregation, then 686 * delete the aggregation itself. 687 */ 688 aggr_del_arg.linkid = aggrid; 689 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, 690 &aggr_del_arg, DATALINK_CLASS_VLAN, 691 DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 692 (void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST); 693 } else { 694 port[0].lp_linkid = del_arg->linkid; 695 (void) dladm_aggr_remove(aggrid, 1, port, 696 DLADM_OPT_PERSIST); 697 } 698 } 699 return (DLADM_WALK_CONTINUE); 700 } 701 702 typedef struct del_phys_arg_s { 703 dladm_status_t rval; 704 } del_phys_arg_t; 705 706 static int 707 i_dladm_phys_delete(datalink_id_t linkid, void *arg) 708 { 709 uint32_t flags; 710 datalink_class_t class; 711 uint32_t media; 712 dladm_status_t status = DLADM_STATUS_OK; 713 del_phys_arg_t *del_phys_arg = arg; 714 consumer_del_phys_arg_t del_arg; 715 716 if ((status = dladm_datalink_id2info(linkid, &flags, &class, 717 &media, NULL, 0)) != DLADM_STATUS_OK) { 718 goto done; 719 } 720 721 /* 722 * see whether this link is a removed physical link. 723 */ 724 if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) || 725 (flags & DLADM_OPT_ACTIVE)) { 726 status = DLADM_STATUS_BADARG; 727 goto done; 728 } 729 730 if (media == DL_ETHER) { 731 del_arg.linkid = linkid; 732 (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg, 733 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 734 DLADM_OPT_PERSIST); 735 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg, 736 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 737 DLADM_OPT_PERSIST); 738 } 739 740 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); 741 (void) dladm_remove_conf(linkid); 742 743 done: 744 del_phys_arg->rval = status; 745 return (DLADM_WALK_CONTINUE); 746 } 747 748 dladm_status_t 749 dladm_phys_delete(datalink_id_t linkid) 750 { 751 del_phys_arg_t arg = {DLADM_STATUS_OK}; 752 753 if (linkid == DATALINK_ALL_LINKID) { 754 (void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg, 755 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, 756 DLADM_OPT_PERSIST); 757 return (DLADM_STATUS_OK); 758 } else { 759 (void) i_dladm_phys_delete(linkid, &arg); 760 return (arg.rval); 761 } 762 } 763 764 dladm_status_t 765 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags) 766 { 767 dladm_status_t status; 768 769 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 770 771 switch (flags) { 772 case DLADM_OPT_PERSIST: { 773 dladm_conf_t conf; 774 775 status = dladm_read_conf(linkid, &conf); 776 if (status != DLADM_STATUS_OK) 777 return (status); 778 779 status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev, 780 MAXLINKNAMELEN); 781 dladm_destroy_conf(conf); 782 return (status); 783 } 784 case DLADM_OPT_ACTIVE: { 785 dld_ioc_phys_attr_t dip; 786 int fd; 787 788 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 789 return (dladm_errno2status(errno)); 790 791 dip.dip_linkid = linkid; 792 if (ioctl(fd, DLDIOC_PHYS_ATTR, &dip) < 0) { 793 status = dladm_errno2status(errno); 794 (void) close(fd); 795 return (status); 796 } 797 (void) close(fd); 798 dpap->dp_novanity = dip.dip_novanity; 799 (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN); 800 return (DLADM_STATUS_OK); 801 } 802 default: 803 return (DLADM_STATUS_BADARG); 804 } 805 } 806 807 typedef struct i_walk_dev_state_s { 808 const char *devname; 809 datalink_id_t linkid; 810 boolean_t found; 811 } i_walk_dev_state_t; 812 813 int 814 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg) 815 { 816 dladm_phys_attr_t dpa; 817 dladm_status_t status; 818 i_walk_dev_state_t *statep = arg; 819 820 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 821 if ((status == DLADM_STATUS_OK) && 822 (strcmp(statep->devname, dpa.dp_dev) == 0)) { 823 statep->found = B_TRUE; 824 statep->linkid = linkid; 825 return (DLADM_WALK_TERMINATE); 826 } 827 return (DLADM_WALK_CONTINUE); 828 } 829 830 /* 831 * Get the linkid from the physical device name. 832 */ 833 dladm_status_t 834 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp) 835 { 836 i_walk_dev_state_t state; 837 838 state.found = B_FALSE; 839 state.devname = devname; 840 841 (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state, 842 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 843 if (state.found == B_TRUE) { 844 *linkidp = state.linkid; 845 return (DLADM_STATUS_OK); 846 } else { 847 return (dladm_errno2status(ENOENT)); 848 } 849 } 850 851 static int 852 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen) 853 { 854 char *cp, *tp; 855 int len; 856 857 /* 858 * device name length must not be 0, and it must end with digit. 859 */ 860 if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1])) 861 return (EINVAL); 862 863 (void) strlcpy(driver, devname, maxlen); 864 cp = (char *)&driver[len - 1]; 865 866 for (tp = cp; isdigit(*tp); tp--) { 867 if (tp <= driver) 868 return (EINVAL); 869 } 870 871 *ppa = atoi(tp + 1); 872 *(tp + 1) = '\0'; 873 return (0); 874 } 875 876 dladm_status_t 877 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len) 878 { 879 char devname[MAXLINKNAMELEN]; 880 uint16_t vid = VLAN_ID_NONE; 881 datalink_class_t class; 882 dladm_status_t status; 883 884 status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0); 885 if (status != DLADM_STATUS_OK) 886 goto done; 887 888 /* 889 * If this is a VLAN, we must first determine the class and linkid of 890 * the link the VLAN has been created over. 891 */ 892 if (class == DATALINK_CLASS_VLAN) { 893 dladm_vlan_attr_t dva; 894 895 status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE); 896 if (status != DLADM_STATUS_OK) 897 goto done; 898 linkid = dva.dv_linkid; 899 vid = dva.dv_vid; 900 901 if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL, 902 NULL, 0)) != DLADM_STATUS_OK) { 903 goto done; 904 } 905 } 906 907 switch (class) { 908 case DATALINK_CLASS_AGGR: { 909 dladm_aggr_grp_attr_t dga; 910 911 status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE); 912 if (status != DLADM_STATUS_OK) 913 goto done; 914 915 if (dga.lg_key == 0) { 916 /* 917 * If the key was not specified when the aggregation 918 * is created, we cannot guess its /dev node name. 919 */ 920 status = DLADM_STATUS_BADARG; 921 goto done; 922 } 923 (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key); 924 break; 925 } 926 case DATALINK_CLASS_PHYS: { 927 dladm_phys_attr_t dpa; 928 929 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 930 if (status != DLADM_STATUS_OK) 931 goto done; 932 933 (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN); 934 break; 935 } 936 default: 937 status = DLADM_STATUS_BADARG; 938 goto done; 939 } 940 941 if (vid != VLAN_ID_NONE) { 942 char drv[MAXNAMELEN]; 943 uint_t ppa; 944 945 if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) { 946 status = DLADM_STATUS_BADARG; 947 goto done; 948 } 949 if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len) 950 status = DLADM_STATUS_TOOSMALL; 951 } else { 952 if (strlcpy(dev, devname, len) >= len) 953 status = DLADM_STATUS_TOOSMALL; 954 } 955 956 done: 957 return (status); 958 } 959 960 dladm_status_t 961 dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type, 962 void *val) 963 { 964 char module[DLPI_LINKNAME_MAX]; 965 uint_t instance; 966 char link[DLPI_LINKNAME_MAX]; 967 dladm_status_t status; 968 uint32_t flags, media; 969 kstat_ctl_t *kcp; 970 kstat_t *ksp; 971 dladm_phys_attr_t dpap; 972 973 if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media, 974 link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 975 return (status); 976 977 if (media != DL_ETHER) 978 return (DLADM_STATUS_LINKINVAL); 979 980 status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST); 981 982 if (status != DLADM_STATUS_OK) 983 return (status); 984 985 status = dladm_parselink(dpap.dp_dev, module, &instance); 986 987 if (status != DLADM_STATUS_OK) 988 return (status); 989 990 if ((kcp = kstat_open()) == NULL) 991 return (dladm_errno2status(errno)); 992 993 /* 994 * The kstat query could fail if the underlying MAC 995 * driver was already detached. 996 */ 997 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 998 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 999 goto bail; 1000 1001 if (kstat_read(kcp, ksp, NULL) == -1) 1002 goto bail; 1003 1004 if (dladm_kstat_value(ksp, name, type, val) < 0) 1005 goto bail; 1006 1007 (void) kstat_close(kcp); 1008 return (DLADM_STATUS_OK); 1009 bail: 1010 (void) kstat_close(kcp); 1011 return (dladm_errno2status(errno)); 1012 1013 } 1014 1015 int 1016 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 1017 { 1018 kstat_named_t *knp; 1019 1020 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 1021 return (-1); 1022 1023 if (knp->data_type != type) 1024 return (-1); 1025 1026 switch (type) { 1027 case KSTAT_DATA_UINT64: 1028 *(uint64_t *)buf = knp->value.ui64; 1029 break; 1030 case KSTAT_DATA_UINT32: 1031 *(uint32_t *)buf = knp->value.ui32; 1032 break; 1033 default: 1034 return (-1); 1035 } 1036 1037 return (0); 1038 } 1039 1040 dladm_status_t 1041 dladm_parselink(const char *dev, char *provider, uint_t *ppa) 1042 { 1043 ifspec_t ifsp; 1044 1045 if (dev == NULL || !ifparse_ifspec(dev, &ifsp)) 1046 return (DLADM_STATUS_LINKINVAL); 1047 1048 if (provider != NULL) 1049 (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX); 1050 1051 if (ppa != NULL) 1052 *ppa = ifsp.ifsp_ppa; 1053 1054 return (DLADM_STATUS_OK); 1055 } 1056