xref: /onnv-gate/usr/src/uts/common/io/vnic/vnic_ctl.c (revision 11076:445f05f9f7b4)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev /*
2210616SSebastien.Roy@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235084Sjohnlev  * Use is subject to license terms.
245084Sjohnlev  */
255084Sjohnlev 
265084Sjohnlev /*
275084Sjohnlev  * Virtual Network Interface Card (VNIC)
285084Sjohnlev  */
295084Sjohnlev 
305084Sjohnlev #include <sys/conf.h>
315084Sjohnlev #include <sys/modctl.h>
325084Sjohnlev #include <sys/vnic.h>
335084Sjohnlev #include <sys/vnic_impl.h>
3410616SSebastien.Roy@Sun.COM #include <sys/policy.h>
355084Sjohnlev 
365084Sjohnlev /* module description */
378275SEric Cheng #define	VNIC_LINKINFO		"Virtual NIC"
385084Sjohnlev 
395084Sjohnlev /* device info ptr, only one for instance 0 */
405084Sjohnlev static dev_info_t *vnic_dip = NULL;
415084Sjohnlev static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
425084Sjohnlev static int vnic_attach(dev_info_t *, ddi_attach_cmd_t);
435084Sjohnlev static int vnic_detach(dev_info_t *, ddi_detach_cmd_t);
448275SEric Cheng 
458275SEric Cheng static int vnic_ioc_create(void *, intptr_t, int, cred_t *, int *);
468275SEric Cheng static int vnic_ioc_delete(void *, intptr_t, int, cred_t *, int *);
478275SEric Cheng static int vnic_ioc_info(void *, intptr_t, int, cred_t *, int *);
488275SEric Cheng static int vnic_ioc_modify(void *, intptr_t, int, cred_t *, int *);
495084Sjohnlev 
507408SSebastien.Roy@Sun.COM static dld_ioc_info_t vnic_ioc_list[] = {
518275SEric Cheng 	{VNIC_IOC_CREATE, DLDCOPYINOUT, sizeof (vnic_ioc_create_t),
5210616SSebastien.Roy@Sun.COM 	    vnic_ioc_create, secpolicy_dl_config},
538275SEric Cheng 	{VNIC_IOC_DELETE, DLDCOPYIN, sizeof (vnic_ioc_delete_t),
5410616SSebastien.Roy@Sun.COM 	    vnic_ioc_delete, secpolicy_dl_config},
557408SSebastien.Roy@Sun.COM 	{VNIC_IOC_INFO, DLDCOPYINOUT, sizeof (vnic_ioc_info_t),
5610616SSebastien.Roy@Sun.COM 	    vnic_ioc_info, NULL},
578275SEric Cheng 	{VNIC_IOC_MODIFY, DLDCOPYIN, sizeof (vnic_ioc_modify_t),
5810616SSebastien.Roy@Sun.COM 	    vnic_ioc_modify, secpolicy_dl_config}
595084Sjohnlev };
605084Sjohnlev 
618275SEric Cheng DDI_DEFINE_STREAM_OPS(vnic_dev_ops, nulldev, nulldev, vnic_attach, vnic_detach,
628275SEric Cheng     nodev, vnic_getinfo, D_MP, NULL, ddi_quiesce_not_supported);
635084Sjohnlev 
645084Sjohnlev static struct modldrv vnic_modldrv = {
655084Sjohnlev 	&mod_driverops,		/* Type of module.  This one is a driver */
665084Sjohnlev 	VNIC_LINKINFO,		/* short description */
675084Sjohnlev 	&vnic_dev_ops		/* driver specific ops */
685084Sjohnlev };
695084Sjohnlev 
705084Sjohnlev static struct modlinkage modlinkage = {
718275SEric Cheng 	MODREV_1, &vnic_modldrv, NULL
725084Sjohnlev };
735084Sjohnlev 
745084Sjohnlev int
_init(void)755084Sjohnlev _init(void)
765084Sjohnlev {
778275SEric Cheng 	int	status;
787408SSebastien.Roy@Sun.COM 
797408SSebastien.Roy@Sun.COM 	mac_init_ops(&vnic_dev_ops, "vnic");
808275SEric Cheng 	status = mod_install(&modlinkage);
818275SEric Cheng 	if (status != DDI_SUCCESS)
827408SSebastien.Roy@Sun.COM 		mac_fini_ops(&vnic_dev_ops);
838275SEric Cheng 
848275SEric Cheng 	return (status);
855084Sjohnlev }
865084Sjohnlev 
875084Sjohnlev int
_fini(void)885084Sjohnlev _fini(void)
895084Sjohnlev {
908275SEric Cheng 	int	status;
917408SSebastien.Roy@Sun.COM 
928275SEric Cheng 	status = mod_remove(&modlinkage);
938275SEric Cheng 	if (status == DDI_SUCCESS)
947408SSebastien.Roy@Sun.COM 		mac_fini_ops(&vnic_dev_ops);
958275SEric Cheng 
968275SEric Cheng 	return (status);
975084Sjohnlev }
985084Sjohnlev 
995084Sjohnlev int
_info(struct modinfo * modinfop)1005084Sjohnlev _info(struct modinfo *modinfop)
1015084Sjohnlev {
1025084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
1035084Sjohnlev }
1045084Sjohnlev 
1055084Sjohnlev static void
vnic_init(void)1065084Sjohnlev vnic_init(void)
1075084Sjohnlev {
1085084Sjohnlev 	vnic_dev_init();
1095084Sjohnlev }
1105084Sjohnlev 
1115084Sjohnlev static void
vnic_fini(void)1125084Sjohnlev vnic_fini(void)
1135084Sjohnlev {
1145084Sjohnlev 	vnic_dev_fini();
1155084Sjohnlev }
1165084Sjohnlev 
1175084Sjohnlev dev_info_t *
vnic_get_dip(void)1185084Sjohnlev vnic_get_dip(void)
1195084Sjohnlev {
1205084Sjohnlev 	return (vnic_dip);
1215084Sjohnlev }
1225084Sjohnlev 
1235084Sjohnlev /*ARGSUSED*/
1245084Sjohnlev static int
vnic_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)1255084Sjohnlev vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1265084Sjohnlev     void **result)
1275084Sjohnlev {
1285084Sjohnlev 	switch (infocmd) {
1295084Sjohnlev 	case DDI_INFO_DEVT2DEVINFO:
1305084Sjohnlev 		*result = vnic_dip;
1315084Sjohnlev 		return (DDI_SUCCESS);
1325084Sjohnlev 	case DDI_INFO_DEVT2INSTANCE:
1338275SEric Cheng 		*result = NULL;
1345084Sjohnlev 		return (DDI_SUCCESS);
1355084Sjohnlev 	}
1365084Sjohnlev 	return (DDI_FAILURE);
1375084Sjohnlev }
1385084Sjohnlev 
1395084Sjohnlev static int
vnic_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1405084Sjohnlev vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1415084Sjohnlev {
1425084Sjohnlev 	switch (cmd) {
1435084Sjohnlev 	case DDI_ATTACH:
1445084Sjohnlev 		if (ddi_get_instance(dip) != 0) {
1455084Sjohnlev 			/* we only allow instance 0 to attach */
1465084Sjohnlev 			return (DDI_FAILURE);
1475084Sjohnlev 		}
1487408SSebastien.Roy@Sun.COM 		if (dld_ioc_register(VNIC_IOC, vnic_ioc_list,
1497408SSebastien.Roy@Sun.COM 		    DLDIOCCNT(vnic_ioc_list)) != 0)
1505084Sjohnlev 			return (DDI_FAILURE);
1515084Sjohnlev 
1525084Sjohnlev 		vnic_dip = dip;
1535084Sjohnlev 		vnic_init();
1545084Sjohnlev 		return (DDI_SUCCESS);
1555084Sjohnlev 
1565084Sjohnlev 	case DDI_RESUME:
1575084Sjohnlev 		return (DDI_SUCCESS);
1585084Sjohnlev 
1595084Sjohnlev 	default:
1605084Sjohnlev 		return (DDI_FAILURE);
1615084Sjohnlev 	}
1625084Sjohnlev }
1635084Sjohnlev 
1645084Sjohnlev /*ARGSUSED*/
1655084Sjohnlev static int
vnic_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1665084Sjohnlev vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1675084Sjohnlev {
1685084Sjohnlev 	switch (cmd) {
1695084Sjohnlev 	case DDI_DETACH:
1705084Sjohnlev 		/*
1715084Sjohnlev 		 * Allow the VNIC instance to be detached only if there
1725084Sjohnlev 		 * are not VNICs configured.
1735084Sjohnlev 		 */
1745084Sjohnlev 		if (vnic_dev_count() > 0)
1755084Sjohnlev 			return (DDI_FAILURE);
1765084Sjohnlev 
1775084Sjohnlev 		vnic_dip = NULL;
1785084Sjohnlev 		vnic_fini();
1797408SSebastien.Roy@Sun.COM 		dld_ioc_unregister(VNIC_IOC);
1805084Sjohnlev 		return (DDI_SUCCESS);
1815084Sjohnlev 
1825084Sjohnlev 	case DDI_SUSPEND:
1835084Sjohnlev 		return (DDI_SUCCESS);
1845084Sjohnlev 
1855084Sjohnlev 	default:
1865084Sjohnlev 		return (DDI_FAILURE);
1875084Sjohnlev 	}
1885084Sjohnlev }
1895084Sjohnlev 
1905084Sjohnlev /*
1918275SEric Cheng  * Process a VNICIOC_CREATE request.
1925084Sjohnlev  */
1937408SSebastien.Roy@Sun.COM /* ARGSUSED */
1945895Syz147064 static int
vnic_ioc_create(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)1958275SEric Cheng vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1965084Sjohnlev {
1977408SSebastien.Roy@Sun.COM 	vnic_ioc_create_t *create_arg = karg;
1988275SEric Cheng 	int err = 0, mac_len = 0, mac_slot;
1995084Sjohnlev 	uchar_t mac_addr[MAXMACADDRLEN];
2008275SEric Cheng 	uint_t mac_prefix_len;
2015084Sjohnlev 	vnic_mac_addr_type_t mac_addr_type;
2028275SEric Cheng 	vnic_ioc_diag_t diag = VNIC_IOC_DIAG_NONE;
2038275SEric Cheng 	boolean_t is_anchor = create_arg->vc_flags & VNIC_IOC_CREATE_ANCHOR;
2045084Sjohnlev 
2055084Sjohnlev 	/* MAC address */
2067408SSebastien.Roy@Sun.COM 	mac_addr_type = create_arg->vc_mac_addr_type;
2078275SEric Cheng 
2088275SEric Cheng 	if (is_anchor)
2098275SEric Cheng 		goto create;
2105084Sjohnlev 
2115084Sjohnlev 	switch (mac_addr_type) {
2125084Sjohnlev 	case VNIC_MAC_ADDR_TYPE_FIXED:
213*11076SCathy.Zhou@Sun.COM 	case VNIC_MAC_ADDR_TYPE_VRID:
2148275SEric Cheng 		mac_len = create_arg->vc_mac_len;
2158275SEric Cheng 		/*
2168275SEric Cheng 		 * Sanity check the MAC address length. vnic_dev_create()
2178275SEric Cheng 		 * will perform additional checks to ensure that the
2188275SEric Cheng 		 * address is a valid unicast address of the appropriate
2198275SEric Cheng 		 * length.
2208275SEric Cheng 		 */
2218275SEric Cheng 		if (mac_len == 0 || mac_len > MAXMACADDRLEN) {
2228275SEric Cheng 			err = EINVAL;
2238275SEric Cheng 			diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID;
2248275SEric Cheng 			goto bail;
2258275SEric Cheng 		}
2267408SSebastien.Roy@Sun.COM 		bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
2275084Sjohnlev 		break;
2288275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_FACTORY:
2298275SEric Cheng 		mac_slot = create_arg->vc_mac_slot;
2308275SEric Cheng 		/* sanity check the specified slot number */
2318275SEric Cheng 		if (mac_slot < 0 && mac_slot != -1) {
2328275SEric Cheng 			err = EINVAL;
2338275SEric Cheng 			diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
2348275SEric Cheng 			goto bail;
2358275SEric Cheng 		}
2368275SEric Cheng 		break;
2378275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_AUTO:
2388275SEric Cheng 		mac_slot = -1;
2398275SEric Cheng 		/* FALLTHROUGH */
2408275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_RANDOM:
2418275SEric Cheng 		mac_prefix_len = create_arg->vc_mac_prefix_len;
2428275SEric Cheng 		if (mac_prefix_len > MAXMACADDRLEN) {
2438275SEric Cheng 			err = EINVAL;
2448275SEric Cheng 			diag = VNIC_IOC_DIAG_MACPREFIXLEN_INVALID;
2458275SEric Cheng 			goto bail;
2468275SEric Cheng 		}
2478275SEric Cheng 		mac_len = create_arg->vc_mac_len;
2488275SEric Cheng 		if (mac_len > MAXMACADDRLEN) {
2498275SEric Cheng 			err = EINVAL;
2508275SEric Cheng 			diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID;
2518275SEric Cheng 			goto bail;
2528275SEric Cheng 		}
2538275SEric Cheng 		bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
2548275SEric Cheng 		break;
2558275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_PRIMARY:
2568275SEric Cheng 		/*
2578275SEric Cheng 		 * We will get the primary address when we add this
2588275SEric Cheng 		 * client
2598275SEric Cheng 		 */
2608275SEric Cheng 		break;
2615084Sjohnlev 	default:
2628275SEric Cheng 		err = ENOTSUP;
2638275SEric Cheng 		goto bail;
2645084Sjohnlev 	}
2655084Sjohnlev 
2668275SEric Cheng create:
2678275SEric Cheng 	err = vnic_dev_create(create_arg->vc_vnic_id, create_arg->vc_link_id,
2688275SEric Cheng 	    &mac_addr_type, &mac_len, mac_addr, &mac_slot, mac_prefix_len,
269*11076SCathy.Zhou@Sun.COM 	    create_arg->vc_vid, create_arg->vc_vrid, create_arg->vc_af,
270*11076SCathy.Zhou@Sun.COM 	    &create_arg->vc_resource_props, create_arg->vc_flags, &diag,
271*11076SCathy.Zhou@Sun.COM 	    cred);
272*11076SCathy.Zhou@Sun.COM 
273*11076SCathy.Zhou@Sun.COM 
2748275SEric Cheng 	if (err != 0)
2758275SEric Cheng 		goto bail;
2768275SEric Cheng 
2778275SEric Cheng 	create_arg->vc_mac_addr_type = mac_addr_type;
2785084Sjohnlev 
2798275SEric Cheng 	if (is_anchor)
2808275SEric Cheng 		goto bail;
2815084Sjohnlev 
2828275SEric Cheng 	switch (mac_addr_type) {
2838275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_FACTORY:
2848275SEric Cheng 		create_arg->vc_mac_slot = mac_slot;
2858275SEric Cheng 		break;
2868275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_RANDOM:
2878275SEric Cheng 		bcopy(mac_addr, create_arg->vc_mac_addr, MAXMACADDRLEN);
2888275SEric Cheng 		create_arg->vc_mac_len = mac_len;
2898275SEric Cheng 		break;
2905084Sjohnlev 	}
2915084Sjohnlev 
2928275SEric Cheng bail:
2938275SEric Cheng 	create_arg->vc_diag = diag;
2948275SEric Cheng 	create_arg->vc_status = err;
2958275SEric Cheng 	return (err);
2965084Sjohnlev }
2975084Sjohnlev 
2987408SSebastien.Roy@Sun.COM /* ARGSUSED */
2995895Syz147064 static int
vnic_ioc_modify(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)3008275SEric Cheng vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
3015084Sjohnlev {
3028275SEric Cheng 	vnic_ioc_modify_t *modify_arg = karg;
3035084Sjohnlev 
3048275SEric Cheng 	return (vnic_dev_modify(modify_arg->vm_vnic_id,
3058275SEric Cheng 	    modify_arg->vm_modify_mask, modify_arg->vm_mac_addr_type,
3068275SEric Cheng 	    modify_arg->vm_mac_len, modify_arg->vm_mac_addr,
3078275SEric Cheng 	    modify_arg->vm_mac_slot, &modify_arg->vm_resource_props));
3085084Sjohnlev }
3095084Sjohnlev 
3105895Syz147064 /* ARGSUSED */
3115895Syz147064 static int
vnic_ioc_delete(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)3128275SEric Cheng vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
3135084Sjohnlev {
3148275SEric Cheng 	vnic_ioc_delete_t *delete_arg = karg;
3158275SEric Cheng 
31610616SSebastien.Roy@Sun.COM 	return (vnic_dev_delete(delete_arg->vd_vnic_id, 0, cred));
3178275SEric Cheng }
3185084Sjohnlev 
3198275SEric Cheng /* ARGSUSED */
3208275SEric Cheng static int
vnic_ioc_info(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)3218275SEric Cheng vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
3228275SEric Cheng {
3238275SEric Cheng 	vnic_ioc_info_t *info_arg = karg;
3245084Sjohnlev 
32510616SSebastien.Roy@Sun.COM 	return (vnic_info(&info_arg->vi_info, cred));
3265084Sjohnlev }
327