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