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