1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * IEEE 802.3ad Link Aggregation -- IOCTL processing. 30 */ 31 32 #include <sys/ddi.h> 33 #include <sys/aggr.h> 34 #include <sys/aggr_impl.h> 35 #include <sys/strsun.h> 36 37 static int aggr_ioc_create(mblk_t *, int); 38 static int aggr_ioc_delete(mblk_t *, int); 39 static int aggr_ioc_info(mblk_t *, int); 40 static int aggr_ioc_add(mblk_t *, int); 41 static int aggr_ioc_remove(mblk_t *, int); 42 static int aggr_ioc_status(mblk_t *, int); 43 static int aggr_ioc_modify(mblk_t *, int); 44 45 typedef struct ioc_cmd_s { 46 int ic_cmd; 47 int (*ic_func)(mblk_t *, int); 48 } ioc_cmd_t; 49 50 static ioc_cmd_t ioc_cmd[] = { 51 {LAIOC_CREATE, aggr_ioc_create}, 52 {LAIOC_DELETE, aggr_ioc_delete}, 53 {LAIOC_INFO, aggr_ioc_info}, 54 {LAIOC_ADD, aggr_ioc_add}, 55 {LAIOC_REMOVE, aggr_ioc_remove}, 56 {LAIOC_MODIFY, aggr_ioc_modify}}; 57 58 #define IOC_CMD_SZ (sizeof (ioc_cmd) / sizeof (ioc_cmd_t)) 59 60 /* 61 * Process a LAIOC_MODIFY request. 62 */ 63 static int 64 aggr_ioc_modify(mblk_t *mp, int mode) 65 { 66 STRUCT_HANDLE(laioc_modify, modify_arg); 67 uint32_t policy; 68 boolean_t mac_fixed; 69 uchar_t mac_addr[ETHERADDRL]; 70 uint8_t modify_mask_arg, modify_mask = 0; 71 uint32_t rc, key; 72 aggr_lacp_mode_t lacp_mode; 73 aggr_lacp_timer_t lacp_timer; 74 75 STRUCT_SET_HANDLE(modify_arg, mode, (void *)mp->b_cont->b_rptr); 76 if (MBLKL(mp->b_cont) < STRUCT_SIZE(modify_arg)) 77 return (EINVAL); 78 79 key = STRUCT_FGET(modify_arg, lu_key); 80 modify_mask_arg = STRUCT_FGET(modify_arg, lu_modify_mask); 81 82 if (modify_mask_arg & LAIOC_MODIFY_POLICY) { 83 modify_mask |= AGGR_MODIFY_POLICY; 84 policy = STRUCT_FGET(modify_arg, lu_policy); 85 } 86 87 if (modify_mask_arg & LAIOC_MODIFY_MAC) { 88 modify_mask |= AGGR_MODIFY_MAC; 89 bcopy(STRUCT_FGET(modify_arg, lu_mac), mac_addr, ETHERADDRL); 90 mac_fixed = STRUCT_FGET(modify_arg, lu_mac_fixed); 91 } 92 93 if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) { 94 modify_mask |= AGGR_MODIFY_LACP_MODE; 95 lacp_mode = STRUCT_FGET(modify_arg, lu_lacp_mode); 96 } 97 98 if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) { 99 modify_mask |= AGGR_MODIFY_LACP_TIMER; 100 lacp_timer = STRUCT_FGET(modify_arg, lu_lacp_timer); 101 } 102 103 rc = aggr_grp_modify(key, NULL, modify_mask, policy, mac_fixed, 104 mac_addr, lacp_mode, lacp_timer); 105 106 freemsg(mp->b_cont); 107 mp->b_cont = NULL; 108 return (rc); 109 } 110 111 /* 112 * Process a LAIOC_CREATE request. 113 */ 114 static int 115 aggr_ioc_create(mblk_t *mp, int mode) 116 { 117 STRUCT_HANDLE(laioc_create, create_arg); 118 uint16_t nports; 119 laioc_port_t *ports = NULL; 120 uint32_t policy; 121 boolean_t mac_fixed; 122 uchar_t mac_addr[ETHERADDRL]; 123 aggr_lacp_mode_t lacp_mode; 124 aggr_lacp_timer_t lacp_timer; 125 int rc, len; 126 127 STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr); 128 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(create_arg)) 129 return (EINVAL); 130 131 nports = STRUCT_FGET(create_arg, lc_nports); 132 if (nports > AGGR_MAX_PORTS) 133 return (EINVAL); 134 135 policy = STRUCT_FGET(create_arg, lc_policy); 136 lacp_mode = STRUCT_FGET(create_arg, lc_lacp_mode); 137 lacp_timer = STRUCT_FGET(create_arg, lc_lacp_timer); 138 139 if (len < STRUCT_SIZE(create_arg) + (nports * sizeof (laioc_port_t))) 140 return (EINVAL); 141 142 ports = (laioc_port_t *)(STRUCT_BUF(create_arg) + 1); 143 144 bcopy(STRUCT_FGET(create_arg, lc_mac), mac_addr, ETHERADDRL); 145 mac_fixed = STRUCT_FGET(create_arg, lc_mac_fixed); 146 147 rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_key), 148 nports, ports, policy, mac_fixed, mac_addr, lacp_mode, lacp_timer); 149 150 freemsg(mp->b_cont); 151 mp->b_cont = NULL; 152 return (rc); 153 } 154 155 static int 156 aggr_ioc_delete(mblk_t *mp, int mode) 157 { 158 STRUCT_HANDLE(laioc_delete, delete_arg); 159 int rc; 160 161 STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr); 162 if (STRUCT_SIZE(delete_arg) > MBLKL(mp)) 163 return (EINVAL); 164 165 rc = aggr_grp_delete(STRUCT_FGET(delete_arg, ld_key)); 166 167 freemsg(mp->b_cont); 168 mp->b_cont = NULL; 169 return (rc); 170 } 171 172 typedef struct aggr_ioc_info_state { 173 uint32_t bytes_left; 174 uchar_t *where; 175 } aggr_ioc_info_state_t; 176 177 static int 178 aggr_ioc_info_new_grp(void *arg, uint32_t key, uchar_t *mac, 179 boolean_t mac_fixed, uint32_t policy, uint32_t nports, 180 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 181 { 182 aggr_ioc_info_state_t *state = arg; 183 laioc_info_group_t grp; 184 185 if (state->bytes_left < sizeof (grp)) 186 return (ENOSPC); 187 188 grp.lg_key = key; 189 bcopy(mac, grp.lg_mac, ETHERADDRL); 190 grp.lg_mac_fixed = mac_fixed; 191 grp.lg_policy = policy; 192 grp.lg_nports = nports; 193 grp.lg_lacp_mode = lacp_mode; 194 grp.lg_lacp_timer = lacp_timer; 195 196 bcopy(&grp, state->where, sizeof (grp)); 197 state->where += sizeof (grp); 198 state->bytes_left -= sizeof (grp); 199 200 return (0); 201 } 202 203 static int 204 aggr_ioc_info_new_port(void *arg, char *devname, uchar_t *mac, 205 aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state) 206 { 207 aggr_ioc_info_state_t *state = arg; 208 laioc_info_port_t port; 209 210 if (state->bytes_left < sizeof (port)) 211 return (ENOSPC); 212 213 bcopy(devname, port.lp_devname, MAXNAMELEN + 1); 214 bcopy(mac, port.lp_mac, ETHERADDRL); 215 port.lp_state = portstate; 216 port.lp_lacp_state = *lacp_state; 217 218 bcopy(&port, state->where, sizeof (port)); 219 state->where += sizeof (port); 220 state->bytes_left -= sizeof (port); 221 222 return (0); 223 } 224 225 /*ARGSUSED*/ 226 static int 227 aggr_ioc_info(mblk_t *mp, int mode) 228 { 229 laioc_info_t *info_argp; 230 uint32_t ngroups, group_key; 231 int rc, len; 232 aggr_ioc_info_state_t state; 233 234 if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp)) 235 return (EINVAL); 236 237 info_argp = (laioc_info_t *)mp->b_cont->b_rptr; 238 /* 239 * Key of the group to return. If zero, the call returns information 240 * regarding all groups currently defined. 241 */ 242 group_key = info_argp->li_group_key; 243 244 state.bytes_left = len - sizeof (laioc_info_t); 245 state.where = (uchar_t *)(info_argp + 1); 246 247 rc = aggr_grp_info(&ngroups, group_key, &state, aggr_ioc_info_new_grp, 248 aggr_ioc_info_new_port); 249 if (rc == 0) 250 info_argp->li_ngroups = ngroups; 251 252 return (rc); 253 } 254 255 static int 256 aggr_ioc_add(mblk_t *mp, int mode) 257 { 258 STRUCT_HANDLE(laioc_add_rem, add_arg); 259 uint32_t nports; 260 laioc_port_t *ports = NULL; 261 int rc, len; 262 263 STRUCT_SET_HANDLE(add_arg, mode, (void *)mp->b_cont->b_rptr); 264 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(add_arg)) 265 return (EINVAL); 266 267 nports = STRUCT_FGET(add_arg, la_nports); 268 if (nports > AGGR_MAX_PORTS) 269 return (EINVAL); 270 271 if (len < STRUCT_SIZE(add_arg) + (nports * sizeof (laioc_port_t))) 272 return (EINVAL); 273 274 ports = (laioc_port_t *)(STRUCT_BUF(add_arg) + 1); 275 276 rc = aggr_grp_add_ports(STRUCT_FGET(add_arg, la_key), 277 nports, ports); 278 279 freemsg(mp->b_cont); 280 mp->b_cont = NULL; 281 return (rc); 282 } 283 284 static int 285 aggr_ioc_remove(mblk_t *mp, int mode) 286 { 287 STRUCT_HANDLE(laioc_add_rem, rem_arg); 288 uint32_t nports; 289 laioc_port_t *ports = NULL; 290 int rc, len; 291 292 STRUCT_SET_HANDLE(rem_arg, mode, (void *)mp->b_cont->b_rptr); 293 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(rem_arg)) 294 return (EINVAL); 295 296 nports = STRUCT_FGET(rem_arg, la_nports); 297 if (nports > AGGR_MAX_PORTS) 298 return (EINVAL); 299 300 if (len < STRUCT_SIZE(rem_arg) + (nports * sizeof (laioc_port_t))) 301 return (EINVAL); 302 303 ports = (laioc_port_t *)(STRUCT_BUF(rem_arg) + 1); 304 305 rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_key), 306 nports, ports); 307 308 freemsg(mp->b_cont); 309 mp->b_cont = NULL; 310 return (rc); 311 } 312 313 void 314 aggr_ioctl(queue_t *wq, mblk_t *mp) 315 { 316 struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 317 int i, err = EINVAL; 318 mblk_t *nmp; 319 320 if (mp->b_cont == NULL) 321 goto done; 322 323 /* 324 * Construct contiguous message 325 */ 326 if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) { 327 err = ENOMEM; 328 goto done; 329 } 330 331 freemsg(mp->b_cont); 332 mp->b_cont = nmp; 333 334 for (i = 0; i < IOC_CMD_SZ; i++) { 335 if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) { 336 err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag); 337 break; 338 } 339 } 340 341 if (err == 0) { 342 int len = 0; 343 344 if (mp->b_cont != NULL) { 345 len = MBLKL(mp->b_cont); 346 } 347 miocack(wq, mp, len, 0); 348 return; 349 } 350 351 done: 352 miocnak(wq, mp, 0, err); 353 } 354