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