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 37 static int aggr_ioc_create(int, void *, int); 38 static int aggr_ioc_delete(int, void *, int); 39 static int aggr_ioc_info(int, void *, int); 40 static int aggr_ioc_add_remove(int, void *, int); 41 static int aggr_ioc_status(int, void *, int); 42 static int aggr_ioc_modify(int, void *, int); 43 44 typedef struct ioc_cmd_s { 45 int ic_cmd; 46 int (*ic_func)(int, void *, 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_remove}, 54 {LAIOC_REMOVE, aggr_ioc_add_remove}, 55 {LAIOC_MODIFY, aggr_ioc_modify}}; 56 57 /*ARGSUSED*/ 58 int 59 aggr_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 60 { 61 /* only the control interface can be opened */ 62 if (getminor(*devp) != AGGR_MINOR_CTL) 63 return (ENOSYS); 64 return (0); 65 } 66 67 /*ARGSUSED*/ 68 int 69 aggr_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 70 { 71 return (0); 72 } 73 74 /* 75 * Process a LAIOC_MODIFY request. 76 */ 77 /* ARGSUSED */ 78 static int 79 aggr_ioc_modify(int cmd, void *arg, int mode) 80 { 81 STRUCT_DECL(laioc_modify, modify_arg); 82 uint32_t policy; 83 boolean_t mac_fixed; 84 uchar_t mac_addr[ETHERADDRL]; 85 uint8_t modify_mask_arg, modify_mask = 0; 86 uint32_t key; 87 aggr_lacp_mode_t lacp_mode; 88 aggr_lacp_timer_t lacp_timer; 89 90 STRUCT_INIT(modify_arg, mode); 91 92 if (copyin(arg, STRUCT_BUF(modify_arg), STRUCT_SIZE(modify_arg)) != 0) 93 return (EFAULT); 94 95 key = STRUCT_FGET(modify_arg, lu_key); 96 modify_mask_arg = STRUCT_FGET(modify_arg, lu_modify_mask); 97 98 if (modify_mask_arg & LAIOC_MODIFY_POLICY) { 99 modify_mask |= AGGR_MODIFY_POLICY; 100 policy = STRUCT_FGET(modify_arg, lu_policy); 101 } 102 103 if (modify_mask_arg & LAIOC_MODIFY_MAC) { 104 modify_mask |= AGGR_MODIFY_MAC; 105 bcopy(STRUCT_FGET(modify_arg, lu_mac), mac_addr, ETHERADDRL); 106 mac_fixed = STRUCT_FGET(modify_arg, lu_mac_fixed); 107 } 108 109 if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) { 110 modify_mask |= AGGR_MODIFY_LACP_MODE; 111 lacp_mode = STRUCT_FGET(modify_arg, lu_lacp_mode); 112 } 113 114 if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) { 115 modify_mask |= AGGR_MODIFY_LACP_TIMER; 116 lacp_timer = STRUCT_FGET(modify_arg, lu_lacp_timer); 117 } 118 119 return (aggr_grp_modify(key, NULL, modify_mask, policy, mac_fixed, 120 mac_addr, lacp_mode, lacp_timer)); 121 } 122 123 /* 124 * Process a LAIOC_CREATE request. 125 */ 126 /* ARGSUSED */ 127 static int 128 aggr_ioc_create(int cmd, void *arg, int mode) 129 { 130 STRUCT_DECL(laioc_create, create_arg); 131 uint16_t nports; 132 laioc_port_t *ports = NULL; 133 uint32_t policy; 134 boolean_t mac_fixed; 135 uchar_t mac_addr[ETHERADDRL]; 136 aggr_lacp_mode_t lacp_mode; 137 aggr_lacp_timer_t lacp_timer; 138 int rc; 139 140 STRUCT_INIT(create_arg, mode); 141 142 if (copyin(arg, STRUCT_BUF(create_arg), STRUCT_SIZE(create_arg)) != 0) 143 return (EFAULT); 144 145 nports = STRUCT_FGET(create_arg, lc_nports); 146 if (nports > AGGR_MAX_PORTS) 147 return (EINVAL); 148 149 policy = STRUCT_FGET(create_arg, lc_policy); 150 lacp_mode = STRUCT_FGET(create_arg, lc_lacp_mode); 151 lacp_timer = STRUCT_FGET(create_arg, lc_lacp_timer); 152 153 ports = kmem_alloc(nports * sizeof (laioc_port_t), KM_SLEEP); 154 155 if (copyin(STRUCT_FGETP(create_arg, lc_ports), ports, 156 nports * sizeof (laioc_port_t)) != 0) { 157 rc = EFAULT; 158 goto bail; 159 } 160 161 bcopy(STRUCT_FGET(create_arg, lc_mac), mac_addr, ETHERADDRL); 162 mac_fixed = STRUCT_FGET(create_arg, lc_mac_fixed); 163 164 rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_key), 165 nports, ports, policy, mac_fixed, mac_addr, lacp_mode, lacp_timer); 166 167 bail: 168 kmem_free(ports, nports * sizeof (laioc_port_t)); 169 return (rc); 170 } 171 172 /* ARGSUSED */ 173 static int 174 aggr_ioc_delete(int cmd, void *arg, int mode) 175 { 176 STRUCT_DECL(laioc_delete, delete_arg); 177 178 STRUCT_INIT(delete_arg, mode); 179 180 if (copyin(arg, STRUCT_BUF(delete_arg), STRUCT_SIZE(delete_arg)) != 0) 181 return (EFAULT); 182 183 return (aggr_grp_delete(STRUCT_FGET(delete_arg, ld_key))); 184 } 185 186 typedef struct aggr_ioc_info_state { 187 uint32_t bytes_left; 188 uchar_t *where; /* in user buffer */ 189 } aggr_ioc_info_state_t; 190 191 static int 192 aggr_ioc_info_new_grp(void *arg, uint32_t key, uchar_t *mac, 193 boolean_t mac_fixed, uint32_t policy, uint32_t nports, 194 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 195 { 196 aggr_ioc_info_state_t *state = arg; 197 laioc_info_group_t grp; 198 199 if (state->bytes_left < sizeof (grp)) 200 return (ENOSPC); 201 202 grp.lg_key = key; 203 bcopy(mac, grp.lg_mac, ETHERADDRL); 204 grp.lg_mac_fixed = mac_fixed; 205 grp.lg_policy = policy; 206 grp.lg_nports = nports; 207 grp.lg_lacp_mode = lacp_mode; 208 grp.lg_lacp_timer = lacp_timer; 209 210 if (copyout(&grp, state->where, sizeof (grp)) != 0) 211 return (EFAULT); 212 213 state->where += sizeof (grp); 214 state->bytes_left -= sizeof (grp); 215 216 return (0); 217 } 218 219 static int 220 aggr_ioc_info_new_port(void *arg, char *devname, uint32_t portnum, 221 uchar_t *mac, aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state) 222 { 223 aggr_ioc_info_state_t *state = arg; 224 laioc_info_port_t port; 225 226 if (state->bytes_left < sizeof (port)) 227 return (ENOSPC); 228 229 bcopy(devname, port.lp_devname, MAXNAMELEN + 1); 230 port.lp_port = portnum; 231 bcopy(mac, port.lp_mac, ETHERADDRL); 232 port.lp_state = portstate; 233 port.lp_lacp_state = *lacp_state; 234 235 if (copyout(&port, state->where, sizeof (port)) != 0) 236 return (EFAULT); 237 238 state->where += sizeof (port); 239 state->bytes_left -= sizeof (port); 240 241 return (0); 242 } 243 244 /*ARGSUSED*/ 245 static int 246 aggr_ioc_info(int cmd, void *arg, int mode) 247 { 248 laioc_info_t info_arg; 249 uint32_t ngroups, group_key; 250 int rc; 251 aggr_ioc_info_state_t state; 252 253 if (copyin(arg, &info_arg, sizeof (info_arg)) != 0) 254 return (EFAULT); 255 256 /* 257 * Key of the group to return. If zero, the call returns information 258 * regarding all groups currently defined. 259 */ 260 group_key = info_arg.li_group_key; 261 262 state.bytes_left = info_arg.li_bufsize - sizeof (laioc_info_t); 263 state.where = (uchar_t *)arg + sizeof (laioc_info_t); 264 265 rc = aggr_grp_info(&ngroups, group_key, &state, aggr_ioc_info_new_grp, 266 aggr_ioc_info_new_port); 267 if (rc == 0) { 268 info_arg.li_ngroups = ngroups; 269 if (copyout(&info_arg, arg, sizeof (info_arg)) != 0) 270 return (EFAULT); 271 } 272 return (rc); 273 } 274 275 /*ARGSUSED*/ 276 static int 277 aggr_ioc_add_remove(int cmd, void *arg, int mode) 278 { 279 STRUCT_DECL(laioc_add_rem, add_rem_arg); 280 uint16_t nports; 281 laioc_port_t *ports = NULL; 282 int rc; 283 284 STRUCT_INIT(add_rem_arg, mode); 285 286 if (copyin(arg, STRUCT_BUF(add_rem_arg), STRUCT_SIZE(add_rem_arg)) != 0) 287 return (EFAULT); 288 289 nports = STRUCT_FGET(add_rem_arg, la_nports); 290 if (nports > AGGR_MAX_PORTS) 291 return (EINVAL); 292 293 ports = kmem_alloc(nports * sizeof (laioc_port_t), KM_SLEEP); 294 295 if (copyin(STRUCT_FGETP(add_rem_arg, la_ports), ports, 296 nports * sizeof (laioc_port_t)) != 0) { 297 rc = EFAULT; 298 goto bail; 299 } 300 301 switch (cmd) { 302 case LAIOC_ADD: 303 rc = aggr_grp_add_ports(STRUCT_FGET(add_rem_arg, la_key), 304 nports, ports); 305 break; 306 case LAIOC_REMOVE: 307 rc = aggr_grp_rem_ports(STRUCT_FGET(add_rem_arg, la_key), 308 nports, ports); 309 break; 310 default: 311 rc = EINVAL; 312 } 313 314 bail: 315 if (ports != NULL) 316 kmem_free(ports, nports * sizeof (laioc_port_t)); 317 return (rc); 318 } 319 320 /*ARGSUSED*/ 321 static int 322 aggr_ioc_remove(void *arg, int mode) 323 { 324 STRUCT_DECL(laioc_add_rem, rem_arg); 325 uint16_t nports; 326 laioc_port_t *ports = NULL; 327 int rc; 328 329 STRUCT_INIT(rem_arg, mode); 330 331 if (copyin(arg, STRUCT_BUF(rem_arg), STRUCT_SIZE(rem_arg)) != 0) 332 return (EFAULT); 333 334 nports = STRUCT_FGET(rem_arg, la_nports); 335 if (nports > AGGR_MAX_PORTS) 336 return (EINVAL); 337 338 ports = kmem_alloc(nports * sizeof (laioc_port_t), KM_SLEEP); 339 340 if (copyin(STRUCT_FGETP(rem_arg, la_ports), ports, 341 nports * sizeof (laioc_port_t)) != 0) { 342 rc = EFAULT; 343 goto bail; 344 } 345 346 rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_key), 347 nports, ports); 348 349 bail: 350 if (ports != NULL) 351 kmem_free(ports, nports * sizeof (laioc_port_t)); 352 return (rc); 353 } 354 355 /*ARGSUSED*/ 356 int 357 aggr_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rv) 358 { 359 int i; 360 361 for (i = 0; i < sizeof (ioc_cmd) / sizeof (ioc_cmd_t); i++) { 362 if (cmd == ioc_cmd[i].ic_cmd) 363 return (ioc_cmd[i].ic_func(cmd, (void *)arg, mode)); 364 } 365 366 return (EINVAL); 367 } 368