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