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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * IEEE 802.3ad Link Aggregation -- IOCTL processing. 28 */ 29 30 #include <sys/aggr.h> 31 #include <sys/aggr_impl.h> 32 #include <sys/policy.h> 33 34 /* 35 * Process a LAIOC_MODIFY request. 36 */ 37 /* ARGSUSED */ 38 static int 39 aggr_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 40 { 41 laioc_modify_t *modify_arg = karg; 42 uint32_t policy; 43 boolean_t mac_fixed; 44 uchar_t mac_addr[ETHERADDRL]; 45 uint8_t modify_mask_arg, modify_mask = 0; 46 aggr_lacp_mode_t lacp_mode; 47 aggr_lacp_timer_t lacp_timer; 48 49 policy = 0; 50 mac_fixed = B_FALSE; 51 lacp_mode = AGGR_LACP_OFF; 52 lacp_timer = AGGR_LACP_TIMER_LONG; 53 54 modify_mask_arg = modify_arg->lu_modify_mask; 55 56 if (modify_mask_arg & LAIOC_MODIFY_POLICY) { 57 modify_mask |= AGGR_MODIFY_POLICY; 58 policy = modify_arg->lu_policy; 59 } 60 61 if (modify_mask_arg & LAIOC_MODIFY_MAC) { 62 modify_mask |= AGGR_MODIFY_MAC; 63 bcopy(modify_arg->lu_mac, mac_addr, ETHERADDRL); 64 mac_fixed = modify_arg->lu_mac_fixed; 65 } 66 67 if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) { 68 modify_mask |= AGGR_MODIFY_LACP_MODE; 69 lacp_mode = modify_arg->lu_lacp_mode; 70 } 71 72 if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) { 73 modify_mask |= AGGR_MODIFY_LACP_TIMER; 74 lacp_timer = modify_arg->lu_lacp_timer; 75 } 76 77 return (aggr_grp_modify(modify_arg->lu_linkid, modify_mask, policy, 78 mac_fixed, mac_addr, lacp_mode, lacp_timer)); 79 } 80 81 /* 82 * Process a LAIOC_CREATE request. 83 */ 84 /* ARGSUSED */ 85 static int 86 aggr_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 87 { 88 laioc_create_t *create_arg = karg; 89 uint16_t nports; 90 laioc_port_t *ports = NULL; 91 size_t ports_size; 92 uint32_t policy; 93 boolean_t mac_fixed; 94 boolean_t force; 95 uchar_t mac_addr[ETHERADDRL]; 96 aggr_lacp_mode_t lacp_mode; 97 aggr_lacp_timer_t lacp_timer; 98 int rc; 99 100 nports = create_arg->lc_nports; 101 if (nports > AGGR_MAX_PORTS) 102 return (EINVAL); 103 104 policy = create_arg->lc_policy; 105 lacp_mode = create_arg->lc_lacp_mode; 106 lacp_timer = create_arg->lc_lacp_timer; 107 108 ports_size = nports * sizeof (laioc_port_t); 109 ports = kmem_alloc(ports_size, KM_SLEEP); 110 111 if (ddi_copyin((uchar_t *)arg + sizeof (*create_arg), ports, 112 ports_size, mode) != 0) { 113 rc = EFAULT; 114 goto done; 115 } 116 117 bcopy(create_arg->lc_mac, mac_addr, ETHERADDRL); 118 mac_fixed = create_arg->lc_mac_fixed; 119 force = create_arg->lc_force; 120 121 rc = aggr_grp_create(create_arg->lc_linkid, create_arg->lc_key, nports, 122 ports, policy, mac_fixed, force, mac_addr, lacp_mode, lacp_timer, 123 cred); 124 125 done: 126 kmem_free(ports, ports_size); 127 return (rc); 128 } 129 130 /* ARGSUSED */ 131 static int 132 aggr_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 133 { 134 laioc_delete_t *delete_arg = karg; 135 136 return (aggr_grp_delete(delete_arg->ld_linkid, cred)); 137 } 138 139 typedef struct aggr_ioc_info_state { 140 uint32_t bytes_left; 141 uchar_t *where; /* in user buffer */ 142 int mode; 143 } aggr_ioc_info_state_t; 144 145 static int 146 aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key, 147 uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy, 148 uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 149 { 150 aggr_ioc_info_state_t *state = arg; 151 laioc_info_group_t grp; 152 153 if (state->bytes_left < sizeof (grp)) 154 return (ENOSPC); 155 156 grp.lg_linkid = linkid; 157 grp.lg_key = key; 158 bcopy(mac, grp.lg_mac, ETHERADDRL); 159 grp.lg_mac_fixed = mac_fixed; 160 grp.lg_force = force; 161 grp.lg_policy = policy; 162 grp.lg_nports = nports; 163 grp.lg_lacp_mode = lacp_mode; 164 grp.lg_lacp_timer = lacp_timer; 165 166 if (ddi_copyout(&grp, state->where, sizeof (grp), state->mode) != 0) 167 return (EFAULT); 168 169 state->where += sizeof (grp); 170 state->bytes_left -= sizeof (grp); 171 172 return (0); 173 } 174 175 static int 176 aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac, 177 aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state) 178 { 179 aggr_ioc_info_state_t *state = arg; 180 laioc_info_port_t port; 181 182 if (state->bytes_left < sizeof (port)) 183 return (ENOSPC); 184 185 port.lp_linkid = linkid; 186 bcopy(mac, port.lp_mac, ETHERADDRL); 187 port.lp_state = portstate; 188 port.lp_lacp_state = *lacp_state; 189 190 if (ddi_copyout(&port, state->where, sizeof (port), state->mode) != 0) 191 return (EFAULT); 192 193 state->where += sizeof (port); 194 state->bytes_left -= sizeof (port); 195 196 return (0); 197 } 198 199 /*ARGSUSED*/ 200 static int 201 aggr_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 202 { 203 laioc_info_t *info_argp = karg; 204 aggr_ioc_info_state_t state; 205 206 state.bytes_left = info_argp->li_bufsize - sizeof (laioc_info_t); 207 state.where = (uchar_t *)arg + sizeof (laioc_info_t); 208 state.mode = mode; 209 210 return (aggr_grp_info(info_argp->li_group_linkid, &state, 211 aggr_ioc_info_new_grp, aggr_ioc_info_new_port, cred)); 212 } 213 214 static int 215 aggr_ioc_add_remove(laioc_add_rem_t *add_rem_arg, intptr_t arg, int cmd, 216 int mode) 217 { 218 uint16_t nports; 219 laioc_port_t *ports = NULL; 220 size_t ports_size; 221 int rc; 222 223 nports = add_rem_arg->la_nports; 224 if (nports > AGGR_MAX_PORTS) 225 return (EINVAL); 226 227 ports_size = nports * sizeof (laioc_port_t); 228 ports = kmem_alloc(ports_size, KM_SLEEP); 229 if (ddi_copyin((uchar_t *)arg + sizeof (*add_rem_arg), ports, 230 ports_size, mode) != 0) { 231 rc = EFAULT; 232 goto done; 233 } 234 235 switch (cmd) { 236 case LAIOC_ADD: 237 rc = aggr_grp_add_ports(add_rem_arg->la_linkid, nports, 238 add_rem_arg->la_force, ports); 239 break; 240 case LAIOC_REMOVE: 241 rc = aggr_grp_rem_ports(add_rem_arg->la_linkid, nports, ports); 242 break; 243 default: 244 rc = 0; 245 break; 246 } 247 248 done: 249 kmem_free(ports, ports_size); 250 return (rc); 251 } 252 253 /* ARGSUSED */ 254 static int 255 aggr_ioc_add(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 256 { 257 return (aggr_ioc_add_remove(karg, arg, LAIOC_ADD, mode)); 258 } 259 260 /* ARGSUSED */ 261 static int 262 aggr_ioc_remove(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 263 { 264 return (aggr_ioc_add_remove(karg, arg, LAIOC_REMOVE, mode)); 265 } 266 267 static dld_ioc_info_t aggr_ioc_list[] = { 268 {LAIOC_CREATE, DLDCOPYIN, sizeof (laioc_create_t), aggr_ioc_create, 269 secpolicy_dl_config}, 270 {LAIOC_DELETE, DLDCOPYIN, sizeof (laioc_delete_t), aggr_ioc_delete, 271 secpolicy_dl_config}, 272 {LAIOC_INFO, DLDCOPYINOUT, sizeof (laioc_info_t), aggr_ioc_info, NULL}, 273 {LAIOC_ADD, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_add, 274 secpolicy_dl_config}, 275 {LAIOC_REMOVE, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_remove, 276 secpolicy_dl_config}, 277 {LAIOC_MODIFY, DLDCOPYIN, sizeof (laioc_modify_t), aggr_ioc_modify, 278 secpolicy_dl_config} 279 }; 280 281 int 282 aggr_ioc_init(void) 283 { 284 return (dld_ioc_register(AGGR_IOC, aggr_ioc_list, 285 DLDIOCCNT(aggr_ioc_list))); 286 } 287 288 void 289 aggr_ioc_fini(void) 290 { 291 dld_ioc_unregister(AGGR_IOC); 292 } 293