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