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