xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
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 /*
27  * IEEE 802.3ad Link Aggregation -- IOCTL processing.
28  */
29 
30 #include <sys/aggr.h>
31 #include <sys/aggr_impl.h>
32 
33 /*
34  * Process a LAIOC_MODIFY request.
35  */
36 /* ARGSUSED */
37 static int
38 aggr_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred)
39 {
40 	laioc_modify_t *modify_arg = karg;
41 	uint32_t policy;
42 	boolean_t mac_fixed;
43 	uchar_t mac_addr[ETHERADDRL];
44 	uint8_t modify_mask_arg, modify_mask = 0;
45 	aggr_lacp_mode_t lacp_mode;
46 	aggr_lacp_timer_t lacp_timer;
47 
48 	modify_mask_arg = modify_arg->lu_modify_mask;
49 
50 	if (modify_mask_arg & LAIOC_MODIFY_POLICY) {
51 		modify_mask |= AGGR_MODIFY_POLICY;
52 		policy = modify_arg->lu_policy;
53 	}
54 
55 	if (modify_mask_arg & LAIOC_MODIFY_MAC) {
56 		modify_mask |= AGGR_MODIFY_MAC;
57 		bcopy(modify_arg->lu_mac, mac_addr, ETHERADDRL);
58 		mac_fixed = modify_arg->lu_mac_fixed;
59 	}
60 
61 	if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) {
62 		modify_mask |= AGGR_MODIFY_LACP_MODE;
63 		lacp_mode = modify_arg->lu_lacp_mode;
64 	}
65 
66 	if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) {
67 		modify_mask |= AGGR_MODIFY_LACP_TIMER;
68 		lacp_timer = modify_arg->lu_lacp_timer;
69 	}
70 
71 	return (aggr_grp_modify(modify_arg->lu_linkid, NULL, modify_mask,
72 	    policy, mac_fixed, mac_addr, lacp_mode, lacp_timer));
73 }
74 
75 /*
76  * Process a LAIOC_CREATE request.
77  */
78 /* ARGSUSED */
79 static int
80 aggr_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred)
81 {
82 	laioc_create_t *create_arg = karg;
83 	uint16_t nports;
84 	laioc_port_t *ports = NULL;
85 	size_t ports_size;
86 	uint32_t policy;
87 	boolean_t mac_fixed;
88 	boolean_t force;
89 	uchar_t mac_addr[ETHERADDRL];
90 	aggr_lacp_mode_t lacp_mode;
91 	aggr_lacp_timer_t lacp_timer;
92 	int rc;
93 
94 	nports = create_arg->lc_nports;
95 	if (nports > AGGR_MAX_PORTS)
96 		return (EINVAL);
97 
98 	policy = create_arg->lc_policy;
99 	lacp_mode = create_arg->lc_lacp_mode;
100 	lacp_timer = create_arg->lc_lacp_timer;
101 
102 	ports_size = nports * sizeof (laioc_port_t);
103 	ports = kmem_alloc(ports_size, KM_SLEEP);
104 
105 	if (ddi_copyin((uchar_t *)arg + sizeof (*create_arg), ports,
106 	    ports_size, mode) != 0) {
107 		rc = EFAULT;
108 		goto done;
109 	}
110 
111 	bcopy(create_arg->lc_mac, mac_addr, ETHERADDRL);
112 	mac_fixed = create_arg->lc_mac_fixed;
113 	force = create_arg->lc_force;
114 
115 	rc = aggr_grp_create(create_arg->lc_linkid, create_arg->lc_key, nports,
116 	    ports, policy, mac_fixed, force, mac_addr, lacp_mode, lacp_timer);
117 
118 done:
119 	kmem_free(ports, ports_size);
120 	return (rc);
121 }
122 
123 /* ARGSUSED */
124 static int
125 aggr_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred)
126 {
127 	laioc_delete_t *delete_arg = karg;
128 
129 	return (aggr_grp_delete(delete_arg->ld_linkid));
130 }
131 
132 typedef struct aggr_ioc_info_state {
133 	uint32_t	bytes_left;
134 	uchar_t		*where;		/* in user buffer */
135 	int		mode;
136 } aggr_ioc_info_state_t;
137 
138 static int
139 aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key,
140     uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy,
141     uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
142 {
143 	aggr_ioc_info_state_t *state = arg;
144 	laioc_info_group_t grp;
145 
146 	if (state->bytes_left < sizeof (grp))
147 		return (ENOSPC);
148 
149 	grp.lg_linkid = linkid;
150 	grp.lg_key = key;
151 	bcopy(mac, grp.lg_mac, ETHERADDRL);
152 	grp.lg_mac_fixed = mac_fixed;
153 	grp.lg_force = force;
154 	grp.lg_policy = policy;
155 	grp.lg_nports = nports;
156 	grp.lg_lacp_mode = lacp_mode;
157 	grp.lg_lacp_timer = lacp_timer;
158 
159 	if (ddi_copyout(&grp, state->where, sizeof (grp), state->mode) != 0)
160 		return (EFAULT);
161 
162 	state->where += sizeof (grp);
163 	state->bytes_left -= sizeof (grp);
164 
165 	return (0);
166 }
167 
168 static int
169 aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac,
170     aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
171 {
172 	aggr_ioc_info_state_t *state = arg;
173 	laioc_info_port_t port;
174 
175 	if (state->bytes_left < sizeof (port))
176 		return (ENOSPC);
177 
178 	port.lp_linkid = linkid;
179 	bcopy(mac, port.lp_mac, ETHERADDRL);
180 	port.lp_state = portstate;
181 	port.lp_lacp_state = *lacp_state;
182 
183 	if (ddi_copyout(&port, state->where, sizeof (port), state->mode) != 0)
184 		return (EFAULT);
185 
186 	state->where += sizeof (port);
187 	state->bytes_left -= sizeof (port);
188 
189 	return (0);
190 }
191 
192 /*ARGSUSED*/
193 static int
194 aggr_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred)
195 {
196 	laioc_info_t *info_argp = karg;
197 	datalink_id_t linkid;
198 	aggr_ioc_info_state_t state;
199 
200 	/*
201 	 * linkid of the group to return. Must not be DATALINK_INVALID_LINKID.
202 	 */
203 	if ((linkid = info_argp->li_group_linkid) == DATALINK_INVALID_LINKID)
204 		return (EINVAL);
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(linkid, &state, aggr_ioc_info_new_grp,
211 	    aggr_ioc_info_new_port));
212 }
213 
214 static int
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 	}
244 
245 done:
246 	kmem_free(ports, ports_size);
247 	return (rc);
248 }
249 
250 /* ARGSUSED */
251 static int
252 aggr_ioc_add(void *karg, intptr_t arg, int mode, cred_t *cred)
253 {
254 	return (aggr_ioc_add_remove(karg, arg, LAIOC_ADD, mode));
255 }
256 
257 /* ARGSUSED */
258 static int
259 aggr_ioc_remove(void *karg, intptr_t arg, int mode, cred_t *cred)
260 {
261 	return (aggr_ioc_add_remove(karg, arg, LAIOC_REMOVE, mode));
262 }
263 
264 static dld_ioc_info_t aggr_ioc_list[] = {
265 	{LAIOC_CREATE, DLDCOPYIN | DLDDLCONFIG, sizeof (laioc_create_t),
266 	    aggr_ioc_create},
267 	{LAIOC_DELETE, DLDCOPYIN | DLDDLCONFIG, sizeof (laioc_delete_t),
268 	    aggr_ioc_delete},
269 	{LAIOC_INFO, DLDCOPYINOUT, sizeof (laioc_info_t), aggr_ioc_info},
270 	{LAIOC_ADD, DLDCOPYIN | DLDDLCONFIG, sizeof (laioc_add_rem_t),
271 	    aggr_ioc_add},
272 	{LAIOC_REMOVE, DLDCOPYIN | DLDDLCONFIG, sizeof (laioc_add_rem_t),
273 	    aggr_ioc_remove},
274 	{LAIOC_MODIFY, DLDCOPYIN | DLDDLCONFIG, sizeof (laioc_modify_t),
275 	    aggr_ioc_modify}
276 };
277 
278 int
279 aggr_ioc_init(void)
280 {
281 	return (dld_ioc_register(AGGR_IOC, aggr_ioc_list,
282 	    DLDIOCCNT(aggr_ioc_list)));
283 }
284 
285 void
286 aggr_ioc_fini(void)
287 {
288 	dld_ioc_unregister(AGGR_IOC);
289 }
290