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