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