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