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