xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision 22eb7cb5)
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 #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_modify(mblk_t *, int);
43 
44 typedef struct ioc_cmd_s {
45 	int ic_cmd;
46 	int (*ic_func)(mblk_t *, int);
47 } ioc_cmd_t;
48 
49 static ioc_cmd_t ioc_cmd[] = {
50 	{LAIOC_CREATE, aggr_ioc_create},
51 	{LAIOC_DELETE, aggr_ioc_delete},
52 	{LAIOC_INFO, aggr_ioc_info},
53 	{LAIOC_ADD, aggr_ioc_add},
54 	{LAIOC_REMOVE, aggr_ioc_remove},
55 	{LAIOC_MODIFY, aggr_ioc_modify}};
56 
57 #define	IOC_CMD_SZ	(sizeof (ioc_cmd) / sizeof (ioc_cmd_t))
58 
59 /*
60  * Process a LAIOC_MODIFY request.
61  */
62 static int
63 aggr_ioc_modify(mblk_t *mp, int mode)
64 {
65 	STRUCT_HANDLE(laioc_modify, modify_arg);
66 	uint32_t policy;
67 	boolean_t mac_fixed;
68 	uchar_t mac_addr[ETHERADDRL];
69 	uint8_t modify_mask_arg, modify_mask = 0;
70 	datalink_id_t linkid;
71 	uint32_t rc;
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 	linkid = STRUCT_FGET(modify_arg, lu_linkid);
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(linkid, 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 	boolean_t force;
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 	force = STRUCT_FGET(create_arg, lc_force);
148 
149 	rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_linkid),
150 	    STRUCT_FGET(create_arg, lc_key), nports, ports, policy,
151 	    mac_fixed, force, mac_addr, lacp_mode, lacp_timer);
152 
153 	freemsg(mp->b_cont);
154 	mp->b_cont = NULL;
155 	return (rc);
156 }
157 
158 static int
159 aggr_ioc_delete(mblk_t *mp, int mode)
160 {
161 	STRUCT_HANDLE(laioc_delete, delete_arg);
162 	int rc;
163 
164 	STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr);
165 	if (STRUCT_SIZE(delete_arg) > MBLKL(mp))
166 		return (EINVAL);
167 
168 	rc = aggr_grp_delete(STRUCT_FGET(delete_arg, ld_linkid));
169 
170 	freemsg(mp->b_cont);
171 	mp->b_cont = NULL;
172 	return (rc);
173 }
174 
175 typedef struct aggr_ioc_info_state {
176 	uint32_t bytes_left;
177 	uchar_t *where;
178 } aggr_ioc_info_state_t;
179 
180 static int
181 aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key,
182     uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy,
183     uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
184 {
185 	aggr_ioc_info_state_t *state = arg;
186 	laioc_info_group_t grp;
187 
188 	if (state->bytes_left < sizeof (grp))
189 		return (ENOSPC);
190 
191 	grp.lg_linkid = linkid;
192 	grp.lg_key = key;
193 	bcopy(mac, grp.lg_mac, ETHERADDRL);
194 	grp.lg_mac_fixed = mac_fixed;
195 	grp.lg_force = force;
196 	grp.lg_policy = policy;
197 	grp.lg_nports = nports;
198 	grp.lg_lacp_mode = lacp_mode;
199 	grp.lg_lacp_timer = lacp_timer;
200 
201 	bcopy(&grp, state->where, sizeof (grp));
202 	state->where += sizeof (grp);
203 	state->bytes_left -= sizeof (grp);
204 
205 	return (0);
206 }
207 
208 static int
209 aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac,
210     aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
211 {
212 	aggr_ioc_info_state_t *state = arg;
213 	laioc_info_port_t port;
214 
215 	if (state->bytes_left < sizeof (port))
216 		return (ENOSPC);
217 
218 	port.lp_linkid = linkid;
219 	bcopy(mac, port.lp_mac, ETHERADDRL);
220 	port.lp_state = portstate;
221 	port.lp_lacp_state = *lacp_state;
222 
223 	bcopy(&port, state->where, sizeof (port));
224 	state->where += sizeof (port);
225 	state->bytes_left -= sizeof (port);
226 
227 	return (0);
228 }
229 
230 /*ARGSUSED*/
231 static int
232 aggr_ioc_info(mblk_t *mp, int mode)
233 {
234 	laioc_info_t *info_argp;
235 	datalink_id_t linkid;
236 	int rc, len;
237 	aggr_ioc_info_state_t state;
238 
239 	if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp))
240 		return (EINVAL);
241 
242 	info_argp = (laioc_info_t *)mp->b_cont->b_rptr;
243 
244 	/*
245 	 * linkid of the group to return. Must not be DATALINK_INVALID_LINKID.
246 	 */
247 	if ((linkid = info_argp->li_group_linkid) == DATALINK_INVALID_LINKID)
248 		return (EINVAL);
249 
250 	state.bytes_left = len - sizeof (laioc_info_t);
251 	state.where = (uchar_t *)(info_argp + 1);
252 
253 	rc = aggr_grp_info(linkid, &state,
254 	    aggr_ioc_info_new_grp, aggr_ioc_info_new_port);
255 
256 	return (rc);
257 }
258 
259 static int
260 aggr_ioc_add(mblk_t *mp, int mode)
261 {
262 	STRUCT_HANDLE(laioc_add_rem, add_arg);
263 	uint32_t nports;
264 	laioc_port_t *ports = NULL;
265 	boolean_t force;
266 	int rc, len;
267 
268 	STRUCT_SET_HANDLE(add_arg, mode, (void *)mp->b_cont->b_rptr);
269 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(add_arg))
270 		return (EINVAL);
271 
272 	nports = STRUCT_FGET(add_arg, la_nports);
273 	if (nports > AGGR_MAX_PORTS)
274 		return (EINVAL);
275 
276 	if (len < STRUCT_SIZE(add_arg) + (nports * sizeof (laioc_port_t)))
277 		return (EINVAL);
278 
279 	ports = (laioc_port_t *)(STRUCT_BUF(add_arg) + 1);
280 	force = STRUCT_FGET(add_arg, la_force);
281 
282 	rc = aggr_grp_add_ports(STRUCT_FGET(add_arg, la_linkid),
283 	    nports, force, ports);
284 
285 	freemsg(mp->b_cont);
286 	mp->b_cont = NULL;
287 	return (rc);
288 }
289 
290 static int
291 aggr_ioc_remove(mblk_t *mp, int mode)
292 {
293 	STRUCT_HANDLE(laioc_add_rem, rem_arg);
294 	uint32_t nports;
295 	laioc_port_t *ports = NULL;
296 	int rc, len;
297 
298 	STRUCT_SET_HANDLE(rem_arg, mode, (void *)mp->b_cont->b_rptr);
299 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(rem_arg))
300 		return (EINVAL);
301 
302 	nports = STRUCT_FGET(rem_arg, la_nports);
303 	if (nports > AGGR_MAX_PORTS)
304 		return (EINVAL);
305 
306 	if (len < STRUCT_SIZE(rem_arg) + (nports * sizeof (laioc_port_t)))
307 		return (EINVAL);
308 
309 	ports = (laioc_port_t *)(STRUCT_BUF(rem_arg) + 1);
310 
311 	rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_linkid),
312 	    nports, ports);
313 
314 	freemsg(mp->b_cont);
315 	mp->b_cont = NULL;
316 	return (rc);
317 }
318 
319 void
320 aggr_ioctl(queue_t *wq, mblk_t *mp)
321 {
322 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
323 	int i, err = EINVAL;
324 	mblk_t *nmp;
325 
326 	if (mp->b_cont == NULL)
327 		goto done;
328 
329 	/*
330 	 * Construct contiguous message
331 	 */
332 	if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) {
333 		err = ENOMEM;
334 		goto done;
335 	}
336 
337 	freemsg(mp->b_cont);
338 	mp->b_cont = nmp;
339 
340 	for (i = 0; i < IOC_CMD_SZ; i++) {
341 		if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) {
342 			err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag);
343 			break;
344 		}
345 	}
346 
347 	if (err == 0) {
348 		int len = 0;
349 
350 		if (mp->b_cont != NULL) {
351 			len = MBLKL(mp->b_cont);
352 		}
353 		miocack(wq, mp, len, 0);
354 		return;
355 	}
356 
357 done:
358 	miocnak(wq, mp, 0, err);
359 }
360