xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision 4703203d)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * IEEE 802.3ad Link Aggregation -- IOCTL processing.
30  */
31 
32 #include <sys/ddi.h>
33 #include <sys/aggr.h>
34 #include <sys/aggr_impl.h>
35 #include <sys/strsun.h>
36 
37 static int aggr_ioc_create(mblk_t *, int);
38 static int aggr_ioc_delete(mblk_t *, int);
39 static int aggr_ioc_info(mblk_t *, int);
40 static int aggr_ioc_add(mblk_t *, int);
41 static int aggr_ioc_remove(mblk_t *, int);
42 static int aggr_ioc_status(mblk_t *, int);
43 static int aggr_ioc_modify(mblk_t *, int);
44 
45 typedef struct ioc_cmd_s {
46 	int ic_cmd;
47 	int (*ic_func)(mblk_t *, int);
48 } ioc_cmd_t;
49 
50 static ioc_cmd_t ioc_cmd[] = {
51 	{LAIOC_CREATE, aggr_ioc_create},
52 	{LAIOC_DELETE, aggr_ioc_delete},
53 	{LAIOC_INFO, aggr_ioc_info},
54 	{LAIOC_ADD, aggr_ioc_add},
55 	{LAIOC_REMOVE, aggr_ioc_remove},
56 	{LAIOC_MODIFY, aggr_ioc_modify}};
57 
58 #define	IOC_CMD_SZ	(sizeof (ioc_cmd) / sizeof (ioc_cmd_t))
59 
60 /*
61  * Process a LAIOC_MODIFY request.
62  */
63 static int
64 aggr_ioc_modify(mblk_t *mp, int mode)
65 {
66 	STRUCT_HANDLE(laioc_modify, modify_arg);
67 	uint32_t policy;
68 	boolean_t mac_fixed;
69 	uchar_t mac_addr[ETHERADDRL];
70 	uint8_t modify_mask_arg, modify_mask = 0;
71 	uint32_t rc, key;
72 	aggr_lacp_mode_t lacp_mode;
73 	aggr_lacp_timer_t lacp_timer;
74 
75 	STRUCT_SET_HANDLE(modify_arg, mode, (void *)mp->b_cont->b_rptr);
76 	if (MBLKL(mp->b_cont) < STRUCT_SIZE(modify_arg))
77 		return (EINVAL);
78 
79 	key = STRUCT_FGET(modify_arg, lu_key);
80 	modify_mask_arg = STRUCT_FGET(modify_arg, lu_modify_mask);
81 
82 	if (modify_mask_arg & LAIOC_MODIFY_POLICY) {
83 		modify_mask |= AGGR_MODIFY_POLICY;
84 		policy = STRUCT_FGET(modify_arg, lu_policy);
85 	}
86 
87 	if (modify_mask_arg & LAIOC_MODIFY_MAC) {
88 		modify_mask |= AGGR_MODIFY_MAC;
89 		bcopy(STRUCT_FGET(modify_arg, lu_mac), mac_addr, ETHERADDRL);
90 		mac_fixed = STRUCT_FGET(modify_arg, lu_mac_fixed);
91 	}
92 
93 	if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) {
94 		modify_mask |= AGGR_MODIFY_LACP_MODE;
95 		lacp_mode = STRUCT_FGET(modify_arg, lu_lacp_mode);
96 	}
97 
98 	if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) {
99 		modify_mask |= AGGR_MODIFY_LACP_TIMER;
100 		lacp_timer = STRUCT_FGET(modify_arg, lu_lacp_timer);
101 	}
102 
103 	rc = aggr_grp_modify(key, NULL, modify_mask, policy, mac_fixed,
104 	    mac_addr, lacp_mode, lacp_timer);
105 
106 	freemsg(mp->b_cont);
107 	mp->b_cont = NULL;
108 	return (rc);
109 }
110 
111 /*
112  * Process a LAIOC_CREATE request.
113  */
114 static int
115 aggr_ioc_create(mblk_t *mp, int mode)
116 {
117 	STRUCT_HANDLE(laioc_create, create_arg);
118 	uint16_t nports;
119 	laioc_port_t *ports = NULL;
120 	uint32_t policy;
121 	boolean_t mac_fixed;
122 	uchar_t mac_addr[ETHERADDRL];
123 	aggr_lacp_mode_t lacp_mode;
124 	aggr_lacp_timer_t lacp_timer;
125 	int rc, len;
126 
127 	STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr);
128 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(create_arg))
129 		return (EINVAL);
130 
131 	nports = STRUCT_FGET(create_arg, lc_nports);
132 	if (nports > AGGR_MAX_PORTS)
133 		return (EINVAL);
134 
135 	policy = STRUCT_FGET(create_arg, lc_policy);
136 	lacp_mode = STRUCT_FGET(create_arg, lc_lacp_mode);
137 	lacp_timer = STRUCT_FGET(create_arg, lc_lacp_timer);
138 
139 	if (len < STRUCT_SIZE(create_arg) + (nports * sizeof (laioc_port_t)))
140 		return (EINVAL);
141 
142 	ports = (laioc_port_t *)(STRUCT_BUF(create_arg) + 1);
143 
144 	bcopy(STRUCT_FGET(create_arg, lc_mac), mac_addr, ETHERADDRL);
145 	mac_fixed = STRUCT_FGET(create_arg, lc_mac_fixed);
146 
147 	rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_key),
148 	    nports, ports, policy, mac_fixed, mac_addr, lacp_mode, lacp_timer);
149 
150 	freemsg(mp->b_cont);
151 	mp->b_cont = NULL;
152 	return (rc);
153 }
154 
155 static int
156 aggr_ioc_delete(mblk_t *mp, int mode)
157 {
158 	STRUCT_HANDLE(laioc_delete, delete_arg);
159 	int rc;
160 
161 	STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr);
162 	if (STRUCT_SIZE(delete_arg) > MBLKL(mp))
163 		return (EINVAL);
164 
165 	rc = aggr_grp_delete(STRUCT_FGET(delete_arg, ld_key));
166 
167 	freemsg(mp->b_cont);
168 	mp->b_cont = NULL;
169 	return (rc);
170 }
171 
172 typedef struct aggr_ioc_info_state {
173 	uint32_t bytes_left;
174 	uchar_t *where;
175 } aggr_ioc_info_state_t;
176 
177 static int
178 aggr_ioc_info_new_grp(void *arg, uint32_t key, uchar_t *mac,
179     boolean_t mac_fixed, uint32_t policy, uint32_t nports,
180     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
181 {
182 	aggr_ioc_info_state_t *state = arg;
183 	laioc_info_group_t grp;
184 
185 	if (state->bytes_left < sizeof (grp))
186 		return (ENOSPC);
187 
188 	grp.lg_key = key;
189 	bcopy(mac, grp.lg_mac, ETHERADDRL);
190 	grp.lg_mac_fixed = mac_fixed;
191 	grp.lg_policy = policy;
192 	grp.lg_nports = nports;
193 	grp.lg_lacp_mode = lacp_mode;
194 	grp.lg_lacp_timer = lacp_timer;
195 
196 	bcopy(&grp, state->where, sizeof (grp));
197 	state->where += sizeof (grp);
198 	state->bytes_left -= sizeof (grp);
199 
200 	return (0);
201 }
202 
203 static int
204 aggr_ioc_info_new_port(void *arg, char *devname, uchar_t *mac,
205     aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
206 {
207 	aggr_ioc_info_state_t *state = arg;
208 	laioc_info_port_t port;
209 
210 	if (state->bytes_left < sizeof (port))
211 		return (ENOSPC);
212 
213 	bcopy(devname, port.lp_devname, MAXNAMELEN + 1);
214 	bcopy(mac, port.lp_mac, ETHERADDRL);
215 	port.lp_state = portstate;
216 	port.lp_lacp_state = *lacp_state;
217 
218 	bcopy(&port, state->where, sizeof (port));
219 	state->where += sizeof (port);
220 	state->bytes_left -= sizeof (port);
221 
222 	return (0);
223 }
224 
225 /*ARGSUSED*/
226 static int
227 aggr_ioc_info(mblk_t *mp, int mode)
228 {
229 	laioc_info_t *info_argp;
230 	uint32_t ngroups, group_key;
231 	int rc, len;
232 	aggr_ioc_info_state_t state;
233 
234 	if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp))
235 		return (EINVAL);
236 
237 	info_argp = (laioc_info_t *)mp->b_cont->b_rptr;
238 	/*
239 	 * Key of the group to return. If zero, the call returns information
240 	 * regarding all groups currently defined.
241 	 */
242 	group_key = info_argp->li_group_key;
243 
244 	state.bytes_left = len - sizeof (laioc_info_t);
245 	state.where = (uchar_t *)(info_argp + 1);
246 
247 	rc = aggr_grp_info(&ngroups, group_key, &state, aggr_ioc_info_new_grp,
248 	    aggr_ioc_info_new_port);
249 	if (rc == 0)
250 		info_argp->li_ngroups = ngroups;
251 
252 	return (rc);
253 }
254 
255 static int
256 aggr_ioc_add(mblk_t *mp, int mode)
257 {
258 	STRUCT_HANDLE(laioc_add_rem, add_arg);
259 	uint32_t nports;
260 	laioc_port_t *ports = NULL;
261 	int rc, len;
262 
263 	STRUCT_SET_HANDLE(add_arg, mode, (void *)mp->b_cont->b_rptr);
264 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(add_arg))
265 		return (EINVAL);
266 
267 	nports = STRUCT_FGET(add_arg, la_nports);
268 	if (nports > AGGR_MAX_PORTS)
269 		return (EINVAL);
270 
271 	if (len < STRUCT_SIZE(add_arg) + (nports * sizeof (laioc_port_t)))
272 		return (EINVAL);
273 
274 	ports = (laioc_port_t *)(STRUCT_BUF(add_arg) + 1);
275 
276 	rc = aggr_grp_add_ports(STRUCT_FGET(add_arg, la_key),
277 	    nports, ports);
278 
279 	freemsg(mp->b_cont);
280 	mp->b_cont = NULL;
281 	return (rc);
282 }
283 
284 static int
285 aggr_ioc_remove(mblk_t *mp, int mode)
286 {
287 	STRUCT_HANDLE(laioc_add_rem, rem_arg);
288 	uint32_t nports;
289 	laioc_port_t *ports = NULL;
290 	int rc, len;
291 
292 	STRUCT_SET_HANDLE(rem_arg, mode, (void *)mp->b_cont->b_rptr);
293 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(rem_arg))
294 		return (EINVAL);
295 
296 	nports = STRUCT_FGET(rem_arg, la_nports);
297 	if (nports > AGGR_MAX_PORTS)
298 		return (EINVAL);
299 
300 	if (len < STRUCT_SIZE(rem_arg) + (nports * sizeof (laioc_port_t)))
301 		return (EINVAL);
302 
303 	ports = (laioc_port_t *)(STRUCT_BUF(rem_arg) + 1);
304 
305 	rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_key),
306 	    nports, ports);
307 
308 	freemsg(mp->b_cont);
309 	mp->b_cont = NULL;
310 	return (rc);
311 }
312 
313 void
314 aggr_ioctl(queue_t *wq, mblk_t *mp)
315 {
316 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
317 	int i, err = EINVAL;
318 	mblk_t *nmp;
319 
320 	if (mp->b_cont == NULL)
321 		goto done;
322 
323 	/*
324 	 * Construct contiguous message
325 	 */
326 	if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) {
327 		err = ENOMEM;
328 		goto done;
329 	}
330 
331 	freemsg(mp->b_cont);
332 	mp->b_cont = nmp;
333 
334 	for (i = 0; i < IOC_CMD_SZ; i++) {
335 		if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) {
336 			err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag);
337 			break;
338 		}
339 	}
340 
341 	if (err == 0) {
342 		int len = 0;
343 
344 		if (mp->b_cont != NULL) {
345 			len = MBLKL(mp->b_cont);
346 		}
347 		miocack(wq, mp, len, 0);
348 		return;
349 	}
350 
351 done:
352 	miocnak(wq, mp, 0, err);
353 }
354