xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision 34a4e6b5)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ba2e4443Sseb  * Common Development and Distribution License (the "License").
6ba2e4443Sseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
222b24ab6bSSebastien Roy  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * IEEE 802.3ad Link Aggregation -- IOCTL processing.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/aggr.h>
317c478bd9Sstevel@tonic-gate #include <sys/aggr_impl.h>
322b24ab6bSSebastien Roy #include <sys/policy.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate /*
357c478bd9Sstevel@tonic-gate  * Process a LAIOC_MODIFY request.
367c478bd9Sstevel@tonic-gate  */
37eae72b5bSSebastien Roy /* ARGSUSED */
387c478bd9Sstevel@tonic-gate static int
aggr_ioc_modify(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)39da14cebeSEric Cheng aggr_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
407c478bd9Sstevel@tonic-gate {
41eae72b5bSSebastien Roy 	laioc_modify_t *modify_arg = karg;
427c478bd9Sstevel@tonic-gate 	uint32_t policy;
437c478bd9Sstevel@tonic-gate 	boolean_t mac_fixed;
447c478bd9Sstevel@tonic-gate 	uchar_t mac_addr[ETHERADDRL];
457c478bd9Sstevel@tonic-gate 	uint8_t modify_mask_arg, modify_mask = 0;
467c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t lacp_mode;
477c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t lacp_timer;
487c478bd9Sstevel@tonic-gate 
49*34a4e6b5SToomas Soome 	policy = 0;
50*34a4e6b5SToomas Soome 	mac_fixed = B_FALSE;
51*34a4e6b5SToomas Soome 	lacp_mode = AGGR_LACP_OFF;
52*34a4e6b5SToomas Soome 	lacp_timer = AGGR_LACP_TIMER_LONG;
53*34a4e6b5SToomas Soome 
54eae72b5bSSebastien Roy 	modify_mask_arg = modify_arg->lu_modify_mask;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_POLICY) {
577c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_POLICY;
58eae72b5bSSebastien Roy 		policy = modify_arg->lu_policy;
597c478bd9Sstevel@tonic-gate 	}
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_MAC) {
627c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_MAC;
63eae72b5bSSebastien Roy 		bcopy(modify_arg->lu_mac, mac_addr, ETHERADDRL);
64eae72b5bSSebastien Roy 		mac_fixed = modify_arg->lu_mac_fixed;
657c478bd9Sstevel@tonic-gate 	}
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) {
687c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_LACP_MODE;
69eae72b5bSSebastien Roy 		lacp_mode = modify_arg->lu_lacp_mode;
707c478bd9Sstevel@tonic-gate 	}
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) {
737c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_LACP_TIMER;
74eae72b5bSSebastien Roy 		lacp_timer = modify_arg->lu_lacp_timer;
757c478bd9Sstevel@tonic-gate 	}
767c478bd9Sstevel@tonic-gate 
77da14cebeSEric Cheng 	return (aggr_grp_modify(modify_arg->lu_linkid, modify_mask, policy,
78da14cebeSEric Cheng 	    mac_fixed, mac_addr, lacp_mode, lacp_timer));
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * Process a LAIOC_CREATE request.
837c478bd9Sstevel@tonic-gate  */
84eae72b5bSSebastien Roy /* ARGSUSED */
857c478bd9Sstevel@tonic-gate static int
aggr_ioc_create(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)86da14cebeSEric Cheng aggr_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
877c478bd9Sstevel@tonic-gate {
88eae72b5bSSebastien Roy 	laioc_create_t *create_arg = karg;
897c478bd9Sstevel@tonic-gate 	uint16_t nports;
907c478bd9Sstevel@tonic-gate 	laioc_port_t *ports = NULL;
91eae72b5bSSebastien Roy 	size_t ports_size;
927c478bd9Sstevel@tonic-gate 	uint32_t policy;
937c478bd9Sstevel@tonic-gate 	boolean_t mac_fixed;
94d62bc4baSyz 	boolean_t force;
957c478bd9Sstevel@tonic-gate 	uchar_t mac_addr[ETHERADDRL];
967c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t lacp_mode;
977c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t lacp_timer;
98eae72b5bSSebastien Roy 	int rc;
997c478bd9Sstevel@tonic-gate 
100eae72b5bSSebastien Roy 	nports = create_arg->lc_nports;
1017c478bd9Sstevel@tonic-gate 	if (nports > AGGR_MAX_PORTS)
1027c478bd9Sstevel@tonic-gate 		return (EINVAL);
1037c478bd9Sstevel@tonic-gate 
104eae72b5bSSebastien Roy 	policy = create_arg->lc_policy;
105eae72b5bSSebastien Roy 	lacp_mode = create_arg->lc_lacp_mode;
106eae72b5bSSebastien Roy 	lacp_timer = create_arg->lc_lacp_timer;
1077c478bd9Sstevel@tonic-gate 
108eae72b5bSSebastien Roy 	ports_size = nports * sizeof (laioc_port_t);
109eae72b5bSSebastien Roy 	ports = kmem_alloc(ports_size, KM_SLEEP);
1107c478bd9Sstevel@tonic-gate 
111eae72b5bSSebastien Roy 	if (ddi_copyin((uchar_t *)arg + sizeof (*create_arg), ports,
112eae72b5bSSebastien Roy 	    ports_size, mode) != 0) {
113eae72b5bSSebastien Roy 		rc = EFAULT;
114eae72b5bSSebastien Roy 		goto done;
115eae72b5bSSebastien Roy 	}
1167c478bd9Sstevel@tonic-gate 
117eae72b5bSSebastien Roy 	bcopy(create_arg->lc_mac, mac_addr, ETHERADDRL);
118eae72b5bSSebastien Roy 	mac_fixed = create_arg->lc_mac_fixed;
119eae72b5bSSebastien Roy 	force = create_arg->lc_force;
1207c478bd9Sstevel@tonic-gate 
121eae72b5bSSebastien Roy 	rc = aggr_grp_create(create_arg->lc_linkid, create_arg->lc_key, nports,
1222b24ab6bSSebastien Roy 	    ports, policy, mac_fixed, force, mac_addr, lacp_mode, lacp_timer,
1232b24ab6bSSebastien Roy 	    cred);
1247c478bd9Sstevel@tonic-gate 
125eae72b5bSSebastien Roy done:
126eae72b5bSSebastien Roy 	kmem_free(ports, ports_size);
1277c478bd9Sstevel@tonic-gate 	return (rc);
1287c478bd9Sstevel@tonic-gate }
1297c478bd9Sstevel@tonic-gate 
130eae72b5bSSebastien Roy /* ARGSUSED */
1317c478bd9Sstevel@tonic-gate static int
aggr_ioc_delete(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)132da14cebeSEric Cheng aggr_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1337c478bd9Sstevel@tonic-gate {
134eae72b5bSSebastien Roy 	laioc_delete_t *delete_arg = karg;
1357c478bd9Sstevel@tonic-gate 
1362b24ab6bSSebastien Roy 	return (aggr_grp_delete(delete_arg->ld_linkid, cred));
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate typedef struct aggr_ioc_info_state {
140eae72b5bSSebastien Roy 	uint32_t	bytes_left;
141eae72b5bSSebastien Roy 	uchar_t		*where;		/* in user buffer */
142eae72b5bSSebastien Roy 	int		mode;
1437c478bd9Sstevel@tonic-gate } aggr_ioc_info_state_t;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate static int
aggr_ioc_info_new_grp(void * arg,datalink_id_t linkid,uint32_t key,uchar_t * mac,boolean_t mac_fixed,boolean_t force,uint32_t policy,uint32_t nports,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer)146d62bc4baSyz aggr_ioc_info_new_grp(void *arg, datalink_id_t linkid, uint32_t key,
147d62bc4baSyz     uchar_t *mac, boolean_t mac_fixed, boolean_t force, uint32_t policy,
148d62bc4baSyz     uint32_t nports, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
1497c478bd9Sstevel@tonic-gate {
1507c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t *state = arg;
1517c478bd9Sstevel@tonic-gate 	laioc_info_group_t grp;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	if (state->bytes_left < sizeof (grp))
1547c478bd9Sstevel@tonic-gate 		return (ENOSPC);
1557c478bd9Sstevel@tonic-gate 
156d62bc4baSyz 	grp.lg_linkid = linkid;
1577c478bd9Sstevel@tonic-gate 	grp.lg_key = key;
1587c478bd9Sstevel@tonic-gate 	bcopy(mac, grp.lg_mac, ETHERADDRL);
1597c478bd9Sstevel@tonic-gate 	grp.lg_mac_fixed = mac_fixed;
160d62bc4baSyz 	grp.lg_force = force;
1617c478bd9Sstevel@tonic-gate 	grp.lg_policy = policy;
1627c478bd9Sstevel@tonic-gate 	grp.lg_nports = nports;
1637c478bd9Sstevel@tonic-gate 	grp.lg_lacp_mode = lacp_mode;
1647c478bd9Sstevel@tonic-gate 	grp.lg_lacp_timer = lacp_timer;
1657c478bd9Sstevel@tonic-gate 
166eae72b5bSSebastien Roy 	if (ddi_copyout(&grp, state->where, sizeof (grp), state->mode) != 0)
167eae72b5bSSebastien Roy 		return (EFAULT);
168eae72b5bSSebastien Roy 
1697c478bd9Sstevel@tonic-gate 	state->where += sizeof (grp);
1707c478bd9Sstevel@tonic-gate 	state->bytes_left -= sizeof (grp);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	return (0);
1737c478bd9Sstevel@tonic-gate }
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate static int
aggr_ioc_info_new_port(void * arg,datalink_id_t linkid,uchar_t * mac,aggr_port_state_t portstate,aggr_lacp_state_t * lacp_state)176d62bc4baSyz aggr_ioc_info_new_port(void *arg, datalink_id_t linkid, uchar_t *mac,
177ba2e4443Sseb     aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t *state = arg;
1807c478bd9Sstevel@tonic-gate 	laioc_info_port_t port;
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	if (state->bytes_left < sizeof (port))
1837c478bd9Sstevel@tonic-gate 		return (ENOSPC);
1847c478bd9Sstevel@tonic-gate 
185d62bc4baSyz 	port.lp_linkid = linkid;
1867c478bd9Sstevel@tonic-gate 	bcopy(mac, port.lp_mac, ETHERADDRL);
1877c478bd9Sstevel@tonic-gate 	port.lp_state = portstate;
1887c478bd9Sstevel@tonic-gate 	port.lp_lacp_state = *lacp_state;
1897c478bd9Sstevel@tonic-gate 
190eae72b5bSSebastien Roy 	if (ddi_copyout(&port, state->where, sizeof (port), state->mode) != 0)
191eae72b5bSSebastien Roy 		return (EFAULT);
192eae72b5bSSebastien Roy 
1937c478bd9Sstevel@tonic-gate 	state->where += sizeof (port);
1947c478bd9Sstevel@tonic-gate 	state->bytes_left -= sizeof (port);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	return (0);
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2007c478bd9Sstevel@tonic-gate static int
aggr_ioc_info(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)201da14cebeSEric Cheng aggr_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
2027c478bd9Sstevel@tonic-gate {
203eae72b5bSSebastien Roy 	laioc_info_t *info_argp = karg;
2047c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t state;
2057c478bd9Sstevel@tonic-gate 
206eae72b5bSSebastien Roy 	state.bytes_left = info_argp->li_bufsize - sizeof (laioc_info_t);
207eae72b5bSSebastien Roy 	state.where = (uchar_t *)arg + sizeof (laioc_info_t);
208eae72b5bSSebastien Roy 	state.mode = mode;
2097c478bd9Sstevel@tonic-gate 
2102b24ab6bSSebastien Roy 	return (aggr_grp_info(info_argp->li_group_linkid, &state,
2112b24ab6bSSebastien Roy 	    aggr_ioc_info_new_grp, aggr_ioc_info_new_port, cred));
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate static int
aggr_ioc_add_remove(laioc_add_rem_t * add_rem_arg,intptr_t arg,int cmd,int mode)215eae72b5bSSebastien Roy aggr_ioc_add_remove(laioc_add_rem_t *add_rem_arg, intptr_t arg, int cmd,
216eae72b5bSSebastien Roy     int mode)
2177c478bd9Sstevel@tonic-gate {
218eae72b5bSSebastien Roy 	uint16_t nports;
2197c478bd9Sstevel@tonic-gate 	laioc_port_t *ports = NULL;
220eae72b5bSSebastien Roy 	size_t ports_size;
221eae72b5bSSebastien Roy 	int rc;
2227c478bd9Sstevel@tonic-gate 
223eae72b5bSSebastien Roy 	nports = add_rem_arg->la_nports;
2247c478bd9Sstevel@tonic-gate 	if (nports > AGGR_MAX_PORTS)
2257c478bd9Sstevel@tonic-gate 		return (EINVAL);
2267c478bd9Sstevel@tonic-gate 
227eae72b5bSSebastien Roy 	ports_size = nports * sizeof (laioc_port_t);
228eae72b5bSSebastien Roy 	ports = kmem_alloc(ports_size, KM_SLEEP);
229eae72b5bSSebastien Roy 	if (ddi_copyin((uchar_t *)arg + sizeof (*add_rem_arg), ports,
230eae72b5bSSebastien Roy 	    ports_size, mode) != 0) {
231eae72b5bSSebastien Roy 		rc = EFAULT;
232eae72b5bSSebastien Roy 		goto done;
233eae72b5bSSebastien Roy 	}
2347c478bd9Sstevel@tonic-gate 
235eae72b5bSSebastien Roy 	switch (cmd) {
236eae72b5bSSebastien Roy 	case LAIOC_ADD:
237eae72b5bSSebastien Roy 		rc = aggr_grp_add_ports(add_rem_arg->la_linkid, nports,
238eae72b5bSSebastien Roy 		    add_rem_arg->la_force, ports);
239eae72b5bSSebastien Roy 		break;
240eae72b5bSSebastien Roy 	case LAIOC_REMOVE:
241eae72b5bSSebastien Roy 		rc = aggr_grp_rem_ports(add_rem_arg->la_linkid, nports, ports);
242eae72b5bSSebastien Roy 		break;
243*34a4e6b5SToomas Soome 	default:
244*34a4e6b5SToomas Soome 		rc = 0;
245*34a4e6b5SToomas Soome 		break;
246eae72b5bSSebastien Roy 	}
2477c478bd9Sstevel@tonic-gate 
248eae72b5bSSebastien Roy done:
249eae72b5bSSebastien Roy 	kmem_free(ports, ports_size);
2507c478bd9Sstevel@tonic-gate 	return (rc);
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate 
253eae72b5bSSebastien Roy /* ARGSUSED */
2547c478bd9Sstevel@tonic-gate static int
aggr_ioc_add(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)255da14cebeSEric Cheng aggr_ioc_add(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
2567c478bd9Sstevel@tonic-gate {
257eae72b5bSSebastien Roy 	return (aggr_ioc_add_remove(karg, arg, LAIOC_ADD, mode));
258eae72b5bSSebastien Roy }
2597c478bd9Sstevel@tonic-gate 
260eae72b5bSSebastien Roy /* ARGSUSED */
261eae72b5bSSebastien Roy static int
aggr_ioc_remove(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)262da14cebeSEric Cheng aggr_ioc_remove(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
263eae72b5bSSebastien Roy {
264eae72b5bSSebastien Roy 	return (aggr_ioc_add_remove(karg, arg, LAIOC_REMOVE, mode));
265eae72b5bSSebastien Roy }
2667c478bd9Sstevel@tonic-gate 
267eae72b5bSSebastien Roy static dld_ioc_info_t aggr_ioc_list[] = {
268da14cebeSEric Cheng 	{LAIOC_CREATE, DLDCOPYIN, sizeof (laioc_create_t), aggr_ioc_create,
2692b24ab6bSSebastien Roy 	    secpolicy_dl_config},
270da14cebeSEric Cheng 	{LAIOC_DELETE, DLDCOPYIN, sizeof (laioc_delete_t), aggr_ioc_delete,
2712b24ab6bSSebastien Roy 	    secpolicy_dl_config},
2722b24ab6bSSebastien Roy 	{LAIOC_INFO, DLDCOPYINOUT, sizeof (laioc_info_t), aggr_ioc_info, NULL},
273da14cebeSEric Cheng 	{LAIOC_ADD, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_add,
2742b24ab6bSSebastien Roy 	    secpolicy_dl_config},
275da14cebeSEric Cheng 	{LAIOC_REMOVE, DLDCOPYIN, sizeof (laioc_add_rem_t), aggr_ioc_remove,
2762b24ab6bSSebastien Roy 	    secpolicy_dl_config},
277da14cebeSEric Cheng 	{LAIOC_MODIFY, DLDCOPYIN, sizeof (laioc_modify_t), aggr_ioc_modify,
2782b24ab6bSSebastien Roy 	    secpolicy_dl_config}
279eae72b5bSSebastien Roy };
280eae72b5bSSebastien Roy 
281eae72b5bSSebastien Roy int
aggr_ioc_init(void)282eae72b5bSSebastien Roy aggr_ioc_init(void)
283eae72b5bSSebastien Roy {
284eae72b5bSSebastien Roy 	return (dld_ioc_register(AGGR_IOC, aggr_ioc_list,
285eae72b5bSSebastien Roy 	    DLDIOCCNT(aggr_ioc_list)));
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
288210db224Sericheng void
aggr_ioc_fini(void)289eae72b5bSSebastien Roy aggr_ioc_fini(void)
2907c478bd9Sstevel@tonic-gate {
291eae72b5bSSebastien Roy 	dld_ioc_unregister(AGGR_IOC);
2927c478bd9Sstevel@tonic-gate }
293