1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <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 and simnets. 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) || 400 (class == DATALINK_CLASS_SIMNET)) || (media != DL_ETHER)) { 401 return (DLADM_STATUS_BADARG); 402 } 403 } 404 405 /* 406 * First, update the persistent configuration if requested. We only 407 * need to update the FPORTS and FNPORTS fields of this aggregation. 408 * Note that FPORTS is a list of port linkids separated by 409 * PORT_DELIMITER ('.'). 410 */ 411 if (flags & DLADM_OPT_PERSIST) { 412 status = dladm_read_conf(handle, linkid, &conf); 413 if (status != DLADM_STATUS_OK) 414 return (status); 415 416 /* 417 * Get the original configuration of FNPORTS and FPORTS. 418 */ 419 status = dladm_get_conf_field(handle, conf, FNPORTS, &u64, 420 sizeof (u64)); 421 if (status != DLADM_STATUS_OK) 422 goto destroyconf; 423 orig_nports = (uint32_t)u64; 424 425 /* 426 * At least one port needs to be in the aggregation. 427 */ 428 if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) { 429 status = DLADM_STATUS_BADARG; 430 goto destroyconf; 431 } 432 433 size = orig_nports * (LINKID_STR_WIDTH + 1) + 1; 434 if ((orig_portstr = calloc(1, size)) == NULL) { 435 status = dladm_errno2status(errno); 436 goto destroyconf; 437 } 438 439 status = dladm_get_conf_field(handle, conf, FPORTS, 440 orig_portstr, size); 441 if (status != DLADM_STATUS_OK) 442 goto destroyconf; 443 444 result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports : 445 orig_nports; 446 447 size = result_nports * (LINKID_STR_WIDTH + 1) + 1; 448 if ((portstr = calloc(1, size)) == NULL) { 449 status = dladm_errno2status(errno); 450 goto destroyconf; 451 } 452 453 /* 454 * get the new configuration and set to result_nports and 455 * portstr. 456 */ 457 if (cmd == LAIOC_ADD) { 458 (void) strlcpy(portstr, orig_portstr, size); 459 for (i = 0; i < nports; i++) 460 WRITE_PORT(portstr, ports[i].lp_linkid, size); 461 } else { 462 char *next; 463 datalink_id_t portid; 464 uint32_t remove = 0; 465 466 for (next = orig_portstr, j = 0; j < orig_nports; j++) { 467 /* 468 * Read the portids from the old configuration 469 * one by one. 470 */ 471 READ_PORT(next, portid, status); 472 if (status != DLADM_STATUS_OK) { 473 free(portstr); 474 goto destroyconf; 475 } 476 477 /* 478 * See whether this port is in the removal 479 * list. If not, copy to the new config. 480 */ 481 for (i = 0; i < nports; i++) { 482 if (ports[i].lp_linkid == portid) 483 break; 484 } 485 if (i == nports) { 486 WRITE_PORT(portstr, portid, size); 487 } else { 488 remove++; 489 } 490 } 491 if (remove != nports) { 492 status = DLADM_STATUS_LINKINVAL; 493 free(portstr); 494 goto destroyconf; 495 } 496 result_nports -= nports; 497 } 498 499 u64 = result_nports; 500 if ((status = dladm_set_conf_field(handle, conf, FNPORTS, 501 DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) { 502 free(portstr); 503 goto destroyconf; 504 } 505 506 status = dladm_set_conf_field(handle, conf, FPORTS, 507 DLADM_TYPE_STR, portstr); 508 free(portstr); 509 if (status != DLADM_STATUS_OK) 510 goto destroyconf; 511 512 /* 513 * Write the new configuration to the persistent repository. 514 */ 515 status = dladm_write_conf(handle, conf); 516 517 destroyconf: 518 dladm_destroy_conf(handle, conf); 519 if (status != DLADM_STATUS_OK) { 520 free(orig_portstr); 521 return (status); 522 } 523 } 524 525 /* 526 * If the caller only requested to update the persistent 527 * configuration, we are done. 528 */ 529 if (!(flags & DLADM_OPT_ACTIVE)) 530 goto done; 531 532 /* 533 * Update the active configuration. 534 */ 535 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 536 if ((iocp = malloc(len)) == NULL) { 537 status = DLADM_STATUS_NOMEM; 538 goto done; 539 } 540 541 iocp->la_linkid = linkid; 542 iocp->la_nports = nports; 543 if (cmd == LAIOC_ADD) 544 iocp->la_force = (flags & DLADM_OPT_FORCE); 545 546 ioc_ports = (laioc_port_t *)(iocp + 1); 547 for (i = 0; i < nports; i++) 548 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 549 550 if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0) 551 status = dladm_errno2status(errno); 552 553 done: 554 free(iocp); 555 556 /* 557 * If the active configuration update fails, restore the old 558 * persistent configuration if we've changed that. 559 */ 560 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 561 if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 562 u64 = orig_nports; 563 if ((dladm_set_conf_field(handle, conf, FNPORTS, 564 DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) && 565 (dladm_set_conf_field(handle, conf, FPORTS, 566 DLADM_TYPE_STR, orig_portstr) == DLADM_STATUS_OK)) { 567 (void) dladm_write_conf(handle, conf); 568 } 569 (void) dladm_destroy_conf(handle, conf); 570 } 571 } 572 free(orig_portstr); 573 return (status); 574 } 575 576 /* 577 * Send a modify command to the link aggregation driver. 578 */ 579 static dladm_status_t 580 i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid, 581 uint32_t mask, dladm_aggr_modify_attr_t *attr) 582 { 583 laioc_modify_t ioc; 584 585 ioc.lu_linkid = linkid; 586 587 ioc.lu_modify_mask = 0; 588 if (mask & DLADM_AGGR_MODIFY_POLICY) 589 ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY; 590 if (mask & DLADM_AGGR_MODIFY_MAC) 591 ioc.lu_modify_mask |= LAIOC_MODIFY_MAC; 592 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) 593 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE; 594 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) 595 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER; 596 597 ioc.lu_policy = attr->ld_policy; 598 ioc.lu_mac_fixed = attr->ld_mac_fixed; 599 bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL); 600 ioc.lu_lacp_mode = attr->ld_lacp_mode; 601 ioc.lu_lacp_timer = attr->ld_lacp_timer; 602 603 if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) { 604 if (errno == EINVAL) 605 return (DLADM_STATUS_MACADDRINVAL); 606 else 607 return (dladm_errno2status(errno)); 608 } else { 609 return (DLADM_STATUS_OK); 610 } 611 } 612 613 /* 614 * Send a create command to the link aggregation driver. 615 */ 616 static dladm_status_t 617 i_dladm_aggr_create_sys(dladm_handle_t handle, datalink_id_t linkid, 618 uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports, 619 uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr, 620 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force) 621 { 622 int i, len; 623 laioc_create_t *iocp = NULL; 624 laioc_port_t *ioc_ports; 625 dladm_status_t status = DLADM_STATUS_OK; 626 627 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 628 iocp = malloc(len); 629 if (iocp == NULL) 630 return (DLADM_STATUS_NOMEM); 631 632 iocp->lc_key = key; 633 iocp->lc_linkid = linkid; 634 iocp->lc_nports = nports; 635 iocp->lc_policy = policy; 636 iocp->lc_lacp_mode = lacp_mode; 637 iocp->lc_lacp_timer = lacp_timer; 638 ioc_ports = (laioc_port_t *)(iocp + 1); 639 iocp->lc_force = force; 640 641 for (i = 0; i < nports; i++) 642 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 643 644 if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) { 645 status = DLADM_STATUS_MACADDRINVAL; 646 goto done; 647 } 648 649 bcopy(mac_addr, iocp->lc_mac, ETHERADDRL); 650 iocp->lc_mac_fixed = mac_addr_fixed; 651 652 if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0) 653 status = dladm_errno2status(errno); 654 655 done: 656 free(iocp); 657 return (status); 658 } 659 660 /* 661 * Invoked to bring up a link aggregation group. 662 */ 663 static int 664 i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg) 665 { 666 dladm_status_t *statusp = (dladm_status_t *)arg; 667 dladm_aggr_grp_attr_t attr; 668 dladm_aggr_port_attr_db_t *ports = NULL; 669 uint16_t key = 0; 670 int i, j; 671 dladm_status_t status; 672 673 status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST); 674 if (status != DLADM_STATUS_OK) { 675 *statusp = status; 676 return (DLADM_WALK_CONTINUE); 677 } 678 679 if (attr.lg_key <= AGGR_MAX_KEY) 680 key = attr.lg_key; 681 682 ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t)); 683 if (ports == NULL) { 684 status = DLADM_STATUS_NOMEM; 685 goto done; 686 } 687 688 /* 689 * Validate (and purge) each physical link associated with this 690 * aggregation, if the specific hardware has been removed during 691 * the system shutdown. 692 */ 693 for (i = 0, j = 0; i < attr.lg_nports; i++) { 694 datalink_id_t portid = attr.lg_ports[i].lp_linkid; 695 uint32_t flags; 696 dladm_status_t s; 697 698 s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL, 699 NULL, 0); 700 if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) 701 continue; 702 703 ports[j++].lp_linkid = portid; 704 } 705 706 if (j == 0) { 707 /* 708 * All of the physical links are removed. 709 */ 710 status = DLADM_STATUS_BADARG; 711 goto done; 712 } 713 714 /* 715 * Create active aggregation. 716 */ 717 if ((status = i_dladm_aggr_create_sys(handle, linkid, 718 key, j, ports, attr.lg_policy, attr.lg_mac_fixed, 719 (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode, 720 attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) { 721 goto done; 722 } 723 724 if ((status = dladm_up_datalink_id(handle, linkid)) != 725 DLADM_STATUS_OK) { 726 laioc_delete_t ioc; 727 ioc.ld_linkid = linkid; 728 (void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc); 729 goto done; 730 } 731 732 /* 733 * Reset the active linkprop of this specific link. 734 */ 735 (void) dladm_init_linkprop(handle, linkid, B_FALSE); 736 737 done: 738 free(attr.lg_ports); 739 free(ports); 740 741 *statusp = status; 742 return (DLADM_WALK_CONTINUE); 743 } 744 745 /* 746 * Bring up one aggregation, or all persistent aggregations. In the latter 747 * case, the walk may terminate early if bringup of an aggregation fails. 748 */ 749 dladm_status_t 750 dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid) 751 { 752 dladm_status_t status; 753 754 if (linkid == DATALINK_ALL_LINKID) { 755 (void) dladm_walk_datalink_id(i_dladm_aggr_up, handle, &status, 756 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 757 DLADM_OPT_PERSIST); 758 return (DLADM_STATUS_OK); 759 } else { 760 (void) i_dladm_aggr_up(handle, linkid, &status); 761 return (status); 762 } 763 } 764 765 /* 766 * Given a policy string, return a policy mask. Returns B_TRUE on 767 * success, or B_FALSE if an error occurred during parsing. 768 */ 769 boolean_t 770 dladm_aggr_str2policy(const char *str, uint32_t *policy) 771 { 772 int i; 773 policy_t *pol; 774 char *token = NULL; 775 char *lasts; 776 777 *policy = 0; 778 779 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",", 780 &lasts)) != NULL) { 781 for (i = 0; i < NPOLICIES; i++) { 782 pol = &policies[i]; 783 if (strcasecmp(token, pol->pol_name) == 0) { 784 *policy |= pol->policy; 785 break; 786 } 787 } 788 if (i == NPOLICIES) 789 return (B_FALSE); 790 } 791 792 return (B_TRUE); 793 } 794 795 /* 796 * Given a policy mask, returns a printable string, or NULL if the 797 * policy mask is invalid. It is the responsibility of the caller to 798 * free the returned string after use. 799 */ 800 char * 801 dladm_aggr_policy2str(uint32_t policy, char *str) 802 { 803 int i, npolicies = 0; 804 policy_t *pol; 805 806 if (str == NULL) 807 return (NULL); 808 809 str[0] = '\0'; 810 811 for (i = 0; i < NPOLICIES; i++) { 812 pol = &policies[i]; 813 if ((policy & pol->policy) != 0) { 814 npolicies++; 815 if (npolicies > 1) 816 (void) strlcat(str, ",", DLADM_STRSIZE); 817 (void) strlcat(str, pol->pol_name, DLADM_STRSIZE); 818 } 819 } 820 821 return (str); 822 } 823 824 /* 825 * Given a MAC address string, return the MAC address in the mac_addr 826 * array. If the MAC address was not explicitly specified, i.e. is 827 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE. 828 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise. 829 */ 830 boolean_t 831 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr) 832 { 833 uchar_t *conv_str; 834 int mac_len; 835 836 *mac_fixed = (strcmp(str, "auto") != 0); 837 if (!*mac_fixed) { 838 bzero(mac_addr, ETHERADDRL); 839 return (B_TRUE); 840 } 841 842 conv_str = _link_aton(str, &mac_len); 843 if (conv_str == NULL) 844 return (B_FALSE); 845 846 if (mac_len != ETHERADDRL) { 847 free(conv_str); 848 return (B_FALSE); 849 } 850 851 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) || 852 (conv_str[0] & 0x01)) { 853 free(conv_str); 854 return (B_FALSE); 855 } 856 857 bcopy(conv_str, mac_addr, ETHERADDRL); 858 free(conv_str); 859 860 return (B_TRUE); 861 } 862 863 /* 864 * Returns a string containing a printable representation of a MAC address. 865 */ 866 const char * 867 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf) 868 { 869 static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 870 871 if (buf == NULL) 872 return (NULL); 873 874 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 875 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 876 else 877 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 878 879 return (buf); 880 } 881 882 /* 883 * Given a LACP mode string, find the corresponding LACP mode number. Returns 884 * B_TRUE if a match was found, B_FALSE otherwise. 885 */ 886 boolean_t 887 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode) 888 { 889 int i; 890 dladm_aggr_lacpmode_t *mode; 891 892 for (i = 0; i < NLACP_MODES; i++) { 893 mode = &lacp_modes[i]; 894 if (strncasecmp(str, mode->mode_str, 895 strlen(mode->mode_str)) == 0) { 896 *lacp_mode = mode->mode_id; 897 return (B_TRUE); 898 } 899 } 900 901 return (B_FALSE); 902 } 903 904 /* 905 * Given a LACP mode number, returns a printable string, or NULL if the 906 * LACP mode number is invalid. 907 */ 908 const char * 909 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf) 910 { 911 int i; 912 dladm_aggr_lacpmode_t *mode; 913 914 if (buf == NULL) 915 return (NULL); 916 917 for (i = 0; i < NLACP_MODES; i++) { 918 mode = &lacp_modes[i]; 919 if (mode->mode_id == mode_id) { 920 (void) snprintf(buf, DLADM_STRSIZE, "%s", 921 mode->mode_str); 922 return (buf); 923 } 924 } 925 926 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 927 return (buf); 928 } 929 930 /* 931 * Given a LACP timer string, find the corresponding LACP timer number. Returns 932 * B_TRUE if a match was found, B_FALSE otherwise. 933 */ 934 boolean_t 935 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer) 936 { 937 int i; 938 dladm_aggr_lacptimer_t *timer; 939 940 for (i = 0; i < NLACP_TIMERS; i++) { 941 timer = &lacp_timers[i]; 942 if (strncasecmp(str, timer->lt_str, 943 strlen(timer->lt_str)) == 0) { 944 *lacp_timer = timer->lt_id; 945 return (B_TRUE); 946 } 947 } 948 949 return (B_FALSE); 950 } 951 952 /* 953 * Given a LACP timer, returns a printable string, or NULL if the 954 * LACP timer number is invalid. 955 */ 956 const char * 957 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf) 958 { 959 int i; 960 dladm_aggr_lacptimer_t *timer; 961 962 if (buf == NULL) 963 return (NULL); 964 965 for (i = 0; i < NLACP_TIMERS; i++) { 966 timer = &lacp_timers[i]; 967 if (timer->lt_id == timer_id) { 968 (void) snprintf(buf, DLADM_STRSIZE, "%s", 969 timer->lt_str); 970 return (buf); 971 } 972 } 973 974 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 975 return (buf); 976 } 977 978 const char * 979 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf) 980 { 981 int i; 982 dladm_aggr_port_state_t *state; 983 984 if (buf == NULL) 985 return (NULL); 986 987 for (i = 0; i < NPORT_STATES; i++) { 988 state = &port_states[i]; 989 if (state->state_id == state_id) { 990 (void) snprintf(buf, DLADM_STRSIZE, "%s", 991 state->state_str); 992 return (buf); 993 } 994 } 995 996 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 997 return (buf); 998 } 999 1000 static dladm_status_t 1001 dladm_aggr_persist_aggr_conf(dladm_handle_t handle, const char *link, 1002 datalink_id_t linkid, uint16_t key, uint32_t nports, 1003 dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed, 1004 const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1005 aggr_lacp_timer_t lacp_timer, boolean_t force) 1006 { 1007 dladm_conf_t conf = DLADM_INVALID_CONF; 1008 char *portstr = NULL; 1009 char macstr[ETHERADDRL * 3]; 1010 dladm_status_t status; 1011 int i, size; 1012 uint64_t u64; 1013 1014 if ((status = dladm_create_conf(handle, link, linkid, 1015 DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) { 1016 return (status); 1017 } 1018 1019 u64 = key; 1020 status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64, 1021 &u64); 1022 if (status != DLADM_STATUS_OK) 1023 goto done; 1024 1025 u64 = nports; 1026 status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64, 1027 &u64); 1028 if (status != DLADM_STATUS_OK) 1029 goto done; 1030 1031 size = nports * (LINKID_STR_WIDTH + 1) + 1; 1032 if ((portstr = calloc(1, size)) == NULL) { 1033 status = DLADM_STATUS_NOMEM; 1034 goto done; 1035 } 1036 1037 for (i = 0; i < nports; i++) 1038 WRITE_PORT(portstr, ports[i].lp_linkid, size); 1039 status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR, 1040 portstr); 1041 free(portstr); 1042 1043 if (status != DLADM_STATUS_OK) 1044 goto done; 1045 1046 u64 = policy; 1047 status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64, 1048 &u64); 1049 if (status != DLADM_STATUS_OK) 1050 goto done; 1051 1052 status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 1053 DLADM_TYPE_BOOLEAN, &mac_addr_fixed); 1054 if (status != DLADM_STATUS_OK) 1055 goto done; 1056 1057 if (mac_addr_fixed) { 1058 if (!VALID_PORT_MAC(mac_addr)) { 1059 status = DLADM_STATUS_MACADDRINVAL; 1060 goto done; 1061 } 1062 1063 (void) dladm_aggr_macaddr2str(mac_addr, macstr); 1064 status = dladm_set_conf_field(handle, conf, FMACADDR, 1065 DLADM_TYPE_STR, macstr); 1066 if (status != DLADM_STATUS_OK) 1067 goto done; 1068 } 1069 1070 status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN, 1071 &force); 1072 if (status != DLADM_STATUS_OK) 1073 goto done; 1074 1075 u64 = lacp_mode; 1076 status = dladm_set_conf_field(handle, conf, FLACPMODE, 1077 DLADM_TYPE_UINT64, &u64); 1078 if (status != DLADM_STATUS_OK) 1079 goto done; 1080 1081 u64 = lacp_timer; 1082 status = dladm_set_conf_field(handle, conf, FLACPTIMER, 1083 DLADM_TYPE_UINT64, &u64); 1084 if (status != DLADM_STATUS_OK) 1085 goto done; 1086 1087 /* 1088 * Commit the link aggregation configuration. 1089 */ 1090 status = dladm_write_conf(handle, conf); 1091 1092 done: 1093 dladm_destroy_conf(handle, conf); 1094 return (status); 1095 } 1096 1097 /* 1098 * Create a new link aggregation group. Update the configuration 1099 * file and bring it up. 1100 */ 1101 dladm_status_t 1102 dladm_aggr_create(dladm_handle_t handle, const char *name, uint16_t key, 1103 uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t policy, 1104 boolean_t mac_addr_fixed, const uchar_t *mac_addr, 1105 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, uint32_t flags) 1106 { 1107 datalink_id_t linkid = DATALINK_INVALID_LINKID; 1108 uint32_t media; 1109 uint32_t i; 1110 datalink_class_t class; 1111 dladm_status_t status; 1112 boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE; 1113 1114 if (key != 0 && key > AGGR_MAX_KEY) 1115 return (DLADM_STATUS_KEYINVAL); 1116 1117 if (nports == 0) 1118 return (DLADM_STATUS_BADARG); 1119 1120 for (i = 0; i < nports; i++) { 1121 if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL, 1122 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 1123 !((class == DATALINK_CLASS_PHYS || class == 1124 DATALINK_CLASS_SIMNET) && (media == DL_ETHER))) { 1125 return (DLADM_STATUS_BADARG); 1126 } 1127 } 1128 1129 flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1130 if ((status = dladm_create_datalink_id(handle, name, 1131 DATALINK_CLASS_AGGR, DL_ETHER, flags, &linkid)) != 1132 DLADM_STATUS_OK) { 1133 goto fail; 1134 } 1135 1136 if ((flags & DLADM_OPT_PERSIST) && 1137 (status = dladm_aggr_persist_aggr_conf(handle, name, linkid, key, 1138 nports, ports, policy, mac_addr_fixed, mac_addr, lacp_mode, 1139 lacp_timer, force)) != DLADM_STATUS_OK) { 1140 goto fail; 1141 } 1142 1143 if (!(flags & DLADM_OPT_ACTIVE)) 1144 return (DLADM_STATUS_OK); 1145 1146 status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports, 1147 policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force); 1148 1149 if (status != DLADM_STATUS_OK) { 1150 if (flags & DLADM_OPT_PERSIST) 1151 (void) dladm_remove_conf(handle, linkid); 1152 goto fail; 1153 } 1154 1155 return (DLADM_STATUS_OK); 1156 1157 fail: 1158 if (linkid != DATALINK_INVALID_LINKID) 1159 (void) dladm_destroy_datalink_id(handle, linkid, flags); 1160 1161 return (status); 1162 } 1163 1164 static dladm_status_t 1165 i_dladm_aggr_get_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 1166 uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1167 { 1168 dladm_status_t status = DLADM_STATUS_OK; 1169 char macstr[ETHERADDRL * 3]; 1170 uint64_t u64; 1171 1172 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1173 status = dladm_get_conf_field(handle, conf, FPOLICY, &u64, 1174 sizeof (u64)); 1175 if (status != DLADM_STATUS_OK) 1176 return (status); 1177 attrp->ld_policy = (uint32_t)u64; 1178 } 1179 1180 if (mask & DLADM_AGGR_MODIFY_MAC) { 1181 status = dladm_get_conf_field(handle, conf, FFIXMACADDR, 1182 &attrp->ld_mac_fixed, sizeof (boolean_t)); 1183 if (status != DLADM_STATUS_OK) 1184 return (status); 1185 1186 if (attrp->ld_mac_fixed) { 1187 boolean_t fixed; 1188 1189 status = dladm_get_conf_field(handle, conf, FMACADDR, 1190 macstr, sizeof (macstr)); 1191 if (status != DLADM_STATUS_OK) 1192 return (status); 1193 1194 if (!dladm_aggr_str2macaddr(macstr, &fixed, 1195 attrp->ld_mac)) { 1196 return (DLADM_STATUS_REPOSITORYINVAL); 1197 } 1198 } 1199 } 1200 1201 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1202 status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64, 1203 sizeof (u64)); 1204 if (status != DLADM_STATUS_OK) 1205 return (status); 1206 attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64; 1207 } 1208 1209 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1210 status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64, 1211 sizeof (u64)); 1212 if (status != DLADM_STATUS_OK) 1213 return (status); 1214 attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64; 1215 } 1216 1217 return (status); 1218 } 1219 1220 static dladm_status_t 1221 i_dladm_aggr_set_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 1222 uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1223 { 1224 dladm_status_t status = DLADM_STATUS_OK; 1225 char macstr[ETHERADDRL * 3]; 1226 uint64_t u64; 1227 1228 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1229 u64 = attrp->ld_policy; 1230 status = dladm_set_conf_field(handle, conf, FPOLICY, 1231 DLADM_TYPE_UINT64, &u64); 1232 if (status != DLADM_STATUS_OK) 1233 return (status); 1234 } 1235 1236 if (mask & DLADM_AGGR_MODIFY_MAC) { 1237 status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 1238 DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed); 1239 if (status != DLADM_STATUS_OK) 1240 return (status); 1241 1242 if (attrp->ld_mac_fixed) { 1243 (void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr); 1244 status = dladm_set_conf_field(handle, conf, FMACADDR, 1245 DLADM_TYPE_STR, macstr); 1246 if (status != DLADM_STATUS_OK) 1247 return (status); 1248 } 1249 } 1250 1251 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1252 u64 = attrp->ld_lacp_mode; 1253 status = dladm_set_conf_field(handle, conf, FLACPMODE, 1254 DLADM_TYPE_UINT64, &u64); 1255 if (status != DLADM_STATUS_OK) 1256 return (status); 1257 } 1258 1259 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1260 u64 = attrp->ld_lacp_timer; 1261 status = dladm_set_conf_field(handle, conf, FLACPTIMER, 1262 DLADM_TYPE_UINT64, &u64); 1263 if (status != DLADM_STATUS_OK) 1264 return (status); 1265 } 1266 1267 return (status); 1268 } 1269 1270 /* 1271 * Modify the parameters of an existing link aggregation group. Update 1272 * the configuration file and pass the changes to the kernel. 1273 */ 1274 dladm_status_t 1275 dladm_aggr_modify(dladm_handle_t handle, datalink_id_t linkid, 1276 uint32_t modify_mask, uint32_t policy, boolean_t mac_fixed, 1277 const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1278 aggr_lacp_timer_t lacp_timer, uint32_t flags) 1279 { 1280 dladm_aggr_modify_attr_t new_attr, old_attr; 1281 dladm_conf_t conf; 1282 dladm_status_t status; 1283 1284 new_attr.ld_policy = policy; 1285 new_attr.ld_mac_fixed = mac_fixed; 1286 new_attr.ld_lacp_mode = lacp_mode; 1287 new_attr.ld_lacp_timer = lacp_timer; 1288 bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL); 1289 1290 if (flags & DLADM_OPT_PERSIST) { 1291 status = dladm_read_conf(handle, linkid, &conf); 1292 if (status != DLADM_STATUS_OK) 1293 return (status); 1294 1295 if ((status = i_dladm_aggr_get_aggr_attr(handle, conf, 1296 modify_mask, &old_attr)) != DLADM_STATUS_OK) { 1297 goto done; 1298 } 1299 1300 if ((status = i_dladm_aggr_set_aggr_attr(handle, conf, 1301 modify_mask, &new_attr)) != DLADM_STATUS_OK) { 1302 goto done; 1303 } 1304 1305 status = dladm_write_conf(handle, conf); 1306 1307 done: 1308 dladm_destroy_conf(handle, conf); 1309 if (status != DLADM_STATUS_OK) 1310 return (status); 1311 } 1312 1313 if (!(flags & DLADM_OPT_ACTIVE)) 1314 return (DLADM_STATUS_OK); 1315 1316 status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask, 1317 &new_attr); 1318 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 1319 if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 1320 if (i_dladm_aggr_set_aggr_attr(handle, conf, 1321 modify_mask, &old_attr) == DLADM_STATUS_OK) { 1322 (void) dladm_write_conf(handle, conf); 1323 } 1324 dladm_destroy_conf(handle, conf); 1325 } 1326 } 1327 1328 return (status); 1329 } 1330 1331 typedef struct aggr_held_arg_s { 1332 datalink_id_t aggrid; 1333 boolean_t isheld; 1334 } aggr_held_arg_t; 1335 1336 static int 1337 i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1338 { 1339 aggr_held_arg_t *aggr_held_arg = arg; 1340 dladm_vlan_attr_t dva; 1341 1342 if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) != 1343 DLADM_STATUS_OK) 1344 return (DLADM_WALK_CONTINUE); 1345 1346 if (dva.dv_linkid == aggr_held_arg->aggrid) { 1347 /* 1348 * This VLAN is created over the given aggregation. 1349 */ 1350 aggr_held_arg->isheld = B_TRUE; 1351 return (DLADM_WALK_TERMINATE); 1352 } 1353 return (DLADM_WALK_CONTINUE); 1354 } 1355 1356 /* 1357 * Delete a previously created link aggregation group. Either the name "aggr" 1358 * or the "key" is specified. 1359 */ 1360 dladm_status_t 1361 dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) 1362 { 1363 laioc_delete_t ioc; 1364 datalink_class_t class; 1365 dladm_status_t status; 1366 1367 if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL, 1368 0) != DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) { 1369 return (DLADM_STATUS_BADARG); 1370 } 1371 1372 if (flags & DLADM_OPT_ACTIVE) { 1373 ioc.ld_linkid = linkid; 1374 if ((i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc) < 0) && 1375 ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) { 1376 status = dladm_errno2status(errno); 1377 return (status); 1378 } 1379 1380 /* 1381 * Delete ACTIVE linkprop first. 1382 */ 1383 (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0, 1384 DLADM_OPT_ACTIVE); 1385 (void) dladm_destroy_datalink_id(handle, linkid, 1386 DLADM_OPT_ACTIVE); 1387 } 1388 1389 /* 1390 * If we reach here, it means that the active aggregation has already 1391 * been deleted, and there is no active VLANs holding this aggregation. 1392 * Now we see whether there is any persistent VLANs holding this 1393 * aggregation. If so, we fail the operation. 1394 */ 1395 if (flags & DLADM_OPT_PERSIST) { 1396 aggr_held_arg_t arg; 1397 1398 arg.aggrid = linkid; 1399 arg.isheld = B_FALSE; 1400 1401 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle, 1402 &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 1403 DLADM_OPT_PERSIST); 1404 if (arg.isheld) 1405 return (DLADM_STATUS_LINKBUSY); 1406 1407 (void) dladm_destroy_datalink_id(handle, linkid, 1408 DLADM_OPT_PERSIST); 1409 (void) dladm_remove_conf(handle, linkid); 1410 } 1411 1412 return (DLADM_STATUS_OK); 1413 } 1414 1415 /* 1416 * Add one or more ports to an existing link aggregation. 1417 */ 1418 dladm_status_t 1419 dladm_aggr_add(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1420 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1421 { 1422 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 1423 LAIOC_ADD)); 1424 } 1425 1426 /* 1427 * Remove one or more ports from an existing link aggregation. 1428 */ 1429 dladm_status_t 1430 dladm_aggr_remove(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1431 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1432 { 1433 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 1434 LAIOC_REMOVE)); 1435 } 1436 1437 typedef struct i_walk_key_state_s { 1438 uint16_t key; 1439 datalink_id_t linkid; 1440 boolean_t found; 1441 } i_walk_key_state_t; 1442 1443 static int 1444 i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1445 { 1446 dladm_conf_t conf; 1447 uint16_t key; 1448 dladm_status_t status; 1449 i_walk_key_state_t *statep = (i_walk_key_state_t *)arg; 1450 uint64_t u64; 1451 1452 if (dladm_read_conf(handle, linkid, &conf) != 0) 1453 return (DLADM_WALK_CONTINUE); 1454 1455 status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64)); 1456 key = (uint16_t)u64; 1457 dladm_destroy_conf(handle, conf); 1458 1459 if ((status == DLADM_STATUS_OK) && (key == statep->key)) { 1460 statep->found = B_TRUE; 1461 statep->linkid = linkid; 1462 return (DLADM_WALK_TERMINATE); 1463 } 1464 1465 return (DLADM_WALK_CONTINUE); 1466 } 1467 1468 dladm_status_t 1469 dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp, 1470 uint32_t flags) 1471 { 1472 i_walk_key_state_t state; 1473 1474 if (key > AGGR_MAX_KEY) 1475 return (DLADM_STATUS_NOTFOUND); 1476 1477 state.found = B_FALSE; 1478 state.key = key; 1479 1480 (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, handle, &state, 1481 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); 1482 if (state.found == B_TRUE) { 1483 *linkidp = state.linkid; 1484 return (DLADM_STATUS_OK); 1485 } else { 1486 return (DLADM_STATUS_NOTFOUND); 1487 } 1488 } 1489