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