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 <stdio.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <stropts.h> 33 #include <stdlib.h> 34 #include <errno.h> 35 #include <assert.h> 36 #include <strings.h> 37 #include <libintl.h> 38 #include <net/if_types.h> 39 #include <net/if_dl.h> 40 #include <sys/dld.h> 41 #include <libdllink.h> 42 #include <libdlvlan.h> 43 #include <libdlaggr.h> 44 #include <libdladm_impl.h> 45 46 /* 47 * Link Aggregation Administration Library. 48 * 49 * This library is used by administration tools such as dladm(1M) to 50 * configure link aggregations. 51 */ 52 53 /* Limits on buffer size for LAIOC_INFO request */ 54 #define MIN_INFO_SIZE (4*1024) 55 #define MAX_INFO_SIZE (128*1024) 56 57 static uchar_t zero_mac[] = {0, 0, 0, 0, 0, 0}; 58 #define VALID_PORT_MAC(mac) \ 59 (((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) && \ 60 (!(mac)[0] & 0x01)) 61 62 #define PORT_DELIMITER '.' 63 64 #define WRITE_PORT(portstr, portid, size) { \ 65 char pstr[LINKID_STR_WIDTH + 2]; \ 66 (void) snprintf(pstr, LINKID_STR_WIDTH + 2, "%d%c", \ 67 (portid), PORT_DELIMITER); \ 68 (void) strlcat((portstr), pstr, (size)); \ 69 } 70 71 #define READ_PORT(portstr, portid, status) { \ 72 errno = 0; \ 73 (status) = DLADM_STATUS_OK; \ 74 (portid) = (int)strtol((portstr), &(portstr), 10); \ 75 if (errno != 0 || *(portstr) != PORT_DELIMITER) { \ 76 (status) = DLADM_STATUS_REPOSITORYINVAL; \ 77 } else { \ 78 /* Skip the delimiter. */ \ 79 (portstr)++; \ 80 } \ 81 } 82 83 typedef struct dladm_aggr_modify_attr { 84 uint32_t ld_policy; 85 boolean_t ld_mac_fixed; 86 uchar_t ld_mac[ETHERADDRL]; 87 aggr_lacp_mode_t ld_lacp_mode; 88 aggr_lacp_timer_t ld_lacp_timer; 89 } dladm_aggr_modify_attr_t; 90 91 typedef struct policy_s { 92 char *pol_name; 93 uint32_t policy; 94 } policy_t; 95 96 static policy_t policies[] = { 97 {"L2", AGGR_POLICY_L2}, 98 {"L3", AGGR_POLICY_L3}, 99 {"L4", AGGR_POLICY_L4}}; 100 101 #define NPOLICIES (sizeof (policies) / sizeof (policy_t)) 102 103 typedef struct dladm_aggr_lacpmode_s { 104 char *mode_str; 105 aggr_lacp_mode_t mode_id; 106 } dladm_aggr_lacpmode_t; 107 108 static dladm_aggr_lacpmode_t lacp_modes[] = { 109 {"off", AGGR_LACP_OFF}, 110 {"active", AGGR_LACP_ACTIVE}, 111 {"passive", AGGR_LACP_PASSIVE}}; 112 113 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t)) 114 115 typedef struct dladm_aggr_lacptimer_s { 116 char *lt_str; 117 aggr_lacp_timer_t lt_id; 118 } dladm_aggr_lacptimer_t; 119 120 static dladm_aggr_lacptimer_t lacp_timers[] = { 121 {"short", AGGR_LACP_TIMER_SHORT}, 122 {"long", AGGR_LACP_TIMER_LONG}}; 123 124 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t)) 125 126 typedef struct dladm_aggr_port_state { 127 char *state_str; 128 aggr_port_state_t state_id; 129 } dladm_aggr_port_state_t; 130 131 static dladm_aggr_port_state_t port_states[] = { 132 {"standby", AGGR_PORT_STATE_STANDBY }, 133 {"attached", AGGR_PORT_STATE_ATTACHED } 134 }; 135 136 #define NPORT_STATES \ 137 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t)) 138 139 static int 140 i_dladm_aggr_ioctl(dladm_handle_t handle, int cmd, void *ptr) 141 { 142 return (ioctl(dladm_dld_fd(handle), cmd, ptr)); 143 } 144 145 /* 146 * Caller must free attr.lg_ports. The ptr pointer is advanced while convert 147 * the laioc_info_t to the dladm_aggr_grp_attr_t structure. 148 */ 149 static int 150 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp) 151 { 152 laioc_info_group_t *grp; 153 laioc_info_port_t *port; 154 int i; 155 void *where = (*ptr); 156 157 grp = (laioc_info_group_t *)where; 158 159 attrp->lg_linkid = grp->lg_linkid; 160 attrp->lg_key = grp->lg_key; 161 attrp->lg_nports = grp->lg_nports; 162 attrp->lg_policy = grp->lg_policy; 163 attrp->lg_lacp_mode = grp->lg_lacp_mode; 164 attrp->lg_lacp_timer = grp->lg_lacp_timer; 165 attrp->lg_force = grp->lg_force; 166 167 bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL); 168 attrp->lg_mac_fixed = grp->lg_mac_fixed; 169 170 if ((attrp->lg_ports = malloc(grp->lg_nports * 171 sizeof (dladm_aggr_port_attr_t))) == NULL) { 172 errno = ENOMEM; 173 goto fail; 174 } 175 176 where = (grp + 1); 177 178 /* 179 * Go through each port that is part of the group. 180 */ 181 for (i = 0; i < grp->lg_nports; i++) { 182 port = (laioc_info_port_t *)where; 183 184 attrp->lg_ports[i].lp_linkid = port->lp_linkid; 185 bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL); 186 attrp->lg_ports[i].lp_state = port->lp_state; 187 attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state; 188 189 where = (port + 1); 190 } 191 *ptr = where; 192 return (0); 193 fail: 194 return (-1); 195 } 196 197 /* 198 * Get active configuration of a specific aggregation. 199 * Caller must free attrp->la_ports. 200 */ 201 static dladm_status_t 202 i_dladm_aggr_info_active(dladm_handle_t handle, datalink_id_t linkid, 203 dladm_aggr_grp_attr_t *attrp) 204 { 205 laioc_info_t *ioc; 206 int bufsize; 207 void *where; 208 dladm_status_t status = DLADM_STATUS_OK; 209 210 bufsize = MIN_INFO_SIZE; 211 ioc = (laioc_info_t *)calloc(1, bufsize); 212 if (ioc == NULL) 213 return (DLADM_STATUS_NOMEM); 214 215 ioc->li_group_linkid = linkid; 216 217 tryagain: 218 ioc->li_bufsize = bufsize; 219 if (i_dladm_aggr_ioctl(handle, LAIOC_INFO, ioc) != 0) { 220 if (errno == ENOSPC) { 221 /* 222 * The LAIOC_INFO call failed due to a short 223 * buffer. Reallocate the buffer and try again. 224 */ 225 bufsize *= 2; 226 if (bufsize <= MAX_INFO_SIZE) { 227 ioc = (laioc_info_t *)realloc(ioc, bufsize); 228 if (ioc != NULL) { 229 bzero(ioc, sizeof (bufsize)); 230 goto tryagain; 231 } 232 } 233 } 234 status = dladm_errno2status(errno); 235 goto bail; 236 } 237 238 /* 239 * Go through each group returned by the aggregation driver. 240 */ 241 where = (char *)(ioc + 1); 242 if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) { 243 status = dladm_errno2status(errno); 244 goto bail; 245 } 246 247 bail: 248 free(ioc); 249 return (status); 250 } 251 252 /* 253 * Get configuration information of a specific aggregation. 254 * Caller must free attrp->la_ports. 255 */ 256 static dladm_status_t 257 i_dladm_aggr_info_persist(dladm_handle_t handle, datalink_id_t linkid, 258 dladm_aggr_grp_attr_t *attrp) 259 { 260 dladm_conf_t conf; 261 uint32_t nports, i; 262 char *portstr, *next; 263 dladm_status_t status; 264 uint64_t u64; 265 int size; 266 char macstr[ETHERADDRL * 3]; 267 268 attrp->lg_linkid = linkid; 269 if ((status = dladm_read_conf(handle, linkid, &conf)) != 270 DLADM_STATUS_OK) 271 return (status); 272 273 status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64)); 274 if (status != DLADM_STATUS_OK) 275 goto done; 276 attrp->lg_key = (uint16_t)u64; 277 278 status = dladm_get_conf_field(handle, conf, FPOLICY, &u64, 279 sizeof (u64)); 280 if (status != DLADM_STATUS_OK) 281 goto done; 282 attrp->lg_policy = (uint32_t)u64; 283 284 status = dladm_get_conf_field(handle, conf, FFIXMACADDR, 285 &attrp->lg_mac_fixed, sizeof (boolean_t)); 286 if (status != DLADM_STATUS_OK) 287 goto done; 288 289 if (attrp->lg_mac_fixed) { 290 boolean_t fixed; 291 292 if ((status = dladm_get_conf_field(handle, conf, FMACADDR, 293 macstr, sizeof (macstr))) != DLADM_STATUS_OK) { 294 goto done; 295 } 296 if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) { 297 status = DLADM_STATUS_REPOSITORYINVAL; 298 goto done; 299 } 300 } 301 302 status = dladm_get_conf_field(handle, conf, FFORCE, &attrp->lg_force, 303 sizeof (boolean_t)); 304 if (status != DLADM_STATUS_OK) 305 goto done; 306 307 status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64, 308 sizeof (u64)); 309 if (status != DLADM_STATUS_OK) 310 goto done; 311 attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64; 312 313 status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64, 314 sizeof (u64)); 315 if (status != DLADM_STATUS_OK) 316 goto done; 317 attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64; 318 319 status = dladm_get_conf_field(handle, conf, FNPORTS, &u64, 320 sizeof (u64)); 321 if (status != DLADM_STATUS_OK) 322 goto done; 323 nports = (uint32_t)u64; 324 attrp->lg_nports = nports; 325 326 size = nports * (LINKID_STR_WIDTH + 1) + 1; 327 if ((portstr = calloc(1, size)) == NULL) { 328 status = DLADM_STATUS_NOMEM; 329 goto done; 330 } 331 332 status = dladm_get_conf_field(handle, conf, FPORTS, portstr, size); 333 if (status != DLADM_STATUS_OK) { 334 free(portstr); 335 goto done; 336 } 337 338 if ((attrp->lg_ports = malloc(nports * 339 sizeof (dladm_aggr_port_attr_t))) == NULL) { 340 free(portstr); 341 status = DLADM_STATUS_NOMEM; 342 goto done; 343 } 344 345 for (next = portstr, i = 0; i < nports; i++) { 346 READ_PORT(next, attrp->lg_ports[i].lp_linkid, status); 347 if (status != DLADM_STATUS_OK) { 348 free(portstr); 349 free(attrp->lg_ports); 350 goto done; 351 } 352 } 353 free(portstr); 354 355 done: 356 dladm_destroy_conf(handle, conf); 357 return (status); 358 } 359 360 dladm_status_t 361 dladm_aggr_info(dladm_handle_t handle, datalink_id_t linkid, 362 dladm_aggr_grp_attr_t *attrp, uint32_t flags) 363 { 364 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 365 if (flags == DLADM_OPT_ACTIVE) 366 return (i_dladm_aggr_info_active(handle, linkid, attrp)); 367 else 368 return (i_dladm_aggr_info_persist(handle, linkid, attrp)); 369 } 370 371 /* 372 * Add or remove one or more ports to/from an existing link aggregation. 373 */ 374 static dladm_status_t 375 i_dladm_aggr_add_rmv(dladm_handle_t handle, datalink_id_t linkid, 376 uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd) 377 { 378 char *orig_portstr = NULL, *portstr = NULL; 379 laioc_add_rem_t *iocp = NULL; 380 laioc_port_t *ioc_ports; 381 uint32_t orig_nports, result_nports, len, i, j; 382 dladm_conf_t conf; 383 datalink_class_t class; 384 dladm_status_t status = DLADM_STATUS_OK; 385 int size; 386 uint64_t u64; 387 uint32_t media; 388 389 if (nports == 0) 390 return (DLADM_STATUS_BADARG); 391 392 /* 393 * Sanity check - aggregations can only be created over Ethernet 394 * physical links. 395 */ 396 for (i = 0; i < nports; i++) { 397 if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL, 398 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 399 (class != DATALINK_CLASS_PHYS) || (media != DL_ETHER)) { 400 return (DLADM_STATUS_BADARG); 401 } 402 } 403 404 /* 405 * First, update the persistent configuration if requested. We only 406 * need to update the FPORTS and FNPORTS fields of this aggregation. 407 * Note that FPORTS is a list of port linkids separated by 408 * PORT_DELIMITER ('.'). 409 */ 410 if (flags & DLADM_OPT_PERSIST) { 411 status = dladm_read_conf(handle, linkid, &conf); 412 if (status != DLADM_STATUS_OK) 413 return (status); 414 415 /* 416 * Get the original configuration of FNPORTS and FPORTS. 417 */ 418 status = dladm_get_conf_field(handle, conf, FNPORTS, &u64, 419 sizeof (u64)); 420 if (status != DLADM_STATUS_OK) 421 goto destroyconf; 422 orig_nports = (uint32_t)u64; 423 424 /* 425 * At least one port needs to be in the aggregation. 426 */ 427 if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) { 428 status = DLADM_STATUS_BADARG; 429 goto destroyconf; 430 } 431 432 size = orig_nports * (LINKID_STR_WIDTH + 1) + 1; 433 if ((orig_portstr = calloc(1, size)) == NULL) { 434 status = dladm_errno2status(errno); 435 goto destroyconf; 436 } 437 438 status = dladm_get_conf_field(handle, conf, FPORTS, 439 orig_portstr, size); 440 if (status != DLADM_STATUS_OK) 441 goto destroyconf; 442 443 result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports : 444 orig_nports; 445 446 size = result_nports * (LINKID_STR_WIDTH + 1) + 1; 447 if ((portstr = calloc(1, size)) == NULL) { 448 status = dladm_errno2status(errno); 449 goto destroyconf; 450 } 451 452 /* 453 * get the new configuration and set to result_nports and 454 * portstr. 455 */ 456 if (cmd == LAIOC_ADD) { 457 (void) strlcpy(portstr, orig_portstr, size); 458 for (i = 0; i < nports; i++) 459 WRITE_PORT(portstr, ports[i].lp_linkid, size); 460 } else { 461 char *next; 462 datalink_id_t portid; 463 uint32_t remove = 0; 464 465 for (next = orig_portstr, j = 0; j < orig_nports; j++) { 466 /* 467 * Read the portids from the old configuration 468 * one by one. 469 */ 470 READ_PORT(next, portid, status); 471 if (status != DLADM_STATUS_OK) { 472 free(portstr); 473 goto destroyconf; 474 } 475 476 /* 477 * See whether this port is in the removal 478 * list. If not, copy to the new config. 479 */ 480 for (i = 0; i < nports; i++) { 481 if (ports[i].lp_linkid == portid) 482 break; 483 } 484 if (i == nports) { 485 WRITE_PORT(portstr, portid, size); 486 } else { 487 remove++; 488 } 489 } 490 if (remove != nports) { 491 status = DLADM_STATUS_LINKINVAL; 492 free(portstr); 493 goto destroyconf; 494 } 495 result_nports -= nports; 496 } 497 498 u64 = result_nports; 499 if ((status = dladm_set_conf_field(handle, conf, FNPORTS, 500 DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) { 501 free(portstr); 502 goto destroyconf; 503 } 504 505 status = dladm_set_conf_field(handle, conf, FPORTS, 506 DLADM_TYPE_STR, portstr); 507 free(portstr); 508 if (status != DLADM_STATUS_OK) 509 goto destroyconf; 510 511 /* 512 * Write the new configuration to the persistent repository. 513 */ 514 status = dladm_write_conf(handle, conf); 515 516 destroyconf: 517 dladm_destroy_conf(handle, conf); 518 if (status != DLADM_STATUS_OK) { 519 free(orig_portstr); 520 return (status); 521 } 522 } 523 524 /* 525 * If the caller only requested to update the persistent 526 * configuration, we are done. 527 */ 528 if (!(flags & DLADM_OPT_ACTIVE)) 529 goto done; 530 531 /* 532 * Update the active configuration. 533 */ 534 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 535 if ((iocp = malloc(len)) == NULL) { 536 status = DLADM_STATUS_NOMEM; 537 goto done; 538 } 539 540 iocp->la_linkid = linkid; 541 iocp->la_nports = nports; 542 if (cmd == LAIOC_ADD) 543 iocp->la_force = (flags & DLADM_OPT_FORCE); 544 545 ioc_ports = (laioc_port_t *)(iocp + 1); 546 for (i = 0; i < nports; i++) 547 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 548 549 if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0) 550 status = dladm_errno2status(errno); 551 552 done: 553 free(iocp); 554 555 /* 556 * If the active configuration update fails, restore the old 557 * persistent configuration if we've changed that. 558 */ 559 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 560 if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 561 u64 = orig_nports; 562 if ((dladm_set_conf_field(handle, conf, FNPORTS, 563 DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) && 564 (dladm_set_conf_field(handle, conf, FPORTS, 565 DLADM_TYPE_STR, orig_portstr) == DLADM_STATUS_OK)) { 566 (void) dladm_write_conf(handle, conf); 567 } 568 (void) dladm_destroy_conf(handle, conf); 569 } 570 } 571 free(orig_portstr); 572 return (status); 573 } 574 575 /* 576 * Send a modify command to the link aggregation driver. 577 */ 578 static dladm_status_t 579 i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid, 580 uint32_t mask, dladm_aggr_modify_attr_t *attr) 581 { 582 laioc_modify_t ioc; 583 584 ioc.lu_linkid = linkid; 585 586 ioc.lu_modify_mask = 0; 587 if (mask & DLADM_AGGR_MODIFY_POLICY) 588 ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY; 589 if (mask & DLADM_AGGR_MODIFY_MAC) 590 ioc.lu_modify_mask |= LAIOC_MODIFY_MAC; 591 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) 592 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE; 593 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) 594 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER; 595 596 ioc.lu_policy = attr->ld_policy; 597 ioc.lu_mac_fixed = attr->ld_mac_fixed; 598 bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL); 599 ioc.lu_lacp_mode = attr->ld_lacp_mode; 600 ioc.lu_lacp_timer = attr->ld_lacp_timer; 601 602 if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) { 603 if (errno == EINVAL) 604 return (DLADM_STATUS_MACADDRINVAL); 605 else 606 return (dladm_errno2status(errno)); 607 } else { 608 return (DLADM_STATUS_OK); 609 } 610 } 611 612 /* 613 * Send a create command to the link aggregation driver. 614 */ 615 static dladm_status_t 616 i_dladm_aggr_create_sys(dladm_handle_t handle, datalink_id_t linkid, 617 uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports, 618 uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr, 619 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force) 620 { 621 int i, len; 622 laioc_create_t *iocp = NULL; 623 laioc_port_t *ioc_ports; 624 dladm_status_t status = DLADM_STATUS_OK; 625 626 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 627 iocp = malloc(len); 628 if (iocp == NULL) 629 return (DLADM_STATUS_NOMEM); 630 631 iocp->lc_key = key; 632 iocp->lc_linkid = linkid; 633 iocp->lc_nports = nports; 634 iocp->lc_policy = policy; 635 iocp->lc_lacp_mode = lacp_mode; 636 iocp->lc_lacp_timer = lacp_timer; 637 ioc_ports = (laioc_port_t *)(iocp + 1); 638 iocp->lc_force = force; 639 640 for (i = 0; i < nports; i++) 641 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 642 643 if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) { 644 status = DLADM_STATUS_MACADDRINVAL; 645 goto done; 646 } 647 648 bcopy(mac_addr, iocp->lc_mac, ETHERADDRL); 649 iocp->lc_mac_fixed = mac_addr_fixed; 650 651 if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0) 652 status = dladm_errno2status(errno); 653 654 done: 655 free(iocp); 656 return (status); 657 } 658 659 /* 660 * Invoked to bring up a link aggregation group. 661 */ 662 static int 663 i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg) 664 { 665 dladm_status_t *statusp = (dladm_status_t *)arg; 666 dladm_aggr_grp_attr_t attr; 667 dladm_aggr_port_attr_db_t *ports = NULL; 668 uint16_t key = 0; 669 int i, j; 670 dladm_status_t status; 671 672 status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST); 673 if (status != DLADM_STATUS_OK) { 674 *statusp = status; 675 return (DLADM_WALK_CONTINUE); 676 } 677 678 if (attr.lg_key <= AGGR_MAX_KEY) 679 key = attr.lg_key; 680 681 ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t)); 682 if (ports == NULL) { 683 status = DLADM_STATUS_NOMEM; 684 goto done; 685 } 686 687 /* 688 * Validate (and purge) each physical link associated with this 689 * aggregation, if the specific hardware has been removed during 690 * the system shutdown. 691 */ 692 for (i = 0, j = 0; i < attr.lg_nports; i++) { 693 datalink_id_t portid = attr.lg_ports[i].lp_linkid; 694 uint32_t flags; 695 dladm_status_t s; 696 697 s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL, 698 NULL, 0); 699 if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) 700 continue; 701 702 ports[j++].lp_linkid = portid; 703 } 704 705 if (j == 0) { 706 /* 707 * All of the physical links are removed. 708 */ 709 status = DLADM_STATUS_BADARG; 710 goto done; 711 } 712 713 /* 714 * Create active aggregation. 715 */ 716 if ((status = i_dladm_aggr_create_sys(handle, linkid, 717 key, j, ports, attr.lg_policy, attr.lg_mac_fixed, 718 (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode, 719 attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) { 720 goto done; 721 } 722 723 if ((status = dladm_up_datalink_id(handle, linkid)) != 724 DLADM_STATUS_OK) { 725 laioc_delete_t ioc; 726 ioc.ld_linkid = linkid; 727 (void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc); 728 goto done; 729 } 730 731 /* 732 * Reset the active linkprop of this specific link. 733 */ 734 (void) dladm_init_linkprop(handle, linkid, B_FALSE); 735 736 done: 737 free(attr.lg_ports); 738 free(ports); 739 740 *statusp = status; 741 return (DLADM_WALK_CONTINUE); 742 } 743 744 /* 745 * Bring up one aggregation, or all persistent aggregations. In the latter 746 * case, the walk may terminate early if bringup of an aggregation fails. 747 */ 748 dladm_status_t 749 dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid) 750 { 751 dladm_status_t status; 752 753 if (linkid == DATALINK_ALL_LINKID) { 754 (void) dladm_walk_datalink_id(i_dladm_aggr_up, handle, &status, 755 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 756 DLADM_OPT_PERSIST); 757 return (DLADM_STATUS_OK); 758 } else { 759 (void) i_dladm_aggr_up(handle, linkid, &status); 760 return (status); 761 } 762 } 763 764 /* 765 * Given a policy string, return a policy mask. Returns B_TRUE on 766 * success, or B_FALSE if an error occurred during parsing. 767 */ 768 boolean_t 769 dladm_aggr_str2policy(const char *str, uint32_t *policy) 770 { 771 int i; 772 policy_t *pol; 773 char *token = NULL; 774 char *lasts; 775 776 *policy = 0; 777 778 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",", 779 &lasts)) != NULL) { 780 for (i = 0; i < NPOLICIES; i++) { 781 pol = &policies[i]; 782 if (strcasecmp(token, pol->pol_name) == 0) { 783 *policy |= pol->policy; 784 break; 785 } 786 } 787 if (i == NPOLICIES) 788 return (B_FALSE); 789 } 790 791 return (B_TRUE); 792 } 793 794 /* 795 * Given a policy mask, returns a printable string, or NULL if the 796 * policy mask is invalid. It is the responsibility of the caller to 797 * free the returned string after use. 798 */ 799 char * 800 dladm_aggr_policy2str(uint32_t policy, char *str) 801 { 802 int i, npolicies = 0; 803 policy_t *pol; 804 805 if (str == NULL) 806 return (NULL); 807 808 str[0] = '\0'; 809 810 for (i = 0; i < NPOLICIES; i++) { 811 pol = &policies[i]; 812 if ((policy & pol->policy) != 0) { 813 npolicies++; 814 if (npolicies > 1) 815 (void) strlcat(str, ",", DLADM_STRSIZE); 816 (void) strlcat(str, pol->pol_name, DLADM_STRSIZE); 817 } 818 } 819 820 return (str); 821 } 822 823 /* 824 * Given a MAC address string, return the MAC address in the mac_addr 825 * array. If the MAC address was not explicitly specified, i.e. is 826 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE. 827 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise. 828 */ 829 boolean_t 830 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr) 831 { 832 uchar_t *conv_str; 833 int mac_len; 834 835 *mac_fixed = (strcmp(str, "auto") != 0); 836 if (!*mac_fixed) { 837 bzero(mac_addr, ETHERADDRL); 838 return (B_TRUE); 839 } 840 841 conv_str = _link_aton(str, &mac_len); 842 if (conv_str == NULL) 843 return (B_FALSE); 844 845 if (mac_len != ETHERADDRL) { 846 free(conv_str); 847 return (B_FALSE); 848 } 849 850 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) || 851 (conv_str[0] & 0x01)) { 852 free(conv_str); 853 return (B_FALSE); 854 } 855 856 bcopy(conv_str, mac_addr, ETHERADDRL); 857 free(conv_str); 858 859 return (B_TRUE); 860 } 861 862 /* 863 * Returns a string containing a printable representation of a MAC address. 864 */ 865 const char * 866 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf) 867 { 868 static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 869 870 if (buf == NULL) 871 return (NULL); 872 873 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 874 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 875 else 876 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 877 878 return (buf); 879 } 880 881 /* 882 * Given a LACP mode string, find the corresponding LACP mode number. Returns 883 * B_TRUE if a match was found, B_FALSE otherwise. 884 */ 885 boolean_t 886 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode) 887 { 888 int i; 889 dladm_aggr_lacpmode_t *mode; 890 891 for (i = 0; i < NLACP_MODES; i++) { 892 mode = &lacp_modes[i]; 893 if (strncasecmp(str, mode->mode_str, 894 strlen(mode->mode_str)) == 0) { 895 *lacp_mode = mode->mode_id; 896 return (B_TRUE); 897 } 898 } 899 900 return (B_FALSE); 901 } 902 903 /* 904 * Given a LACP mode number, returns a printable string, or NULL if the 905 * LACP mode number is invalid. 906 */ 907 const char * 908 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf) 909 { 910 int i; 911 dladm_aggr_lacpmode_t *mode; 912 913 if (buf == NULL) 914 return (NULL); 915 916 for (i = 0; i < NLACP_MODES; i++) { 917 mode = &lacp_modes[i]; 918 if (mode->mode_id == mode_id) { 919 (void) snprintf(buf, DLADM_STRSIZE, "%s", 920 mode->mode_str); 921 return (buf); 922 } 923 } 924 925 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 926 return (buf); 927 } 928 929 /* 930 * Given a LACP timer string, find the corresponding LACP timer number. Returns 931 * B_TRUE if a match was found, B_FALSE otherwise. 932 */ 933 boolean_t 934 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer) 935 { 936 int i; 937 dladm_aggr_lacptimer_t *timer; 938 939 for (i = 0; i < NLACP_TIMERS; i++) { 940 timer = &lacp_timers[i]; 941 if (strncasecmp(str, timer->lt_str, 942 strlen(timer->lt_str)) == 0) { 943 *lacp_timer = timer->lt_id; 944 return (B_TRUE); 945 } 946 } 947 948 return (B_FALSE); 949 } 950 951 /* 952 * Given a LACP timer, returns a printable string, or NULL if the 953 * LACP timer number is invalid. 954 */ 955 const char * 956 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf) 957 { 958 int i; 959 dladm_aggr_lacptimer_t *timer; 960 961 if (buf == NULL) 962 return (NULL); 963 964 for (i = 0; i < NLACP_TIMERS; i++) { 965 timer = &lacp_timers[i]; 966 if (timer->lt_id == timer_id) { 967 (void) snprintf(buf, DLADM_STRSIZE, "%s", 968 timer->lt_str); 969 return (buf); 970 } 971 } 972 973 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 974 return (buf); 975 } 976 977 const char * 978 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf) 979 { 980 int i; 981 dladm_aggr_port_state_t *state; 982 983 if (buf == NULL) 984 return (NULL); 985 986 for (i = 0; i < NPORT_STATES; i++) { 987 state = &port_states[i]; 988 if (state->state_id == state_id) { 989 (void) snprintf(buf, DLADM_STRSIZE, "%s", 990 state->state_str); 991 return (buf); 992 } 993 } 994 995 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 996 return (buf); 997 } 998 999 static dladm_status_t 1000 dladm_aggr_persist_aggr_conf(dladm_handle_t handle, const char *link, 1001 datalink_id_t linkid, uint16_t key, uint32_t nports, 1002 dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed, 1003 const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1004 aggr_lacp_timer_t lacp_timer, boolean_t force) 1005 { 1006 dladm_conf_t conf = DLADM_INVALID_CONF; 1007 char *portstr = NULL; 1008 char macstr[ETHERADDRL * 3]; 1009 dladm_status_t status; 1010 int i, size; 1011 uint64_t u64; 1012 1013 if ((status = dladm_create_conf(handle, link, linkid, 1014 DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) { 1015 return (status); 1016 } 1017 1018 u64 = key; 1019 status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64, 1020 &u64); 1021 if (status != DLADM_STATUS_OK) 1022 goto done; 1023 1024 u64 = nports; 1025 status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64, 1026 &u64); 1027 if (status != DLADM_STATUS_OK) 1028 goto done; 1029 1030 size = nports * (LINKID_STR_WIDTH + 1) + 1; 1031 if ((portstr = calloc(1, size)) == NULL) { 1032 status = DLADM_STATUS_NOMEM; 1033 goto done; 1034 } 1035 1036 for (i = 0; i < nports; i++) 1037 WRITE_PORT(portstr, ports[i].lp_linkid, size); 1038 status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR, 1039 portstr); 1040 free(portstr); 1041 1042 if (status != DLADM_STATUS_OK) 1043 goto done; 1044 1045 u64 = policy; 1046 status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64, 1047 &u64); 1048 if (status != DLADM_STATUS_OK) 1049 goto done; 1050 1051 status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 1052 DLADM_TYPE_BOOLEAN, &mac_addr_fixed); 1053 if (status != DLADM_STATUS_OK) 1054 goto done; 1055 1056 if (mac_addr_fixed) { 1057 if (!VALID_PORT_MAC(mac_addr)) { 1058 status = DLADM_STATUS_MACADDRINVAL; 1059 goto done; 1060 } 1061 1062 (void) dladm_aggr_macaddr2str(mac_addr, macstr); 1063 status = dladm_set_conf_field(handle, conf, FMACADDR, 1064 DLADM_TYPE_STR, macstr); 1065 if (status != DLADM_STATUS_OK) 1066 goto done; 1067 } 1068 1069 status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN, 1070 &force); 1071 if (status != DLADM_STATUS_OK) 1072 goto done; 1073 1074 u64 = lacp_mode; 1075 status = dladm_set_conf_field(handle, conf, FLACPMODE, 1076 DLADM_TYPE_UINT64, &u64); 1077 if (status != DLADM_STATUS_OK) 1078 goto done; 1079 1080 u64 = lacp_timer; 1081 status = dladm_set_conf_field(handle, conf, FLACPTIMER, 1082 DLADM_TYPE_UINT64, &u64); 1083 if (status != DLADM_STATUS_OK) 1084 goto done; 1085 1086 /* 1087 * Commit the link aggregation configuration. 1088 */ 1089 status = dladm_write_conf(handle, conf); 1090 1091 done: 1092 dladm_destroy_conf(handle, conf); 1093 return (status); 1094 } 1095 1096 /* 1097 * Create a new link aggregation group. Update the configuration 1098 * file and bring it up. 1099 */ 1100 dladm_status_t 1101 dladm_aggr_create(dladm_handle_t handle, const char *name, uint16_t key, 1102 uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t policy, 1103 boolean_t mac_addr_fixed, const uchar_t *mac_addr, 1104 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, uint32_t flags) 1105 { 1106 datalink_id_t linkid = DATALINK_INVALID_LINKID; 1107 uint32_t media; 1108 uint32_t i; 1109 datalink_class_t class; 1110 dladm_status_t status; 1111 boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE; 1112 1113 if (key != 0 && key > AGGR_MAX_KEY) 1114 return (DLADM_STATUS_KEYINVAL); 1115 1116 if (nports == 0) 1117 return (DLADM_STATUS_BADARG); 1118 1119 for (i = 0; i < nports; i++) { 1120 if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL, 1121 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 1122 !((class == DATALINK_CLASS_PHYS) && (media == DL_ETHER))) { 1123 return (DLADM_STATUS_BADARG); 1124 } 1125 } 1126 1127 flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1128 if ((status = dladm_create_datalink_id(handle, name, 1129 DATALINK_CLASS_AGGR, DL_ETHER, flags, &linkid)) != 1130 DLADM_STATUS_OK) { 1131 goto fail; 1132 } 1133 1134 if ((flags & DLADM_OPT_PERSIST) && 1135 (status = dladm_aggr_persist_aggr_conf(handle, name, linkid, key, 1136 nports, ports, policy, mac_addr_fixed, mac_addr, lacp_mode, 1137 lacp_timer, force)) != DLADM_STATUS_OK) { 1138 goto fail; 1139 } 1140 1141 if (!(flags & DLADM_OPT_ACTIVE)) 1142 return (DLADM_STATUS_OK); 1143 1144 status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports, 1145 policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force); 1146 1147 if (status != DLADM_STATUS_OK) { 1148 if (flags & DLADM_OPT_PERSIST) 1149 (void) dladm_remove_conf(handle, linkid); 1150 goto fail; 1151 } 1152 1153 return (DLADM_STATUS_OK); 1154 1155 fail: 1156 if (linkid != DATALINK_INVALID_LINKID) 1157 (void) dladm_destroy_datalink_id(handle, linkid, flags); 1158 1159 return (status); 1160 } 1161 1162 static dladm_status_t 1163 i_dladm_aggr_get_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 1164 uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1165 { 1166 dladm_status_t status = DLADM_STATUS_OK; 1167 char macstr[ETHERADDRL * 3]; 1168 uint64_t u64; 1169 1170 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1171 status = dladm_get_conf_field(handle, conf, FPOLICY, &u64, 1172 sizeof (u64)); 1173 if (status != DLADM_STATUS_OK) 1174 return (status); 1175 attrp->ld_policy = (uint32_t)u64; 1176 } 1177 1178 if (mask & DLADM_AGGR_MODIFY_MAC) { 1179 status = dladm_get_conf_field(handle, conf, FFIXMACADDR, 1180 &attrp->ld_mac_fixed, sizeof (boolean_t)); 1181 if (status != DLADM_STATUS_OK) 1182 return (status); 1183 1184 if (attrp->ld_mac_fixed) { 1185 boolean_t fixed; 1186 1187 status = dladm_get_conf_field(handle, conf, FMACADDR, 1188 macstr, sizeof (macstr)); 1189 if (status != DLADM_STATUS_OK) 1190 return (status); 1191 1192 if (!dladm_aggr_str2macaddr(macstr, &fixed, 1193 attrp->ld_mac)) { 1194 return (DLADM_STATUS_REPOSITORYINVAL); 1195 } 1196 } 1197 } 1198 1199 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1200 status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64, 1201 sizeof (u64)); 1202 if (status != DLADM_STATUS_OK) 1203 return (status); 1204 attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64; 1205 } 1206 1207 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1208 status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64, 1209 sizeof (u64)); 1210 if (status != DLADM_STATUS_OK) 1211 return (status); 1212 attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64; 1213 } 1214 1215 return (status); 1216 } 1217 1218 static dladm_status_t 1219 i_dladm_aggr_set_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 1220 uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1221 { 1222 dladm_status_t status = DLADM_STATUS_OK; 1223 char macstr[ETHERADDRL * 3]; 1224 uint64_t u64; 1225 1226 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1227 u64 = attrp->ld_policy; 1228 status = dladm_set_conf_field(handle, conf, FPOLICY, 1229 DLADM_TYPE_UINT64, &u64); 1230 if (status != DLADM_STATUS_OK) 1231 return (status); 1232 } 1233 1234 if (mask & DLADM_AGGR_MODIFY_MAC) { 1235 status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 1236 DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed); 1237 if (status != DLADM_STATUS_OK) 1238 return (status); 1239 1240 if (attrp->ld_mac_fixed) { 1241 (void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr); 1242 status = dladm_set_conf_field(handle, conf, FMACADDR, 1243 DLADM_TYPE_STR, macstr); 1244 if (status != DLADM_STATUS_OK) 1245 return (status); 1246 } 1247 } 1248 1249 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1250 u64 = attrp->ld_lacp_mode; 1251 status = dladm_set_conf_field(handle, conf, FLACPMODE, 1252 DLADM_TYPE_UINT64, &u64); 1253 if (status != DLADM_STATUS_OK) 1254 return (status); 1255 } 1256 1257 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1258 u64 = attrp->ld_lacp_timer; 1259 status = dladm_set_conf_field(handle, conf, FLACPTIMER, 1260 DLADM_TYPE_UINT64, &u64); 1261 if (status != DLADM_STATUS_OK) 1262 return (status); 1263 } 1264 1265 return (status); 1266 } 1267 1268 /* 1269 * Modify the parameters of an existing link aggregation group. Update 1270 * the configuration file and pass the changes to the kernel. 1271 */ 1272 dladm_status_t 1273 dladm_aggr_modify(dladm_handle_t handle, datalink_id_t linkid, 1274 uint32_t modify_mask, uint32_t policy, boolean_t mac_fixed, 1275 const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1276 aggr_lacp_timer_t lacp_timer, uint32_t flags) 1277 { 1278 dladm_aggr_modify_attr_t new_attr, old_attr; 1279 dladm_conf_t conf; 1280 dladm_status_t status; 1281 1282 new_attr.ld_policy = policy; 1283 new_attr.ld_mac_fixed = mac_fixed; 1284 new_attr.ld_lacp_mode = lacp_mode; 1285 new_attr.ld_lacp_timer = lacp_timer; 1286 bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL); 1287 1288 if (flags & DLADM_OPT_PERSIST) { 1289 status = dladm_read_conf(handle, linkid, &conf); 1290 if (status != DLADM_STATUS_OK) 1291 return (status); 1292 1293 if ((status = i_dladm_aggr_get_aggr_attr(handle, conf, 1294 modify_mask, &old_attr)) != DLADM_STATUS_OK) { 1295 goto done; 1296 } 1297 1298 if ((status = i_dladm_aggr_set_aggr_attr(handle, conf, 1299 modify_mask, &new_attr)) != DLADM_STATUS_OK) { 1300 goto done; 1301 } 1302 1303 status = dladm_write_conf(handle, conf); 1304 1305 done: 1306 dladm_destroy_conf(handle, conf); 1307 if (status != DLADM_STATUS_OK) 1308 return (status); 1309 } 1310 1311 if (!(flags & DLADM_OPT_ACTIVE)) 1312 return (DLADM_STATUS_OK); 1313 1314 status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask, 1315 &new_attr); 1316 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 1317 if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 1318 if (i_dladm_aggr_set_aggr_attr(handle, conf, 1319 modify_mask, &old_attr) == DLADM_STATUS_OK) { 1320 (void) dladm_write_conf(handle, conf); 1321 } 1322 dladm_destroy_conf(handle, conf); 1323 } 1324 } 1325 1326 return (status); 1327 } 1328 1329 typedef struct aggr_held_arg_s { 1330 datalink_id_t aggrid; 1331 boolean_t isheld; 1332 } aggr_held_arg_t; 1333 1334 static int 1335 i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1336 { 1337 aggr_held_arg_t *aggr_held_arg = arg; 1338 dladm_vlan_attr_t dva; 1339 1340 if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) != 1341 DLADM_STATUS_OK) 1342 return (DLADM_WALK_CONTINUE); 1343 1344 if (dva.dv_linkid == aggr_held_arg->aggrid) { 1345 /* 1346 * This VLAN is created over the given aggregation. 1347 */ 1348 aggr_held_arg->isheld = B_TRUE; 1349 return (DLADM_WALK_TERMINATE); 1350 } 1351 return (DLADM_WALK_CONTINUE); 1352 } 1353 1354 /* 1355 * Delete a previously created link aggregation group. Either the name "aggr" 1356 * or the "key" is specified. 1357 */ 1358 dladm_status_t 1359 dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) 1360 { 1361 laioc_delete_t ioc; 1362 datalink_class_t class; 1363 dladm_status_t status; 1364 1365 if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL, 1366 0) != DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) { 1367 return (DLADM_STATUS_BADARG); 1368 } 1369 1370 if (flags & DLADM_OPT_ACTIVE) { 1371 ioc.ld_linkid = linkid; 1372 if ((i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc) < 0) && 1373 ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) { 1374 status = dladm_errno2status(errno); 1375 return (status); 1376 } 1377 1378 /* 1379 * Delete ACTIVE linkprop first. 1380 */ 1381 (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0, 1382 DLADM_OPT_ACTIVE); 1383 (void) dladm_destroy_datalink_id(handle, linkid, 1384 DLADM_OPT_ACTIVE); 1385 } 1386 1387 /* 1388 * If we reach here, it means that the active aggregation has already 1389 * been deleted, and there is no active VLANs holding this aggregation. 1390 * Now we see whether there is any persistent VLANs holding this 1391 * aggregation. If so, we fail the operation. 1392 */ 1393 if (flags & DLADM_OPT_PERSIST) { 1394 aggr_held_arg_t arg; 1395 1396 arg.aggrid = linkid; 1397 arg.isheld = B_FALSE; 1398 1399 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle, 1400 &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 1401 DLADM_OPT_PERSIST); 1402 if (arg.isheld) 1403 return (DLADM_STATUS_LINKBUSY); 1404 1405 (void) dladm_destroy_datalink_id(handle, linkid, 1406 DLADM_OPT_PERSIST); 1407 (void) dladm_remove_conf(handle, linkid); 1408 } 1409 1410 return (DLADM_STATUS_OK); 1411 } 1412 1413 /* 1414 * Add one or more ports to an existing link aggregation. 1415 */ 1416 dladm_status_t 1417 dladm_aggr_add(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1418 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1419 { 1420 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 1421 LAIOC_ADD)); 1422 } 1423 1424 /* 1425 * Remove one or more ports from an existing link aggregation. 1426 */ 1427 dladm_status_t 1428 dladm_aggr_remove(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1429 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1430 { 1431 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 1432 LAIOC_REMOVE)); 1433 } 1434 1435 typedef struct i_walk_key_state_s { 1436 uint16_t key; 1437 datalink_id_t linkid; 1438 boolean_t found; 1439 } i_walk_key_state_t; 1440 1441 static int 1442 i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1443 { 1444 dladm_conf_t conf; 1445 uint16_t key; 1446 dladm_status_t status; 1447 i_walk_key_state_t *statep = (i_walk_key_state_t *)arg; 1448 uint64_t u64; 1449 1450 if (dladm_read_conf(handle, linkid, &conf) != 0) 1451 return (DLADM_WALK_CONTINUE); 1452 1453 status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64)); 1454 key = (uint16_t)u64; 1455 dladm_destroy_conf(handle, conf); 1456 1457 if ((status == DLADM_STATUS_OK) && (key == statep->key)) { 1458 statep->found = B_TRUE; 1459 statep->linkid = linkid; 1460 return (DLADM_WALK_TERMINATE); 1461 } 1462 1463 return (DLADM_WALK_CONTINUE); 1464 } 1465 1466 dladm_status_t 1467 dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp, 1468 uint32_t flags) 1469 { 1470 i_walk_key_state_t state; 1471 1472 if (key > AGGR_MAX_KEY) 1473 return (DLADM_STATUS_NOTFOUND); 1474 1475 state.found = B_FALSE; 1476 state.key = key; 1477 1478 (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, handle, &state, 1479 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); 1480 if (state.found == B_TRUE) { 1481 *linkidp = state.linkid; 1482 return (DLADM_STATUS_OK); 1483 } else { 1484 return (DLADM_STATUS_NOTFOUND); 1485 } 1486 } 1487