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 2009 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/policy.h>
33
34/*
35 * Process a LAIOC_MODIFY request.
36 */
37/* ARGSUSED */
38static int
39aggr_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 */
80static int
81aggr_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	    cred);
119
120done:
121	kmem_free(ports, ports_size);
122	return (rc);
123}
124
125/* ARGSUSED */
126static int
127aggr_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
128{
129	laioc_delete_t *delete_arg = karg;
130
131	return (aggr_grp_delete(delete_arg->ld_linkid, cred));
132}
133
134typedef struct aggr_ioc_info_state {
135	uint32_t	bytes_left;
136	uchar_t		*where;		/* in user buffer */
137	int		mode;
138} aggr_ioc_info_state_t;
139
140static int
141aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key,
142    uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy,
143    uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
144{
145	aggr_ioc_info_state_t *state = arg;
146	laioc_info_group_t grp;
147
148	if (state->bytes_left < sizeof (grp))
149		return (ENOSPC);
150
151	grp.lg_linkid = linkid;
152	grp.lg_key = key;
153	bcopy(mac, grp.lg_mac, ETHERADDRL);
154	grp.lg_mac_fixed = mac_fixed;
155	grp.lg_force = force;
156	grp.lg_policy = policy;
157	grp.lg_nports = nports;
158	grp.lg_lacp_mode = lacp_mode;
159	grp.lg_lacp_timer = lacp_timer;
160
161	if (ddi_copyout(&grp, state->where, sizeof (grp), state->mode) != 0)
162		return (EFAULT);
163
164	state->where += sizeof (grp);
165	state->bytes_left -= sizeof (grp);
166
167	return (0);
168}
169
170static int
171aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac,
172    aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
173{
174	aggr_ioc_info_state_t *state = arg;
175	laioc_info_port_t port;
176
177	if (state->bytes_left < sizeof (port))
178		return (ENOSPC);
179
180	port.lp_linkid = linkid;
181	bcopy(mac, port.lp_mac, ETHERADDRL);
182	port.lp_state = portstate;
183	port.lp_lacp_state = *lacp_state;
184
185	if (ddi_copyout(&port, state->where, sizeof (port), state->mode) != 0)
186		return (EFAULT);
187
188	state->where += sizeof (port);
189	state->bytes_left -= sizeof (port);
190
191	return (0);
192}
193
194/*ARGSUSED*/
195static int
196aggr_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
197{
198	laioc_info_t *info_argp = karg;
199	aggr_ioc_info_state_t state;
200
201	state.bytes_left = info_argp->li_bufsize - sizeof (laioc_info_t);
202	state.where = (uchar_t *)arg + sizeof (laioc_info_t);
203	state.mode = mode;
204
205	return (aggr_grp_info(info_argp->li_group_linkid, &state,
206	    aggr_ioc_info_new_grp, aggr_ioc_info_new_port, cred));
207}
208
209static int
210aggr_ioc_add_remove(laioc_add_rem_t *add_rem_arg, intptr_t arg, int cmd,
211    int mode)
212{
213	uint16_t nports;
214	laioc_port_t *ports = NULL;
215	size_t ports_size;
216	int rc;
217
218	nports = add_rem_arg->la_nports;
219	if (nports > AGGR_MAX_PORTS)
220		return (EINVAL);
221
222	ports_size = nports * sizeof (laioc_port_t);
223	ports = kmem_alloc(ports_size, KM_SLEEP);
224	if (ddi_copyin((uchar_t *)arg + sizeof (*add_rem_arg), ports,
225	    ports_size, mode) != 0) {
226		rc = EFAULT;
227		goto done;
228	}
229
230	switch (cmd) {
231	case LAIOC_ADD:
232		rc = aggr_grp_add_ports(add_rem_arg->la_linkid, nports,
233		    add_rem_arg->la_force, ports);
234		break;
235	case LAIOC_REMOVE:
236		rc = aggr_grp_rem_ports(add_rem_arg->la_linkid, nports, ports);
237		break;
238	}
239
240done:
241	kmem_free(ports, ports_size);
242	return (rc);
243}
244
245/* ARGSUSED */
246static int
247aggr_ioc_add(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
248{
249	return (aggr_ioc_add_remove(karg, arg, LAIOC_ADD, mode));
250}
251
252/* ARGSUSED */
253static int
254aggr_ioc_remove(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
255{
256	return (aggr_ioc_add_remove(karg, arg, LAIOC_REMOVE, mode));
257}
258
259static dld_ioc_info_t aggr_ioc_list[] = {
260	{LAIOC_CREATE, DLDCOPYIN, sizeof (laioc_create_t), aggr_ioc_create,
261	    secpolicy_dl_config},
262	{LAIOC_DELETE, DLDCOPYIN, sizeof (laioc_delete_t), aggr_ioc_delete,
263	    secpolicy_dl_config},
264	{LAIOC_INFO, DLDCOPYINOUT, sizeof (laioc_info_t), aggr_ioc_info, NULL},
265	{LAIOC_ADD, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_add,
266	    secpolicy_dl_config},
267	{LAIOC_REMOVE, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_remove,
268	    secpolicy_dl_config},
269	{LAIOC_MODIFY, DLDCOPYIN, sizeof (laioc_modify_t), aggr_ioc_modify,
270	    secpolicy_dl_config}
271};
272
273int
274aggr_ioc_init(void)
275{
276	return (dld_ioc_register(AGGR_IOC, aggr_ioc_list,
277	    DLDIOCCNT(aggr_ioc_list)));
278}
279
280void
281aggr_ioc_fini(void)
282{
283	dld_ioc_unregister(AGGR_IOC);
284}
285