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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <stropts.h> 35 #include <stdlib.h> 36 #include <errno.h> 37 #include <strings.h> 38 #include <libintl.h> 39 #include <net/if_types.h> 40 #include <net/if_dl.h> 41 #include <libdlaggr.h> 42 #include <libdladm_impl.h> 43 44 /* 45 * Link Aggregation Administration Library. 46 * 47 * This library is used by administration tools such as dladm(1M) to 48 * configure link aggregations. 49 * 50 * Link aggregation configuration information is saved in a text 51 * file of the following format: 52 * 53 * <db-file> ::= <groups>* 54 * <group> ::= <key> <sep> <policy> <sep> <nports> <sep> <ports> <sep> 55 * <mac> <sep> <lacp-mode> <sep> <lacp-timer> 56 * <sep> ::= ' ' | '\t' 57 * <key> ::= <number> 58 * <nports> ::= <number> 59 * <ports> ::= <port> <m-port>* 60 * <m-port> ::= ',' <port> 61 * <port> ::= <devname> 62 * <devname> ::= <string> 63 * <port-num> ::= <number> 64 * <policy> ::= <pol-level> <m-pol>* 65 * <m-pol> ::= ',' <pol-level> 66 * <pol-level> ::= 'L2' | 'L3' | 'L4' 67 * <mac> ::= 'auto' | <mac-addr> 68 * <mac-addr> ::= <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex> 69 * <lacp-mode> ::= 'off' | 'active' | 'passive' 70 * <lacp-timer> ::= 'short' | 'long' 71 */ 72 73 #define DLADM_AGGR_DEV "/devices/pseudo/aggr@0:" AGGR_DEVNAME_CTL 74 #define DLADM_AGGR_DB "/etc/dladm/aggregation.conf" 75 #define DLADM_AGGR_DB_TMP "/etc/dladm/aggregation.conf.new" 76 #define DLADM_AGGR_DB_LOCK "/tmp/aggregation.conf.lock" 77 78 #define DLADM_AGGR_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 79 #define DLADM_AGGR_DB_OWNER 15 /* "dladm" UID */ 80 #define DLADM_AGGR_DB_GROUP 3 /* "sys" GID */ 81 82 /* 83 * The largest configurable aggregation key. Because by default the key is 84 * used as the DLPI device PPA and default VLAN PPA's are calculated as 85 * ((1000 * vid) + PPA), the largest key can't be > 999. 86 */ 87 #define DLADM_AGGR_MAX_KEY 999 88 89 #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) 90 91 /* Limits on buffer size for LAIOC_INFO request */ 92 #define MIN_INFO_SIZE (4*1024) 93 #define MAX_INFO_SIZE (128*1024) 94 95 #define MAXPATHLEN 1024 96 97 static uchar_t zero_mac[] = {0, 0, 0, 0, 0, 0}; 98 99 /* configuration database entry */ 100 typedef struct dladm_aggr_grp_attr_db { 101 uint32_t lt_key; 102 uint32_t lt_policy; 103 uint32_t lt_nports; 104 dladm_aggr_port_attr_db_t *lt_ports; 105 boolean_t lt_mac_fixed; 106 uchar_t lt_mac[ETHERADDRL]; 107 aggr_lacp_mode_t lt_lacp_mode; 108 aggr_lacp_timer_t lt_lacp_timer; 109 } dladm_aggr_grp_attr_db_t; 110 111 typedef struct dladm_aggr_up { 112 uint32_t lu_key; 113 boolean_t lu_found; 114 int lu_fd; 115 } dladm_aggr_up_t; 116 117 typedef struct dladm_aggr_down { 118 uint32_t ld_key; 119 boolean_t ld_found; 120 } dladm_aggr_down_t; 121 122 typedef struct dladm_aggr_modify_attr { 123 uint32_t ld_policy; 124 boolean_t ld_mac_fixed; 125 uchar_t ld_mac[ETHERADDRL]; 126 aggr_lacp_mode_t ld_lacp_mode; 127 aggr_lacp_timer_t ld_lacp_timer; 128 } dladm_aggr_modify_attr_t; 129 130 typedef struct policy_s { 131 char *pol_name; 132 uint32_t policy; 133 } policy_t; 134 135 static policy_t policies[] = { 136 {"L2", AGGR_POLICY_L2}, 137 {"L3", AGGR_POLICY_L3}, 138 {"L4", AGGR_POLICY_L4}}; 139 140 #define NPOLICIES (sizeof (policies) / sizeof (policy_t)) 141 142 typedef struct dladm_aggr_lacpmode_s { 143 char *mode_str; 144 aggr_lacp_mode_t mode_id; 145 } dladm_aggr_lacpmode_t; 146 147 static dladm_aggr_lacpmode_t lacp_modes[] = { 148 {"off", AGGR_LACP_OFF}, 149 {"active", AGGR_LACP_ACTIVE}, 150 {"passive", AGGR_LACP_PASSIVE}}; 151 152 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t)) 153 154 typedef struct dladm_aggr_lacptimer_s { 155 char *lt_str; 156 aggr_lacp_timer_t lt_id; 157 } dladm_aggr_lacptimer_t; 158 159 static dladm_aggr_lacptimer_t lacp_timers[] = { 160 {"short", AGGR_LACP_TIMER_SHORT}, 161 {"long", AGGR_LACP_TIMER_LONG}}; 162 163 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t)) 164 165 typedef struct dladm_aggr_port_state { 166 char *state_str; 167 aggr_port_state_t state_id; 168 } dladm_aggr_port_state_t; 169 170 static dladm_aggr_port_state_t port_states[] = { 171 {"standby", AGGR_PORT_STATE_STANDBY }, 172 {"attached", AGGR_PORT_STATE_ATTACHED } 173 }; 174 175 #define NPORT_STATES \ 176 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t)) 177 178 typedef struct delete_db_state { 179 uint32_t ds_key; 180 boolean_t ds_found; 181 } delete_db_state_t; 182 183 typedef struct modify_db_state { 184 uint32_t us_key; 185 uint32_t us_mask; 186 dladm_aggr_modify_attr_t *us_attr_new; 187 dladm_aggr_modify_attr_t *us_attr_old; 188 boolean_t us_found; 189 } modify_db_state_t; 190 191 typedef struct add_db_state { 192 dladm_aggr_grp_attr_db_t *as_attr; 193 boolean_t as_found; 194 } add_db_state_t; 195 196 static int i_dladm_aggr_fput_grp(FILE *, dladm_aggr_grp_attr_db_t *); 197 198 /* 199 * Open and lock the aggregation configuration file lock. The lock is 200 * acquired as a reader (F_RDLCK) or writer (F_WRLCK). 201 */ 202 static int 203 i_dladm_aggr_lock_db(short type) 204 { 205 int lock_fd; 206 struct flock lock; 207 int errno_save; 208 209 if ((lock_fd = open(DLADM_AGGR_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC, 210 DLADM_AGGR_DB_PERMS)) < 0) 211 return (-1); 212 213 lock.l_type = type; 214 lock.l_whence = SEEK_SET; 215 lock.l_start = 0; 216 lock.l_len = 0; 217 218 if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 219 errno_save = errno; 220 (void) close(lock_fd); 221 (void) unlink(DLADM_AGGR_DB_LOCK); 222 errno = errno_save; 223 return (-1); 224 } 225 return (lock_fd); 226 } 227 228 /* 229 * Unlock and close the specified file. 230 */ 231 static void 232 i_dladm_aggr_unlock_db(int fd) 233 { 234 struct flock lock; 235 236 if (fd < 0) 237 return; 238 239 lock.l_type = F_UNLCK; 240 lock.l_whence = SEEK_SET; 241 lock.l_start = 0; 242 lock.l_len = 0; 243 244 (void) fcntl(fd, F_SETLKW, &lock); 245 (void) close(fd); 246 (void) unlink(DLADM_AGGR_DB_LOCK); 247 } 248 249 /* 250 * Walk through the groups defined on the system and for each group <grp>, 251 * invoke <fn>(<arg>, <grp>); 252 * Terminate the walk if at any time <fn> returns non-NULL value 253 */ 254 int 255 dladm_aggr_walk(int (*fn)(void *, dladm_aggr_grp_attr_t *), void *arg) 256 { 257 laioc_info_t *ioc; 258 laioc_info_group_t *grp; 259 laioc_info_port_t *port; 260 dladm_aggr_grp_attr_t attr; 261 int rc, i, j, bufsize, fd; 262 char *where; 263 264 if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) == -1) 265 return (-1); 266 267 bufsize = MIN_INFO_SIZE; 268 ioc = (laioc_info_t *)calloc(1, bufsize); 269 if (ioc == NULL) { 270 (void) close(fd); 271 errno = ENOMEM; 272 return (-1); 273 } 274 275 tryagain: 276 rc = i_dladm_ioctl(fd, LAIOC_INFO, ioc, bufsize); 277 278 if (rc != 0) { 279 if (errno == ENOSPC) { 280 /* 281 * The LAIOC_INFO call failed due to a short 282 * buffer. Reallocate the buffer and try again. 283 */ 284 bufsize *= 2; 285 if (bufsize <= MAX_INFO_SIZE) { 286 ioc = (laioc_info_t *)realloc(ioc, bufsize); 287 if (ioc != NULL) { 288 bzero(ioc, sizeof (bufsize)); 289 goto tryagain; 290 } 291 } 292 } 293 goto bail; 294 } 295 296 /* 297 * Go through each group returned by the aggregation driver. 298 */ 299 where = (char *)(ioc + 1); 300 for (i = 0; i < ioc->li_ngroups; i++) { 301 /* LINTED E_BAD_PTR_CAST_ALIGN */ 302 grp = (laioc_info_group_t *)where; 303 304 attr.lg_key = grp->lg_key; 305 attr.lg_nports = grp->lg_nports; 306 attr.lg_policy = grp->lg_policy; 307 attr.lg_lacp_mode = grp->lg_lacp_mode; 308 attr.lg_lacp_timer = grp->lg_lacp_timer; 309 310 bcopy(grp->lg_mac, attr.lg_mac, ETHERADDRL); 311 attr.lg_mac_fixed = grp->lg_mac_fixed; 312 313 attr.lg_ports = malloc(grp->lg_nports * 314 sizeof (dladm_aggr_port_attr_t)); 315 if (attr.lg_ports == NULL) { 316 errno = ENOMEM; 317 goto bail; 318 } 319 320 where = (char *)(grp + 1); 321 322 /* 323 * Go through each port that is part of the group. 324 */ 325 for (j = 0; j < grp->lg_nports; j++) { 326 /* LINTED E_BAD_PTR_CAST_ALIGN */ 327 port = (laioc_info_port_t *)where; 328 329 bcopy(port->lp_devname, attr.lg_ports[j].lp_devname, 330 MAXNAMELEN + 1); 331 bcopy(port->lp_mac, attr.lg_ports[j].lp_mac, 332 ETHERADDRL); 333 attr.lg_ports[j].lp_state = port->lp_state; 334 attr.lg_ports[j].lp_lacp_state = port->lp_lacp_state; 335 336 where = (char *)(port + 1); 337 } 338 339 rc = fn(arg, &attr); 340 free(attr.lg_ports); 341 if (rc != 0) 342 goto bail; 343 } 344 345 bail: 346 free(ioc); 347 (void) close(fd); 348 return (rc); 349 } 350 351 /* 352 * Parse one line of the link aggregation DB, and return the corresponding 353 * group. Memory for the ports associated with the aggregation may be 354 * allocated. It is the responsibility of the caller to free the lt_ports 355 * aggregation group attribute. 356 * 357 * Returns -1 on parsing failure, or 0 on success. 358 */ 359 static int 360 i_dladm_aggr_parse_db(char *line, dladm_aggr_grp_attr_db_t *attr) 361 { 362 char *token; 363 int i; 364 int value; 365 char *endp = NULL; 366 char *lasts = NULL; 367 368 bzero(attr, sizeof (*attr)); 369 370 /* key */ 371 if ((token = strtok_r(line, " \t", &lasts)) == NULL) 372 goto failed; 373 374 errno = 0; 375 value = (int)strtol(token, &endp, 10); 376 if (errno != 0 || *endp != '\0') 377 goto failed; 378 379 attr->lt_key = value; 380 381 /* policy */ 382 if ((token = strtok_r(NULL, " \t", &lasts)) == NULL || 383 !dladm_aggr_str2policy(token, &attr->lt_policy)) 384 goto failed; 385 386 /* number of ports */ 387 if ((token = strtok_r(NULL, " \t", &lasts)) == NULL) 388 return (-1); 389 390 errno = 0; 391 value = (int)strtol(token, &endp, 10); 392 if (errno != 0 || *endp != '\0') 393 goto failed; 394 395 attr->lt_nports = value; 396 397 /* ports */ 398 if ((attr->lt_ports = malloc(attr->lt_nports * 399 sizeof (dladm_aggr_port_attr_db_t))) == NULL) 400 goto failed; 401 402 for (i = 0; i < attr->lt_nports; i++) { 403 char *where, *devname; 404 405 /* port */ 406 if ((token = strtok_r(NULL, ", \t\n", &lasts)) == NULL) 407 goto failed; 408 409 /* 410 * device name: In a previous version of this file, a port 411 * number could be specified using <devname>/<portnum>. 412 * This syntax is unecessary and obsolete. 413 */ 414 if ((devname = strtok_r(token, "/", &where)) == NULL) 415 goto failed; 416 if (strlcpy(attr->lt_ports[i].lp_devname, devname, 417 MAXNAMELEN) >= MAXNAMELEN) 418 goto failed; 419 } 420 421 /* unicast MAC address */ 422 if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL || 423 !dladm_aggr_str2macaddr(token, &attr->lt_mac_fixed, 424 attr->lt_mac)) 425 goto failed; 426 427 /* LACP mode */ 428 if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL || 429 !dladm_aggr_str2lacpmode(token, &attr->lt_lacp_mode)) 430 attr->lt_lacp_mode = AGGR_LACP_OFF; 431 432 /* LACP timer */ 433 if ((token = strtok_r(NULL, " \t\n", &lasts)) == NULL || 434 !dladm_aggr_str2lacptimer(token, &attr->lt_lacp_timer)) 435 attr->lt_lacp_timer = AGGR_LACP_TIMER_SHORT; 436 437 return (0); 438 439 failed: 440 free(attr->lt_ports); 441 attr->lt_ports = NULL; 442 return (-1); 443 } 444 445 /* 446 * Walk through the groups defined in the DB and for each group <grp>, 447 * invoke <fn>(<arg>, <grp>); 448 */ 449 static dladm_status_t 450 i_dladm_aggr_walk_db(dladm_status_t (*fn)(void *, dladm_aggr_grp_attr_db_t *), 451 void *arg, const char *root) 452 { 453 FILE *fp; 454 char line[MAXLINELEN]; 455 dladm_aggr_grp_attr_db_t attr; 456 char *db_file; 457 char db_file_buf[MAXPATHLEN]; 458 int lock_fd; 459 dladm_status_t status = DLADM_STATUS_OK; 460 461 if (root == NULL) { 462 db_file = DLADM_AGGR_DB; 463 } else { 464 (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 465 DLADM_AGGR_DB); 466 db_file = db_file_buf; 467 } 468 469 lock_fd = i_dladm_aggr_lock_db(F_RDLCK); 470 471 if ((fp = fopen(db_file, "r")) == NULL) { 472 status = dladm_errno2status(errno); 473 i_dladm_aggr_unlock_db(lock_fd); 474 return (status); 475 } 476 477 bzero(&attr, sizeof (attr)); 478 479 while (fgets(line, MAXLINELEN, fp) != NULL) { 480 /* skip comments */ 481 if (BLANK_LINE(line)) 482 continue; 483 484 if (i_dladm_aggr_parse_db(line, &attr) != 0) { 485 status = DLADM_STATUS_REPOSITORYINVAL; 486 goto done; 487 } 488 489 if ((status = fn(arg, &attr)) != DLADM_STATUS_OK) 490 goto done; 491 492 free(attr.lt_ports); 493 attr.lt_ports = NULL; 494 } 495 496 done: 497 free(attr.lt_ports); 498 (void) fclose(fp); 499 i_dladm_aggr_unlock_db(lock_fd); 500 return (status); 501 } 502 503 /* 504 * Send an add or remove command to the link aggregation driver. 505 */ 506 static dladm_status_t 507 i_dladm_aggr_add_rem_sys(dladm_aggr_grp_attr_db_t *attr, int cmd) 508 { 509 int i, rc, fd, len; 510 laioc_add_rem_t *iocp; 511 laioc_port_t *ports; 512 dladm_status_t status = DLADM_STATUS_OK; 513 514 len = sizeof (*iocp) + attr->lt_nports * sizeof (laioc_port_t); 515 iocp = malloc(len); 516 if (iocp == NULL) { 517 status = DLADM_STATUS_NOMEM; 518 goto done; 519 } 520 521 iocp->la_key = attr->lt_key; 522 iocp->la_nports = attr->lt_nports; 523 ports = (laioc_port_t *)(iocp + 1); 524 525 for (i = 0; i < attr->lt_nports; i++) { 526 if (strlcpy(ports[i].lp_devname, 527 attr->lt_ports[i].lp_devname, 528 MAXNAMELEN) >= MAXNAMELEN) { 529 status = DLADM_STATUS_BADARG; 530 goto done; 531 } 532 } 533 534 if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0) { 535 status = dladm_errno2status(errno); 536 goto done; 537 } 538 539 rc = i_dladm_ioctl(fd, cmd, iocp, len); 540 if (rc < 0) { 541 if (errno == EINVAL) 542 status = DLADM_STATUS_LINKINVAL; 543 else 544 status = dladm_errno2status(errno); 545 } 546 547 (void) close(fd); 548 549 done: 550 free(iocp); 551 return (status); 552 } 553 554 /* 555 * Send a modify command to the link aggregation driver. 556 */ 557 static dladm_status_t 558 i_dladm_aggr_modify_sys(uint32_t key, uint32_t mask, 559 dladm_aggr_modify_attr_t *attr) 560 { 561 int rc, fd; 562 laioc_modify_t ioc; 563 dladm_status_t status = DLADM_STATUS_OK; 564 565 ioc.lu_key = key; 566 567 ioc.lu_modify_mask = 0; 568 if (mask & DLADM_AGGR_MODIFY_POLICY) 569 ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY; 570 if (mask & DLADM_AGGR_MODIFY_MAC) 571 ioc.lu_modify_mask |= LAIOC_MODIFY_MAC; 572 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) 573 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE; 574 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) 575 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER; 576 577 ioc.lu_policy = attr->ld_policy; 578 ioc.lu_mac_fixed = attr->ld_mac_fixed; 579 bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL); 580 ioc.lu_lacp_mode = attr->ld_lacp_mode; 581 ioc.lu_lacp_timer = attr->ld_lacp_timer; 582 583 if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0) 584 return (dladm_errno2status(errno)); 585 586 rc = i_dladm_ioctl(fd, LAIOC_MODIFY, &ioc, sizeof (ioc)); 587 if (rc < 0) { 588 if (errno == EINVAL) 589 status = DLADM_STATUS_MACADDRINVAL; 590 else 591 status = dladm_errno2status(errno); 592 } 593 594 (void) close(fd); 595 return (status); 596 } 597 598 /* 599 * Send a create command to the link aggregation driver. 600 */ 601 static dladm_status_t 602 i_dladm_aggr_create_sys(int fd, dladm_aggr_grp_attr_db_t *attr) 603 { 604 int i, rc, len; 605 laioc_create_t *iocp; 606 laioc_port_t *ports; 607 dladm_status_t status = DLADM_STATUS_OK; 608 609 len = sizeof (*iocp) + attr->lt_nports * sizeof (laioc_port_t); 610 iocp = malloc(len); 611 if (iocp == NULL) 612 return (DLADM_STATUS_NOMEM); 613 614 iocp->lc_key = attr->lt_key; 615 iocp->lc_nports = attr->lt_nports; 616 iocp->lc_policy = attr->lt_policy; 617 iocp->lc_lacp_mode = attr->lt_lacp_mode; 618 iocp->lc_lacp_timer = attr->lt_lacp_timer; 619 620 ports = (laioc_port_t *)(iocp + 1); 621 622 for (i = 0; i < attr->lt_nports; i++) { 623 if (strlcpy(ports[i].lp_devname, 624 attr->lt_ports[i].lp_devname, 625 MAXNAMELEN) >= MAXNAMELEN) { 626 free(iocp); 627 return (DLADM_STATUS_BADARG); 628 } 629 } 630 631 if (attr->lt_mac_fixed && 632 ((bcmp(zero_mac, attr->lt_mac, ETHERADDRL) == 0) || 633 (attr->lt_mac[0] & 0x01))) { 634 free(iocp); 635 return (DLADM_STATUS_MACADDRINVAL); 636 } 637 638 bcopy(attr->lt_mac, iocp->lc_mac, ETHERADDRL); 639 iocp->lc_mac_fixed = attr->lt_mac_fixed; 640 641 rc = i_dladm_ioctl(fd, LAIOC_CREATE, iocp, len); 642 if (rc < 0) 643 status = DLADM_STATUS_LINKINVAL; 644 645 free(iocp); 646 return (status); 647 } 648 649 /* 650 * Invoked to bring up a link aggregation group. 651 */ 652 static dladm_status_t 653 i_dladm_aggr_up(void *arg, dladm_aggr_grp_attr_db_t *attr) 654 { 655 dladm_aggr_up_t *up = (dladm_aggr_up_t *)arg; 656 dladm_status_t status; 657 658 if (up->lu_key != 0 && up->lu_key != attr->lt_key) 659 return (DLADM_STATUS_OK); 660 661 up->lu_found = B_TRUE; 662 663 status = i_dladm_aggr_create_sys(up->lu_fd, attr); 664 if (status != DLADM_STATUS_OK && up->lu_key != 0) 665 return (status); 666 667 return (DLADM_STATUS_OK); 668 } 669 670 /* 671 * Bring up a link aggregation group or all of them if the key is zero. 672 * If key is 0, walk may terminate early if any of the links fail 673 */ 674 dladm_status_t 675 dladm_aggr_up(uint32_t key, const char *root) 676 { 677 dladm_aggr_up_t up; 678 dladm_status_t status; 679 680 if ((up.lu_fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0) 681 return (dladm_errno2status(errno)); 682 683 up.lu_key = key; 684 up.lu_found = B_FALSE; 685 686 status = i_dladm_aggr_walk_db(i_dladm_aggr_up, &up, root); 687 if (status != DLADM_STATUS_OK) { 688 (void) close(up.lu_fd); 689 return (status); 690 } 691 (void) close(up.lu_fd); 692 693 /* 694 * only return error if user specified key and key was 695 * not found 696 */ 697 if (!up.lu_found && key != 0) 698 return (DLADM_STATUS_NOTFOUND); 699 700 return (DLADM_STATUS_OK); 701 } 702 /* 703 * Send a delete command to the link aggregation driver. 704 */ 705 static int 706 i_dladm_aggr_delete_sys(int fd, dladm_aggr_grp_attr_t *attr) 707 { 708 laioc_delete_t ioc; 709 710 ioc.ld_key = attr->lg_key; 711 712 return (i_dladm_ioctl(fd, LAIOC_DELETE, &ioc, sizeof (ioc))); 713 } 714 715 /* 716 * Invoked to bring down a link aggregation group. 717 */ 718 static int 719 i_dladm_aggr_down(void *arg, dladm_aggr_grp_attr_t *attr) 720 { 721 dladm_aggr_down_t *down = (dladm_aggr_down_t *)arg; 722 int fd, errno_save; 723 724 if (down->ld_key != 0 && down->ld_key != attr->lg_key) 725 return (0); 726 727 down->ld_found = B_TRUE; 728 729 if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0) 730 return (-1); 731 732 if (i_dladm_aggr_delete_sys(fd, attr) < 0 && down->ld_key != 0) { 733 errno_save = errno; 734 (void) close(fd); 735 errno = errno_save; 736 return (-1); 737 } 738 739 (void) close(fd); 740 return (0); 741 } 742 743 /* 744 * Bring down a link aggregation group or all of them if the key is zero. 745 * If key is 0, walk may terminate early if any of the links fail 746 */ 747 dladm_status_t 748 dladm_aggr_down(uint32_t key) 749 { 750 dladm_aggr_down_t down; 751 752 down.ld_key = key; 753 down.ld_found = B_FALSE; 754 755 if (dladm_aggr_walk(i_dladm_aggr_down, &down) < 0) 756 return (dladm_errno2status(errno)); 757 758 /* 759 * only return error if user specified key and key was 760 * not found 761 */ 762 if (!down.ld_found && key != 0) 763 return (DLADM_STATUS_NOTFOUND); 764 765 return (DLADM_STATUS_OK); 766 } 767 768 /* 769 * For each group <grp> found in the DB, invokes <fn>(<grp>, <arg>). 770 * 771 * The following values can be returned by <fn>(): 772 * 773 * -1: an error occured. This will cause the walk to be terminated, 774 * and the original DB file to be preserved. 775 * 776 * 0: success and write. The walker will write the contents of 777 * the attribute passed as argument to <fn>(), and continue walking 778 * the entries found in the DB. 779 * 780 * 1: skip. The walker should not write the contents of the current 781 * group attributes to the new DB, but should continue walking 782 * the entries found in the DB. 783 */ 784 static dladm_status_t 785 i_dladm_aggr_walk_rw_db(int (*fn)(void *, dladm_aggr_grp_attr_db_t *), 786 void *arg, const char *root) 787 { 788 FILE *fp, *nfp; 789 int nfd, fn_rc, lock_fd; 790 char line[MAXLINELEN]; 791 dladm_aggr_grp_attr_db_t attr; 792 char *db_file, *tmp_db_file; 793 char db_file_buf[MAXPATHLEN]; 794 char tmp_db_file_buf[MAXPATHLEN]; 795 dladm_status_t status; 796 797 if (root == NULL) { 798 db_file = DLADM_AGGR_DB; 799 tmp_db_file = DLADM_AGGR_DB_TMP; 800 } else { 801 (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 802 DLADM_AGGR_DB); 803 (void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root, 804 DLADM_AGGR_DB_TMP); 805 db_file = db_file_buf; 806 tmp_db_file = tmp_db_file_buf; 807 } 808 809 if ((lock_fd = i_dladm_aggr_lock_db(F_WRLCK)) < 0) 810 return (dladm_errno2status(errno)); 811 812 if ((fp = fopen(db_file, "r")) == NULL) { 813 status = dladm_errno2status(errno); 814 i_dladm_aggr_unlock_db(lock_fd); 815 return (status); 816 } 817 818 if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC, 819 DLADM_AGGR_DB_PERMS)) == -1) { 820 status = dladm_errno2status(errno); 821 (void) fclose(fp); 822 i_dladm_aggr_unlock_db(lock_fd); 823 return (status); 824 } 825 826 if ((nfp = fdopen(nfd, "w")) == NULL) { 827 status = dladm_errno2status(errno); 828 (void) close(nfd); 829 (void) fclose(fp); 830 (void) unlink(tmp_db_file); 831 i_dladm_aggr_unlock_db(lock_fd); 832 return (status); 833 } 834 835 attr.lt_ports = NULL; 836 837 while (fgets(line, MAXLINELEN, fp) != NULL) { 838 839 /* skip comments */ 840 if (BLANK_LINE(line)) { 841 if (fputs(line, nfp) == EOF) { 842 status = dladm_errno2status(errno); 843 goto failed; 844 } 845 continue; 846 } 847 848 if (i_dladm_aggr_parse_db(line, &attr) != 0) { 849 status = DLADM_STATUS_REPOSITORYINVAL; 850 goto failed; 851 } 852 853 fn_rc = fn(arg, &attr); 854 855 switch (fn_rc) { 856 case -1: 857 /* failure, stop walking */ 858 status = dladm_errno2status(errno); 859 goto failed; 860 case 0: 861 /* 862 * Success, write group attributes, which could 863 * have been modified by fn(). 864 */ 865 if (i_dladm_aggr_fput_grp(nfp, &attr) != 0) { 866 status = dladm_errno2status(errno); 867 goto failed; 868 } 869 break; 870 case 1: 871 /* skip current group */ 872 break; 873 } 874 875 free(attr.lt_ports); 876 attr.lt_ports = NULL; 877 } 878 879 if (getuid() == 0 || geteuid() == 0) { 880 if (fchmod(nfd, DLADM_AGGR_DB_PERMS) == -1) { 881 status = dladm_errno2status(errno); 882 goto failed; 883 } 884 885 if (fchown(nfd, DLADM_AGGR_DB_OWNER, 886 DLADM_AGGR_DB_GROUP) == -1) { 887 status = dladm_errno2status(errno); 888 goto failed; 889 } 890 } 891 892 if (fflush(nfp) == EOF) { 893 status = dladm_errno2status(errno); 894 goto failed; 895 } 896 897 (void) fclose(fp); 898 (void) fclose(nfp); 899 900 if (rename(tmp_db_file, db_file) == -1) { 901 status = dladm_errno2status(errno); 902 (void) unlink(tmp_db_file); 903 i_dladm_aggr_unlock_db(lock_fd); 904 return (status); 905 } 906 907 i_dladm_aggr_unlock_db(lock_fd); 908 return (DLADM_STATUS_OK); 909 910 failed: 911 free(attr.lt_ports); 912 (void) fclose(fp); 913 (void) fclose(nfp); 914 (void) unlink(tmp_db_file); 915 i_dladm_aggr_unlock_db(lock_fd); 916 917 return (status); 918 } 919 920 /* 921 * Remove an entry from the DB. 922 */ 923 static int 924 i_dladm_aggr_del_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp) 925 { 926 delete_db_state_t *state = arg; 927 928 if (grp->lt_key != state->ds_key) 929 return (0); 930 931 state->ds_found = B_TRUE; 932 933 /* don't save matching group */ 934 return (1); 935 } 936 937 static dladm_status_t 938 i_dladm_aggr_del_db(dladm_aggr_grp_attr_db_t *attr, const char *root) 939 { 940 delete_db_state_t state; 941 dladm_status_t status; 942 943 state.ds_key = attr->lt_key; 944 state.ds_found = B_FALSE; 945 946 status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_del_db_fn, &state, root); 947 if (status != DLADM_STATUS_OK) 948 return (status); 949 950 if (!state.ds_found) 951 return (DLADM_STATUS_NOTFOUND); 952 953 return (DLADM_STATUS_OK); 954 } 955 956 /* 957 * Modify the properties of an existing group in the DB. 958 */ 959 static int 960 i_dladm_aggr_modify_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp) 961 { 962 modify_db_state_t *state = arg; 963 dladm_aggr_modify_attr_t *new_attr = state->us_attr_new; 964 dladm_aggr_modify_attr_t *old_attr = state->us_attr_old; 965 966 if (grp->lt_key != state->us_key) 967 return (0); 968 969 state->us_found = B_TRUE; 970 971 if (state->us_mask & DLADM_AGGR_MODIFY_POLICY) { 972 if (old_attr != NULL) 973 old_attr->ld_policy = grp->lt_policy; 974 grp->lt_policy = new_attr->ld_policy; 975 } 976 977 if (state->us_mask & DLADM_AGGR_MODIFY_MAC) { 978 if (old_attr != NULL) { 979 old_attr->ld_mac_fixed = grp->lt_mac_fixed; 980 bcopy(grp->lt_mac, old_attr->ld_mac, ETHERADDRL); 981 } 982 grp->lt_mac_fixed = new_attr->ld_mac_fixed; 983 bcopy(new_attr->ld_mac, grp->lt_mac, ETHERADDRL); 984 } 985 986 if (state->us_mask & DLADM_AGGR_MODIFY_LACP_MODE) { 987 if (old_attr != NULL) 988 old_attr->ld_lacp_mode = grp->lt_lacp_mode; 989 grp->lt_lacp_mode = new_attr->ld_lacp_mode; 990 } 991 992 if (state->us_mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 993 if (old_attr != NULL) 994 old_attr->ld_lacp_timer = grp->lt_lacp_timer; 995 grp->lt_lacp_timer = new_attr->ld_lacp_timer; 996 } 997 998 /* save modified group */ 999 return (0); 1000 } 1001 1002 static dladm_status_t 1003 i_dladm_aggr_modify_db(uint32_t key, uint32_t mask, 1004 dladm_aggr_modify_attr_t *new, dladm_aggr_modify_attr_t *old, 1005 const char *root) 1006 { 1007 modify_db_state_t state; 1008 dladm_status_t status; 1009 1010 state.us_key = key; 1011 state.us_mask = mask; 1012 state.us_attr_new = new; 1013 state.us_attr_old = old; 1014 state.us_found = B_FALSE; 1015 1016 if ((status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_modify_db_fn, 1017 &state, root)) != DLADM_STATUS_OK) { 1018 return (status); 1019 } 1020 1021 if (!state.us_found) 1022 return (DLADM_STATUS_NOTFOUND); 1023 1024 return (DLADM_STATUS_OK); 1025 } 1026 1027 /* 1028 * Add ports to an existing group in the DB. 1029 */ 1030 static int 1031 i_dladm_aggr_add_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp) 1032 { 1033 add_db_state_t *state = arg; 1034 dladm_aggr_grp_attr_db_t *attr = state->as_attr; 1035 void *ports; 1036 int i, j; 1037 1038 if (grp->lt_key != attr->lt_key) 1039 return (0); 1040 1041 state->as_found = B_TRUE; 1042 1043 /* are any of the ports to be added already members of the group? */ 1044 for (i = 0; i < grp->lt_nports; i++) { 1045 for (j = 0; j < attr->lt_nports; j++) { 1046 if (strcmp(grp->lt_ports[i].lp_devname, 1047 attr->lt_ports[j].lp_devname) == 0) { 1048 errno = EEXIST; 1049 return (-1); 1050 } 1051 } 1052 } 1053 1054 /* add groups specified by attr to grp */ 1055 ports = realloc(grp->lt_ports, (grp->lt_nports + 1056 attr->lt_nports) * sizeof (dladm_aggr_port_attr_db_t)); 1057 if (ports == NULL) 1058 return (-1); 1059 grp->lt_ports = ports; 1060 1061 for (i = 0; i < attr->lt_nports; i++) { 1062 if (strlcpy(grp->lt_ports[grp->lt_nports + i].lp_devname, 1063 attr->lt_ports[i].lp_devname, MAXNAMELEN + 1) >= 1064 MAXNAMELEN + 1) 1065 return (-1); 1066 } 1067 1068 grp->lt_nports += attr->lt_nports; 1069 1070 /* save modified group */ 1071 return (0); 1072 } 1073 1074 static dladm_status_t 1075 i_dladm_aggr_add_db(dladm_aggr_grp_attr_db_t *attr, const char *root) 1076 { 1077 add_db_state_t state; 1078 dladm_status_t status; 1079 1080 state.as_attr = attr; 1081 state.as_found = B_FALSE; 1082 1083 status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_add_db_fn, &state, root); 1084 if (status != DLADM_STATUS_OK) 1085 return (status); 1086 1087 if (!state.as_found) 1088 return (DLADM_STATUS_NOTFOUND); 1089 1090 return (DLADM_STATUS_OK); 1091 } 1092 1093 /* 1094 * Remove ports from an existing group in the DB. 1095 */ 1096 1097 typedef struct remove_db_state { 1098 dladm_aggr_grp_attr_db_t *rs_attr; 1099 boolean_t rs_found; 1100 } remove_db_state_t; 1101 1102 static int 1103 i_dladm_aggr_remove_db_fn(void *arg, dladm_aggr_grp_attr_db_t *grp) 1104 { 1105 remove_db_state_t *state = (remove_db_state_t *)arg; 1106 dladm_aggr_grp_attr_db_t *attr = state->rs_attr; 1107 int i, j, k, nremoved; 1108 boolean_t match; 1109 1110 if (grp->lt_key != attr->lt_key) 1111 return (0); 1112 1113 state->rs_found = B_TRUE; 1114 1115 /* remove the ports specified by attr from the group */ 1116 nremoved = 0; 1117 k = 0; 1118 for (i = 0; i < grp->lt_nports; i++) { 1119 match = B_FALSE; 1120 for (j = 0; j < attr->lt_nports && !match; j++) { 1121 match = (strcmp(grp->lt_ports[i].lp_devname, 1122 attr->lt_ports[j].lp_devname) == 0); 1123 } 1124 if (match) 1125 nremoved++; 1126 else 1127 grp->lt_ports[k++] = grp->lt_ports[i]; 1128 } 1129 1130 if (nremoved != attr->lt_nports) { 1131 errno = ENOENT; 1132 return (-1); 1133 } 1134 1135 grp->lt_nports -= nremoved; 1136 1137 /* save modified group */ 1138 return (0); 1139 } 1140 1141 static dladm_status_t 1142 i_dladm_aggr_remove_db(dladm_aggr_grp_attr_db_t *attr, const char *root) 1143 { 1144 remove_db_state_t state; 1145 dladm_status_t status; 1146 1147 state.rs_attr = attr; 1148 state.rs_found = B_FALSE; 1149 1150 status = i_dladm_aggr_walk_rw_db(i_dladm_aggr_remove_db_fn, 1151 &state, root); 1152 if (status != DLADM_STATUS_OK) 1153 return (status); 1154 1155 if (!state.rs_found) 1156 return (DLADM_STATUS_NOTFOUND); 1157 1158 return (DLADM_STATUS_OK); 1159 } 1160 1161 /* 1162 * Given a policy string, return a policy mask. Returns B_TRUE on 1163 * success, or B_FALSE if an error occured during parsing. 1164 */ 1165 boolean_t 1166 dladm_aggr_str2policy(const char *str, uint32_t *policy) 1167 { 1168 int i; 1169 policy_t *pol; 1170 char *token = NULL; 1171 char *lasts; 1172 1173 *policy = 0; 1174 1175 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",", 1176 &lasts)) != NULL) { 1177 for (i = 0; i < NPOLICIES; i++) { 1178 pol = &policies[i]; 1179 if (strcasecmp(token, pol->pol_name) == 0) { 1180 *policy |= pol->policy; 1181 break; 1182 } 1183 } 1184 if (i == NPOLICIES) 1185 return (B_FALSE); 1186 } 1187 1188 return (B_TRUE); 1189 } 1190 1191 /* 1192 * Given a policy mask, returns a printable string, or NULL if the 1193 * policy mask is invalid. It is the responsibility of the caller to 1194 * free the returned string after use. 1195 */ 1196 char * 1197 dladm_aggr_policy2str(uint32_t policy, char *str) 1198 { 1199 int i, npolicies = 0; 1200 policy_t *pol; 1201 1202 str[0] = '\0'; 1203 1204 for (i = 0; i < NPOLICIES; i++) { 1205 pol = &policies[i]; 1206 if ((policy & pol->policy) != 0) { 1207 npolicies++; 1208 if (npolicies > 1) 1209 (void) strcat(str, ","); 1210 (void) strcat(str, pol->pol_name); 1211 } 1212 } 1213 1214 return (str); 1215 } 1216 1217 /* 1218 * Given a MAC address string, return the MAC address in the mac_addr 1219 * array. If the MAC address was not explicitly specified, i.e. is 1220 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE. 1221 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise. 1222 */ 1223 boolean_t 1224 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr) 1225 { 1226 uchar_t *conv_str; 1227 int mac_len; 1228 1229 *mac_fixed = (strcmp(str, "auto") != 0); 1230 if (!*mac_fixed) { 1231 bzero(mac_addr, ETHERADDRL); 1232 return (B_TRUE); 1233 } 1234 1235 conv_str = _link_aton(str, &mac_len); 1236 if (conv_str == NULL) 1237 return (B_FALSE); 1238 1239 if (mac_len != ETHERADDRL) { 1240 free(conv_str); 1241 return (B_FALSE); 1242 } 1243 1244 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) || 1245 (conv_str[0] & 0x01)) { 1246 free(conv_str); 1247 return (B_FALSE); 1248 } 1249 1250 bcopy(conv_str, mac_addr, ETHERADDRL); 1251 free(conv_str); 1252 1253 return (B_TRUE); 1254 } 1255 1256 /* 1257 * Returns a string containing a printable representation of a MAC address. 1258 */ 1259 const char * 1260 dladm_aggr_macaddr2str(unsigned char *mac, char *buf) 1261 { 1262 static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 1263 1264 if (buf == NULL) 1265 return (NULL); 1266 1267 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 1268 return (gettext("<unknown>")); 1269 else 1270 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 1271 } 1272 1273 /* 1274 * Given a LACP mode string, find the corresponding LACP mode number. Returns 1275 * B_TRUE if a match was found, B_FALSE otherwise. 1276 */ 1277 boolean_t 1278 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode) 1279 { 1280 int i; 1281 dladm_aggr_lacpmode_t *mode; 1282 1283 for (i = 0; i < NLACP_MODES; i++) { 1284 mode = &lacp_modes[i]; 1285 if (strncasecmp(str, mode->mode_str, 1286 strlen(mode->mode_str)) == 0) { 1287 *lacp_mode = mode->mode_id; 1288 return (B_TRUE); 1289 } 1290 } 1291 1292 return (B_FALSE); 1293 } 1294 1295 /* 1296 * Given a LACP mode number, returns a printable string, or NULL if the 1297 * LACP mode number is invalid. 1298 */ 1299 const char * 1300 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf) 1301 { 1302 int i; 1303 dladm_aggr_lacpmode_t *mode; 1304 1305 for (i = 0; i < NLACP_MODES; i++) { 1306 mode = &lacp_modes[i]; 1307 if (mode->mode_id == mode_id) { 1308 (void) snprintf(buf, DLADM_STRSIZE, "%s", 1309 mode->mode_str); 1310 return (buf); 1311 } 1312 } 1313 1314 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 1315 return (buf); 1316 } 1317 1318 /* 1319 * Given a LACP timer string, find the corresponding LACP timer number. Returns 1320 * B_TRUE if a match was found, B_FALSE otherwise. 1321 */ 1322 boolean_t 1323 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer) 1324 { 1325 int i; 1326 dladm_aggr_lacptimer_t *timer; 1327 1328 for (i = 0; i < NLACP_TIMERS; i++) { 1329 timer = &lacp_timers[i]; 1330 if (strncasecmp(str, timer->lt_str, 1331 strlen(timer->lt_str)) == 0) { 1332 *lacp_timer = timer->lt_id; 1333 return (B_TRUE); 1334 } 1335 } 1336 1337 return (B_FALSE); 1338 } 1339 1340 /* 1341 * Given a LACP timer, returns a printable string, or NULL if the 1342 * LACP timer number is invalid. 1343 */ 1344 const char * 1345 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf) 1346 { 1347 int i; 1348 dladm_aggr_lacptimer_t *timer; 1349 1350 for (i = 0; i < NLACP_TIMERS; i++) { 1351 timer = &lacp_timers[i]; 1352 if (timer->lt_id == timer_id) { 1353 (void) snprintf(buf, DLADM_STRSIZE, "%s", 1354 timer->lt_str); 1355 return (buf); 1356 } 1357 } 1358 1359 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 1360 return (buf); 1361 } 1362 1363 const char * 1364 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf) 1365 { 1366 int i; 1367 dladm_aggr_port_state_t *state; 1368 1369 for (i = 0; i < NPORT_STATES; i++) { 1370 state = &port_states[i]; 1371 if (state->state_id == state_id) { 1372 (void) snprintf(buf, DLADM_STRSIZE, "%s", 1373 state->state_str); 1374 return (buf); 1375 } 1376 } 1377 1378 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 1379 return (buf); 1380 } 1381 1382 #define FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1); 1383 1384 /* 1385 * Write the attribute of a group to the specified file. Returns 0 on 1386 * success, -1 on failure. 1387 */ 1388 static int 1389 i_dladm_aggr_fput_grp(FILE *fp, dladm_aggr_grp_attr_db_t *attr) 1390 { 1391 int i; 1392 char addr_str[ETHERADDRL * 3]; 1393 char buf[DLADM_STRSIZE]; 1394 1395 /* key, policy */ 1396 FPRINTF_ERR(fprintf(fp, "%d\t%s\t", attr->lt_key, 1397 dladm_aggr_policy2str(attr->lt_policy, buf))); 1398 1399 /* number of ports, ports */ 1400 FPRINTF_ERR(fprintf(fp, "%d\t", attr->lt_nports)); 1401 for (i = 0; i < attr->lt_nports; i++) { 1402 if (i > 0) 1403 FPRINTF_ERR(fprintf(fp, ",")); 1404 FPRINTF_ERR(fprintf(fp, "%s", attr->lt_ports[i].lp_devname)); 1405 } 1406 FPRINTF_ERR(fprintf(fp, "\t")); 1407 1408 /* MAC address */ 1409 if (!attr->lt_mac_fixed) { 1410 FPRINTF_ERR(fprintf(fp, "auto")); 1411 } else { 1412 FPRINTF_ERR(fprintf(fp, "%s", 1413 dladm_aggr_macaddr2str(attr->lt_mac, addr_str))); 1414 } 1415 FPRINTF_ERR(fprintf(fp, "\t")); 1416 1417 FPRINTF_ERR(fprintf(fp, "%s\t", 1418 dladm_aggr_lacpmode2str(attr->lt_lacp_mode, buf))); 1419 1420 FPRINTF_ERR(fprintf(fp, "%s\n", 1421 dladm_aggr_lacptimer2str(attr->lt_lacp_timer, buf))); 1422 1423 return (0); 1424 } 1425 1426 static dladm_status_t 1427 i_dladm_aggr_create_db(dladm_aggr_grp_attr_db_t *attr, const char *root) 1428 { 1429 FILE *fp; 1430 char line[MAXLINELEN]; 1431 uint32_t key; 1432 int lock_fd; 1433 char *db_file; 1434 char db_file_buf[MAXPATHLEN]; 1435 char *endp = NULL; 1436 dladm_status_t status; 1437 1438 if (root == NULL) { 1439 db_file = DLADM_AGGR_DB; 1440 } else { 1441 (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 1442 DLADM_AGGR_DB); 1443 db_file = db_file_buf; 1444 } 1445 1446 if ((lock_fd = i_dladm_aggr_lock_db(F_WRLCK)) < 0) 1447 return (dladm_errno2status(errno)); 1448 1449 if ((fp = fopen(db_file, "r+")) == NULL && 1450 (fp = fopen(db_file, "w")) == NULL) { 1451 status = dladm_errno2status(errno); 1452 i_dladm_aggr_unlock_db(lock_fd); 1453 return (status); 1454 } 1455 1456 /* look for existing group with same key */ 1457 while (fgets(line, MAXLINELEN, fp) != NULL) { 1458 char *holder, *lasts; 1459 1460 /* skip comments */ 1461 if (BLANK_LINE(line)) 1462 continue; 1463 1464 /* ignore corrupted lines */ 1465 holder = strtok_r(line, " \t", &lasts); 1466 if (holder == NULL) 1467 continue; 1468 1469 /* port number */ 1470 errno = 0; 1471 key = (int)strtol(holder, &endp, 10); 1472 if (errno != 0 || *endp != '\0') { 1473 status = DLADM_STATUS_REPOSITORYINVAL; 1474 goto done; 1475 } 1476 1477 if (key == attr->lt_key) { 1478 /* group with key already exists */ 1479 status = DLADM_STATUS_EXIST; 1480 goto done; 1481 } 1482 } 1483 1484 /* 1485 * If we get here, we've verified that no existing group with 1486 * the same key already exists. It's now time to add the 1487 * new group to the DB. 1488 */ 1489 if (i_dladm_aggr_fput_grp(fp, attr) != 0) { 1490 status = dladm_errno2status(errno); 1491 goto done; 1492 } 1493 1494 status = DLADM_STATUS_OK; 1495 1496 done: 1497 (void) fclose(fp); 1498 i_dladm_aggr_unlock_db(lock_fd); 1499 return (status); 1500 } 1501 1502 /* 1503 * Create a new link aggregation group. Update the configuration 1504 * file and bring it up. 1505 */ 1506 dladm_status_t 1507 dladm_aggr_create(uint32_t key, uint32_t nports, 1508 dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed, 1509 uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, 1510 boolean_t tempop, const char *root) 1511 { 1512 dladm_aggr_grp_attr_db_t attr; 1513 dladm_status_t status; 1514 1515 if (key == 0 || key > DLADM_AGGR_MAX_KEY) 1516 return (DLADM_STATUS_KEYINVAL); 1517 1518 attr.lt_key = key; 1519 attr.lt_nports = nports; 1520 attr.lt_ports = ports; 1521 attr.lt_policy = policy; 1522 attr.lt_mac_fixed = mac_addr_fixed; 1523 if (attr.lt_mac_fixed) 1524 bcopy(mac_addr, attr.lt_mac, ETHERADDRL); 1525 else 1526 bzero(attr.lt_mac, ETHERADDRL); 1527 attr.lt_lacp_mode = lacp_mode; 1528 attr.lt_lacp_timer = lacp_timer; 1529 1530 /* add the link aggregation group to the DB */ 1531 if (!tempop) { 1532 status = i_dladm_aggr_create_db(&attr, root); 1533 if (status != DLADM_STATUS_OK) 1534 return (status); 1535 } else { 1536 dladm_aggr_up_t up; 1537 1538 up.lu_key = key; 1539 up.lu_found = B_FALSE; 1540 up.lu_fd = open(DLADM_AGGR_DEV, O_RDWR); 1541 if (up.lu_fd < 0) 1542 return (dladm_errno2status(errno)); 1543 1544 status = i_dladm_aggr_up((void *)&up, &attr); 1545 (void) close(up.lu_fd); 1546 return (status); 1547 } 1548 1549 /* bring up the link aggregation group */ 1550 status = dladm_aggr_up(key, root); 1551 /* 1552 * If the operation fails because the aggregation already exists, 1553 * then only update the persistent configuration repository and 1554 * return success. 1555 */ 1556 if (status == DLADM_STATUS_EXIST) 1557 status = DLADM_STATUS_OK; 1558 1559 if (status != DLADM_STATUS_OK && !tempop) 1560 (void) i_dladm_aggr_del_db(&attr, root); 1561 1562 return (status); 1563 } 1564 1565 /* 1566 * Modify the parameters of an existing link aggregation group. Update 1567 * the configuration file and pass the changes to the kernel. 1568 */ 1569 dladm_status_t 1570 dladm_aggr_modify(uint32_t key, uint32_t modify_mask, uint32_t policy, 1571 boolean_t mac_fixed, uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1572 aggr_lacp_timer_t lacp_timer, boolean_t tempop, const char *root) 1573 { 1574 dladm_aggr_modify_attr_t new_attr, old_attr; 1575 dladm_status_t status; 1576 1577 if (key == 0) 1578 return (DLADM_STATUS_KEYINVAL); 1579 1580 if (modify_mask & DLADM_AGGR_MODIFY_POLICY) 1581 new_attr.ld_policy = policy; 1582 1583 if (modify_mask & DLADM_AGGR_MODIFY_MAC) { 1584 new_attr.ld_mac_fixed = mac_fixed; 1585 bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL); 1586 } 1587 1588 if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE) 1589 new_attr.ld_lacp_mode = lacp_mode; 1590 1591 if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER) 1592 new_attr.ld_lacp_timer = lacp_timer; 1593 1594 /* update the DB */ 1595 if (!tempop && ((status = i_dladm_aggr_modify_db(key, modify_mask, 1596 &new_attr, &old_attr, root)) != DLADM_STATUS_OK)) { 1597 return (status); 1598 } 1599 1600 status = i_dladm_aggr_modify_sys(key, modify_mask, &new_attr); 1601 if (status != DLADM_STATUS_OK && !tempop) { 1602 (void) i_dladm_aggr_modify_db(key, modify_mask, &old_attr, 1603 NULL, root); 1604 } 1605 1606 return (status); 1607 } 1608 1609 /* 1610 * Delete a previously created link aggregation group. 1611 */ 1612 dladm_status_t 1613 dladm_aggr_delete(uint32_t key, boolean_t tempop, const char *root) 1614 { 1615 dladm_aggr_grp_attr_db_t db_attr; 1616 dladm_status_t status; 1617 1618 if (key == 0) 1619 return (DLADM_STATUS_KEYINVAL); 1620 1621 if (tempop) { 1622 dladm_aggr_down_t down; 1623 dladm_aggr_grp_attr_t sys_attr; 1624 1625 down.ld_key = key; 1626 down.ld_found = B_FALSE; 1627 sys_attr.lg_key = key; 1628 if (i_dladm_aggr_down((void *)&down, &sys_attr) < 0) 1629 return (dladm_errno2status(errno)); 1630 else 1631 return (DLADM_STATUS_OK); 1632 } else { 1633 status = dladm_aggr_down(key); 1634 1635 /* 1636 * Only continue to delete the configuration repository 1637 * either if we successfully delete the active aggregation 1638 * or if the aggregation is not found. 1639 */ 1640 if (status != DLADM_STATUS_OK && 1641 status != DLADM_STATUS_NOTFOUND) { 1642 return (status); 1643 } 1644 } 1645 1646 if (tempop) 1647 return (DLADM_STATUS_OK); 1648 1649 db_attr.lt_key = key; 1650 return (i_dladm_aggr_del_db(&db_attr, root)); 1651 } 1652 1653 /* 1654 * Add one or more ports to an existing link aggregation. 1655 */ 1656 dladm_status_t 1657 dladm_aggr_add(uint32_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports, 1658 boolean_t tempop, const char *root) 1659 { 1660 dladm_aggr_grp_attr_db_t attr; 1661 dladm_status_t status; 1662 1663 if (key == 0) 1664 return (DLADM_STATUS_KEYINVAL); 1665 1666 bzero(&attr, sizeof (attr)); 1667 attr.lt_key = key; 1668 attr.lt_nports = nports; 1669 attr.lt_ports = ports; 1670 1671 if (!tempop && 1672 ((status = i_dladm_aggr_add_db(&attr, root)) != DLADM_STATUS_OK)) { 1673 return (status); 1674 } 1675 1676 status = i_dladm_aggr_add_rem_sys(&attr, LAIOC_ADD); 1677 if (status != DLADM_STATUS_OK && !tempop) 1678 (void) i_dladm_aggr_remove_db(&attr, root); 1679 1680 return (status); 1681 } 1682 1683 /* 1684 * Remove one or more ports from an existing link aggregation. 1685 */ 1686 dladm_status_t 1687 dladm_aggr_remove(uint32_t key, uint32_t nports, 1688 dladm_aggr_port_attr_db_t *ports, boolean_t tempop, const char *root) 1689 { 1690 dladm_aggr_grp_attr_db_t attr; 1691 dladm_status_t status; 1692 1693 if (key == 0) 1694 return (DLADM_STATUS_KEYINVAL); 1695 1696 bzero(&attr, sizeof (attr)); 1697 attr.lt_key = key; 1698 attr.lt_nports = nports; 1699 attr.lt_ports = ports; 1700 1701 if (!tempop && 1702 ((status = i_dladm_aggr_remove_db(&attr, root)) != 1703 DLADM_STATUS_OK)) { 1704 return (status); 1705 } 1706 1707 status = i_dladm_aggr_add_rem_sys(&attr, LAIOC_REMOVE); 1708 if (status != DLADM_STATUS_OK && !tempop) 1709 (void) i_dladm_aggr_add_db(&attr, root); 1710 1711 return (status); 1712 } 1713