xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision 34a4e6b5)
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
aggr_ioc_modify(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_create(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_delete(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_info_new_grp(void * arg,datalink_id_t linkid,uint32_t key,uchar_t * mac,boolean_t mac_fixed,boolean_t force,uint32_t policy,uint32_t nports,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer)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
aggr_ioc_info_new_port(void * arg,datalink_id_t linkid,uchar_t * mac,aggr_port_state_t portstate,aggr_lacp_state_t * lacp_state)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
aggr_ioc_info(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_add_remove(laioc_add_rem_t * add_rem_arg,intptr_t arg,int cmd,int mode)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
aggr_ioc_add(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_remove(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
aggr_ioc_init(void)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
aggr_ioc_fini(void)289 aggr_ioc_fini(void)
290 {
291 	dld_ioc_unregister(AGGR_IOC);
292 }
293