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 2008 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 datalink_id_t linkid; 72 uint32_t rc; 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 linkid = STRUCT_FGET(modify_arg, lu_linkid); 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(linkid, 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 boolean_t force; 124 uchar_t mac_addr[ETHERADDRL]; 125 aggr_lacp_mode_t lacp_mode; 126 aggr_lacp_timer_t lacp_timer; 127 int rc, len; 128 129 STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr); 130 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(create_arg)) 131 return (EINVAL); 132 133 nports = STRUCT_FGET(create_arg, lc_nports); 134 if (nports > AGGR_MAX_PORTS) 135 return (EINVAL); 136 137 policy = STRUCT_FGET(create_arg, lc_policy); 138 lacp_mode = STRUCT_FGET(create_arg, lc_lacp_mode); 139 lacp_timer = STRUCT_FGET(create_arg, lc_lacp_timer); 140 141 if (len < STRUCT_SIZE(create_arg) + (nports * sizeof (laioc_port_t))) 142 return (EINVAL); 143 144 ports = (laioc_port_t *)(STRUCT_BUF(create_arg) + 1); 145 146 bcopy(STRUCT_FGET(create_arg, lc_mac), mac_addr, ETHERADDRL); 147 mac_fixed = STRUCT_FGET(create_arg, lc_mac_fixed); 148 force = STRUCT_FGET(create_arg, lc_force); 149 150 rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_linkid), 151 STRUCT_FGET(create_arg, lc_key), nports, ports, policy, 152 mac_fixed, force, mac_addr, lacp_mode, lacp_timer); 153 154 freemsg(mp->b_cont); 155 mp->b_cont = NULL; 156 return (rc); 157 } 158 159 static int 160 aggr_ioc_delete(mblk_t *mp, int mode) 161 { 162 STRUCT_HANDLE(laioc_delete, delete_arg); 163 int rc; 164 165 STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr); 166 if (STRUCT_SIZE(delete_arg) > MBLKL(mp)) 167 return (EINVAL); 168 169 rc = aggr_grp_delete(STRUCT_FGET(delete_arg, ld_linkid)); 170 171 freemsg(mp->b_cont); 172 mp->b_cont = NULL; 173 return (rc); 174 } 175 176 typedef struct aggr_ioc_info_state { 177 uint32_t bytes_left; 178 uchar_t *where; 179 } aggr_ioc_info_state_t; 180 181 static int 182 aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key, 183 uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy, 184 uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 185 { 186 aggr_ioc_info_state_t *state = arg; 187 laioc_info_group_t grp; 188 189 if (state->bytes_left < sizeof (grp)) 190 return (ENOSPC); 191 192 grp.lg_linkid = linkid; 193 grp.lg_key = key; 194 bcopy(mac, grp.lg_mac, ETHERADDRL); 195 grp.lg_mac_fixed = mac_fixed; 196 grp.lg_force = force; 197 grp.lg_policy = policy; 198 grp.lg_nports = nports; 199 grp.lg_lacp_mode = lacp_mode; 200 grp.lg_lacp_timer = lacp_timer; 201 202 bcopy(&grp, state->where, sizeof (grp)); 203 state->where += sizeof (grp); 204 state->bytes_left -= sizeof (grp); 205 206 return (0); 207 } 208 209 static int 210 aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac, 211 aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state) 212 { 213 aggr_ioc_info_state_t *state = arg; 214 laioc_info_port_t port; 215 216 if (state->bytes_left < sizeof (port)) 217 return (ENOSPC); 218 219 port.lp_linkid = linkid; 220 bcopy(mac, port.lp_mac, ETHERADDRL); 221 port.lp_state = portstate; 222 port.lp_lacp_state = *lacp_state; 223 224 bcopy(&port, state->where, sizeof (port)); 225 state->where += sizeof (port); 226 state->bytes_left -= sizeof (port); 227 228 return (0); 229 } 230 231 /*ARGSUSED*/ 232 static int 233 aggr_ioc_info(mblk_t *mp, int mode) 234 { 235 laioc_info_t *info_argp; 236 datalink_id_t linkid; 237 int rc, len; 238 aggr_ioc_info_state_t state; 239 240 if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp)) 241 return (EINVAL); 242 243 info_argp = (laioc_info_t *)mp->b_cont->b_rptr; 244 245 /* 246 * linkid of the group to return. Must not be DATALINK_INVALID_LINKID. 247 */ 248 if ((linkid = info_argp->li_group_linkid) == DATALINK_INVALID_LINKID) 249 return (EINVAL); 250 251 state.bytes_left = len - sizeof (laioc_info_t); 252 state.where = (uchar_t *)(info_argp + 1); 253 254 rc = aggr_grp_info(linkid, &state, 255 aggr_ioc_info_new_grp, aggr_ioc_info_new_port); 256 257 return (rc); 258 } 259 260 static int 261 aggr_ioc_add(mblk_t *mp, int mode) 262 { 263 STRUCT_HANDLE(laioc_add_rem, add_arg); 264 uint32_t nports; 265 laioc_port_t *ports = NULL; 266 boolean_t force; 267 int rc, len; 268 269 STRUCT_SET_HANDLE(add_arg, mode, (void *)mp->b_cont->b_rptr); 270 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(add_arg)) 271 return (EINVAL); 272 273 nports = STRUCT_FGET(add_arg, la_nports); 274 if (nports > AGGR_MAX_PORTS) 275 return (EINVAL); 276 277 if (len < STRUCT_SIZE(add_arg) + (nports * sizeof (laioc_port_t))) 278 return (EINVAL); 279 280 ports = (laioc_port_t *)(STRUCT_BUF(add_arg) + 1); 281 force = STRUCT_FGET(add_arg, la_force); 282 283 rc = aggr_grp_add_ports(STRUCT_FGET(add_arg, la_linkid), 284 nports, force, ports); 285 286 freemsg(mp->b_cont); 287 mp->b_cont = NULL; 288 return (rc); 289 } 290 291 static int 292 aggr_ioc_remove(mblk_t *mp, int mode) 293 { 294 STRUCT_HANDLE(laioc_add_rem, rem_arg); 295 uint32_t nports; 296 laioc_port_t *ports = NULL; 297 int rc, len; 298 299 STRUCT_SET_HANDLE(rem_arg, mode, (void *)mp->b_cont->b_rptr); 300 if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(rem_arg)) 301 return (EINVAL); 302 303 nports = STRUCT_FGET(rem_arg, la_nports); 304 if (nports > AGGR_MAX_PORTS) 305 return (EINVAL); 306 307 if (len < STRUCT_SIZE(rem_arg) + (nports * sizeof (laioc_port_t))) 308 return (EINVAL); 309 310 ports = (laioc_port_t *)(STRUCT_BUF(rem_arg) + 1); 311 312 rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_linkid), 313 nports, ports); 314 315 freemsg(mp->b_cont); 316 mp->b_cont = NULL; 317 return (rc); 318 } 319 320 void 321 aggr_ioctl(queue_t *wq, mblk_t *mp) 322 { 323 struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 324 int i, err = EINVAL; 325 mblk_t *nmp; 326 327 if (mp->b_cont == NULL) 328 goto done; 329 330 /* 331 * Construct contiguous message 332 */ 333 if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) { 334 err = ENOMEM; 335 goto done; 336 } 337 338 freemsg(mp->b_cont); 339 mp->b_cont = nmp; 340 341 for (i = 0; i < IOC_CMD_SZ; i++) { 342 if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) { 343 err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag); 344 break; 345 } 346 } 347 348 if (err == 0) { 349 int len = 0; 350 351 if (mp->b_cont != NULL) { 352 len = MBLKL(mp->b_cont); 353 } 354 miocack(wq, mp, len, 0); 355 return; 356 } 357 358 done: 359 miocnak(wq, mp, 0, err); 360 } 361