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