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