1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev /* 222b24ab6bSSebastien Roy * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23843e1988Sjohnlev * Use is subject to license terms. 24843e1988Sjohnlev */ 25843e1988Sjohnlev 26843e1988Sjohnlev /* 27843e1988Sjohnlev * Virtual Network Interface Card (VNIC) 28843e1988Sjohnlev */ 29843e1988Sjohnlev 30843e1988Sjohnlev #include <sys/conf.h> 31843e1988Sjohnlev #include <sys/modctl.h> 32843e1988Sjohnlev #include <sys/vnic.h> 33843e1988Sjohnlev #include <sys/vnic_impl.h> 342b24ab6bSSebastien Roy #include <sys/policy.h> 35843e1988Sjohnlev 36843e1988Sjohnlev /* module description */ 37da14cebeSEric Cheng #define VNIC_LINKINFO "Virtual NIC" 38843e1988Sjohnlev 39843e1988Sjohnlev /* device info ptr, only one for instance 0 */ 40843e1988Sjohnlev static dev_info_t *vnic_dip = NULL; 41843e1988Sjohnlev static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 42843e1988Sjohnlev static int vnic_attach(dev_info_t *, ddi_attach_cmd_t); 43843e1988Sjohnlev static int vnic_detach(dev_info_t *, ddi_detach_cmd_t); 44da14cebeSEric Cheng 45da14cebeSEric Cheng static int vnic_ioc_create(void *, intptr_t, int, cred_t *, int *); 46da14cebeSEric Cheng static int vnic_ioc_delete(void *, intptr_t, int, cred_t *, int *); 47da14cebeSEric Cheng static int vnic_ioc_info(void *, intptr_t, int, cred_t *, int *); 48da14cebeSEric Cheng static int vnic_ioc_modify(void *, intptr_t, int, cred_t *, int *); 49843e1988Sjohnlev 50eae72b5bSSebastien Roy static dld_ioc_info_t vnic_ioc_list[] = { 51da14cebeSEric Cheng {VNIC_IOC_CREATE, DLDCOPYINOUT, sizeof (vnic_ioc_create_t), 522b24ab6bSSebastien Roy vnic_ioc_create, secpolicy_dl_config}, 53da14cebeSEric Cheng {VNIC_IOC_DELETE, DLDCOPYIN, sizeof (vnic_ioc_delete_t), 542b24ab6bSSebastien Roy vnic_ioc_delete, secpolicy_dl_config}, 55eae72b5bSSebastien Roy {VNIC_IOC_INFO, DLDCOPYINOUT, sizeof (vnic_ioc_info_t), 562b24ab6bSSebastien Roy vnic_ioc_info, NULL}, 57da14cebeSEric Cheng {VNIC_IOC_MODIFY, DLDCOPYIN, sizeof (vnic_ioc_modify_t), 582b24ab6bSSebastien Roy vnic_ioc_modify, secpolicy_dl_config} 59843e1988Sjohnlev }; 60843e1988Sjohnlev 61da14cebeSEric Cheng DDI_DEFINE_STREAM_OPS(vnic_dev_ops, nulldev, nulldev, vnic_attach, vnic_detach, 62da14cebeSEric Cheng nodev, vnic_getinfo, D_MP, NULL, ddi_quiesce_not_supported); 63843e1988Sjohnlev 64843e1988Sjohnlev static struct modldrv vnic_modldrv = { 65843e1988Sjohnlev &mod_driverops, /* Type of module. This one is a driver */ 66843e1988Sjohnlev VNIC_LINKINFO, /* short description */ 67843e1988Sjohnlev &vnic_dev_ops /* driver specific ops */ 68843e1988Sjohnlev }; 69843e1988Sjohnlev 70843e1988Sjohnlev static struct modlinkage modlinkage = { 71da14cebeSEric Cheng MODREV_1, &vnic_modldrv, NULL 72843e1988Sjohnlev }; 73843e1988Sjohnlev 74843e1988Sjohnlev int 75843e1988Sjohnlev _init(void) 76843e1988Sjohnlev { 77da14cebeSEric Cheng int status; 78eae72b5bSSebastien Roy 79eae72b5bSSebastien Roy mac_init_ops(&vnic_dev_ops, "vnic"); 80da14cebeSEric Cheng status = mod_install(&modlinkage); 81da14cebeSEric Cheng if (status != DDI_SUCCESS) 82eae72b5bSSebastien Roy mac_fini_ops(&vnic_dev_ops); 83da14cebeSEric Cheng 84da14cebeSEric Cheng return (status); 85843e1988Sjohnlev } 86843e1988Sjohnlev 87843e1988Sjohnlev int 88843e1988Sjohnlev _fini(void) 89843e1988Sjohnlev { 90da14cebeSEric Cheng int status; 91eae72b5bSSebastien Roy 92da14cebeSEric Cheng status = mod_remove(&modlinkage); 93da14cebeSEric Cheng if (status == DDI_SUCCESS) 94eae72b5bSSebastien Roy mac_fini_ops(&vnic_dev_ops); 95da14cebeSEric Cheng 96da14cebeSEric Cheng return (status); 97843e1988Sjohnlev } 98843e1988Sjohnlev 99843e1988Sjohnlev int 100843e1988Sjohnlev _info(struct modinfo *modinfop) 101843e1988Sjohnlev { 102843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 103843e1988Sjohnlev } 104843e1988Sjohnlev 105843e1988Sjohnlev static void 106843e1988Sjohnlev vnic_init(void) 107843e1988Sjohnlev { 108843e1988Sjohnlev vnic_dev_init(); 109843e1988Sjohnlev } 110843e1988Sjohnlev 111843e1988Sjohnlev static void 112843e1988Sjohnlev vnic_fini(void) 113843e1988Sjohnlev { 114843e1988Sjohnlev vnic_dev_fini(); 115843e1988Sjohnlev } 116843e1988Sjohnlev 117843e1988Sjohnlev dev_info_t * 118843e1988Sjohnlev vnic_get_dip(void) 119843e1988Sjohnlev { 120843e1988Sjohnlev return (vnic_dip); 121843e1988Sjohnlev } 122843e1988Sjohnlev 123843e1988Sjohnlev /*ARGSUSED*/ 124843e1988Sjohnlev static int 125843e1988Sjohnlev vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 126843e1988Sjohnlev void **result) 127843e1988Sjohnlev { 128843e1988Sjohnlev switch (infocmd) { 129843e1988Sjohnlev case DDI_INFO_DEVT2DEVINFO: 130843e1988Sjohnlev *result = vnic_dip; 131843e1988Sjohnlev return (DDI_SUCCESS); 132843e1988Sjohnlev case DDI_INFO_DEVT2INSTANCE: 133da14cebeSEric Cheng *result = NULL; 134843e1988Sjohnlev return (DDI_SUCCESS); 135843e1988Sjohnlev } 136843e1988Sjohnlev return (DDI_FAILURE); 137843e1988Sjohnlev } 138843e1988Sjohnlev 139843e1988Sjohnlev static int 140843e1988Sjohnlev vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 141843e1988Sjohnlev { 142843e1988Sjohnlev switch (cmd) { 143843e1988Sjohnlev case DDI_ATTACH: 144843e1988Sjohnlev if (ddi_get_instance(dip) != 0) { 145843e1988Sjohnlev /* we only allow instance 0 to attach */ 146843e1988Sjohnlev return (DDI_FAILURE); 147843e1988Sjohnlev } 148eae72b5bSSebastien Roy if (dld_ioc_register(VNIC_IOC, vnic_ioc_list, 149eae72b5bSSebastien Roy DLDIOCCNT(vnic_ioc_list)) != 0) 150843e1988Sjohnlev return (DDI_FAILURE); 151843e1988Sjohnlev 152843e1988Sjohnlev vnic_dip = dip; 153843e1988Sjohnlev vnic_init(); 154843e1988Sjohnlev return (DDI_SUCCESS); 155843e1988Sjohnlev 156843e1988Sjohnlev case DDI_RESUME: 157843e1988Sjohnlev return (DDI_SUCCESS); 158843e1988Sjohnlev 159843e1988Sjohnlev default: 160843e1988Sjohnlev return (DDI_FAILURE); 161843e1988Sjohnlev } 162843e1988Sjohnlev } 163843e1988Sjohnlev 164843e1988Sjohnlev /*ARGSUSED*/ 165843e1988Sjohnlev static int 166843e1988Sjohnlev vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 167843e1988Sjohnlev { 168843e1988Sjohnlev switch (cmd) { 169843e1988Sjohnlev case DDI_DETACH: 170843e1988Sjohnlev /* 171843e1988Sjohnlev * Allow the VNIC instance to be detached only if there 172843e1988Sjohnlev * are not VNICs configured. 173843e1988Sjohnlev */ 174843e1988Sjohnlev if (vnic_dev_count() > 0) 175843e1988Sjohnlev return (DDI_FAILURE); 176843e1988Sjohnlev 177843e1988Sjohnlev vnic_dip = NULL; 178843e1988Sjohnlev vnic_fini(); 179eae72b5bSSebastien Roy dld_ioc_unregister(VNIC_IOC); 180843e1988Sjohnlev return (DDI_SUCCESS); 181843e1988Sjohnlev 182843e1988Sjohnlev case DDI_SUSPEND: 183843e1988Sjohnlev return (DDI_SUCCESS); 184843e1988Sjohnlev 185843e1988Sjohnlev default: 186843e1988Sjohnlev return (DDI_FAILURE); 187843e1988Sjohnlev } 188843e1988Sjohnlev } 189843e1988Sjohnlev 190843e1988Sjohnlev /* 191da14cebeSEric Cheng * Process a VNICIOC_CREATE request. 192843e1988Sjohnlev */ 193eae72b5bSSebastien Roy /* ARGSUSED */ 194d62bc4baSyz147064 static int 195da14cebeSEric Cheng vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 196843e1988Sjohnlev { 197eae72b5bSSebastien Roy vnic_ioc_create_t *create_arg = karg; 198da14cebeSEric Cheng int err = 0, mac_len = 0, mac_slot; 199843e1988Sjohnlev uchar_t mac_addr[MAXMACADDRLEN]; 200da14cebeSEric Cheng uint_t mac_prefix_len; 201843e1988Sjohnlev vnic_mac_addr_type_t mac_addr_type; 202da14cebeSEric Cheng vnic_ioc_diag_t diag = VNIC_IOC_DIAG_NONE; 203da14cebeSEric Cheng boolean_t is_anchor = create_arg->vc_flags & VNIC_IOC_CREATE_ANCHOR; 204843e1988Sjohnlev 205843e1988Sjohnlev /* MAC address */ 206eae72b5bSSebastien Roy mac_addr_type = create_arg->vc_mac_addr_type; 207da14cebeSEric Cheng 208da14cebeSEric Cheng if (is_anchor) 209da14cebeSEric Cheng goto create; 210843e1988Sjohnlev 211843e1988Sjohnlev switch (mac_addr_type) { 212843e1988Sjohnlev case VNIC_MAC_ADDR_TYPE_FIXED: 213*1cb875aeSCathy Zhou case VNIC_MAC_ADDR_TYPE_VRID: 214da14cebeSEric Cheng mac_len = create_arg->vc_mac_len; 215da14cebeSEric Cheng /* 216da14cebeSEric Cheng * Sanity check the MAC address length. vnic_dev_create() 217da14cebeSEric Cheng * will perform additional checks to ensure that the 218da14cebeSEric Cheng * address is a valid unicast address of the appropriate 219da14cebeSEric Cheng * length. 220da14cebeSEric Cheng */ 221da14cebeSEric Cheng if (mac_len == 0 || mac_len > MAXMACADDRLEN) { 222da14cebeSEric Cheng err = EINVAL; 223da14cebeSEric Cheng diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID; 224da14cebeSEric Cheng goto bail; 225da14cebeSEric Cheng } 226eae72b5bSSebastien Roy bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN); 227843e1988Sjohnlev break; 228da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_FACTORY: 229da14cebeSEric Cheng mac_slot = create_arg->vc_mac_slot; 230da14cebeSEric Cheng /* sanity check the specified slot number */ 231da14cebeSEric Cheng if (mac_slot < 0 && mac_slot != -1) { 232da14cebeSEric Cheng err = EINVAL; 233da14cebeSEric Cheng diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID; 234da14cebeSEric Cheng goto bail; 235da14cebeSEric Cheng } 236da14cebeSEric Cheng break; 237da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_AUTO: 238da14cebeSEric Cheng mac_slot = -1; 239da14cebeSEric Cheng /* FALLTHROUGH */ 240da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_RANDOM: 241da14cebeSEric Cheng mac_prefix_len = create_arg->vc_mac_prefix_len; 242da14cebeSEric Cheng if (mac_prefix_len > MAXMACADDRLEN) { 243da14cebeSEric Cheng err = EINVAL; 244da14cebeSEric Cheng diag = VNIC_IOC_DIAG_MACPREFIXLEN_INVALID; 245da14cebeSEric Cheng goto bail; 246da14cebeSEric Cheng } 247da14cebeSEric Cheng mac_len = create_arg->vc_mac_len; 248da14cebeSEric Cheng if (mac_len > MAXMACADDRLEN) { 249da14cebeSEric Cheng err = EINVAL; 250da14cebeSEric Cheng diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID; 251da14cebeSEric Cheng goto bail; 252da14cebeSEric Cheng } 253da14cebeSEric Cheng bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN); 254da14cebeSEric Cheng break; 255da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_PRIMARY: 256da14cebeSEric Cheng /* 257da14cebeSEric Cheng * We will get the primary address when we add this 258da14cebeSEric Cheng * client 259da14cebeSEric Cheng */ 260da14cebeSEric Cheng break; 261843e1988Sjohnlev default: 262da14cebeSEric Cheng err = ENOTSUP; 263da14cebeSEric Cheng goto bail; 264843e1988Sjohnlev } 265843e1988Sjohnlev 266da14cebeSEric Cheng create: 267da14cebeSEric Cheng err = vnic_dev_create(create_arg->vc_vnic_id, create_arg->vc_link_id, 268da14cebeSEric Cheng &mac_addr_type, &mac_len, mac_addr, &mac_slot, mac_prefix_len, 269*1cb875aeSCathy Zhou create_arg->vc_vid, create_arg->vc_vrid, create_arg->vc_af, 270*1cb875aeSCathy Zhou &create_arg->vc_resource_props, create_arg->vc_flags, &diag, 271*1cb875aeSCathy Zhou cred); 272*1cb875aeSCathy Zhou 273*1cb875aeSCathy Zhou 274da14cebeSEric Cheng if (err != 0) 275da14cebeSEric Cheng goto bail; 276da14cebeSEric Cheng 277da14cebeSEric Cheng create_arg->vc_mac_addr_type = mac_addr_type; 278da14cebeSEric Cheng 279da14cebeSEric Cheng if (is_anchor) 280da14cebeSEric Cheng goto bail; 281da14cebeSEric Cheng 282da14cebeSEric Cheng switch (mac_addr_type) { 283da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_FACTORY: 284da14cebeSEric Cheng create_arg->vc_mac_slot = mac_slot; 285da14cebeSEric Cheng break; 286da14cebeSEric Cheng case VNIC_MAC_ADDR_TYPE_RANDOM: 287da14cebeSEric Cheng bcopy(mac_addr, create_arg->vc_mac_addr, MAXMACADDRLEN); 288da14cebeSEric Cheng create_arg->vc_mac_len = mac_len; 289da14cebeSEric Cheng break; 290da14cebeSEric Cheng } 291da14cebeSEric Cheng 292da14cebeSEric Cheng bail: 293da14cebeSEric Cheng create_arg->vc_diag = diag; 294da14cebeSEric Cheng create_arg->vc_status = err; 295da14cebeSEric Cheng return (err); 296843e1988Sjohnlev } 297843e1988Sjohnlev 298eae72b5bSSebastien Roy /* ARGSUSED */ 299d62bc4baSyz147064 static int 300da14cebeSEric Cheng vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 301843e1988Sjohnlev { 302eae72b5bSSebastien Roy vnic_ioc_modify_t *modify_arg = karg; 303843e1988Sjohnlev 304da14cebeSEric Cheng return (vnic_dev_modify(modify_arg->vm_vnic_id, 305da14cebeSEric Cheng modify_arg->vm_modify_mask, modify_arg->vm_mac_addr_type, 306da14cebeSEric Cheng modify_arg->vm_mac_len, modify_arg->vm_mac_addr, 307da14cebeSEric Cheng modify_arg->vm_mac_slot, &modify_arg->vm_resource_props)); 308843e1988Sjohnlev } 309843e1988Sjohnlev 310eae72b5bSSebastien Roy /* ARGSUSED */ 311d62bc4baSyz147064 static int 312da14cebeSEric Cheng vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 313843e1988Sjohnlev { 314eae72b5bSSebastien Roy vnic_ioc_delete_t *delete_arg = karg; 315843e1988Sjohnlev 3162b24ab6bSSebastien Roy return (vnic_dev_delete(delete_arg->vd_vnic_id, 0, cred)); 317843e1988Sjohnlev } 318843e1988Sjohnlev 319d62bc4baSyz147064 /* ARGSUSED */ 320d62bc4baSyz147064 static int 321da14cebeSEric Cheng vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 322843e1988Sjohnlev { 323da14cebeSEric Cheng vnic_ioc_info_t *info_arg = karg; 324843e1988Sjohnlev 3252b24ab6bSSebastien Roy return (vnic_info(&info_arg->vi_info, cred)); 326843e1988Sjohnlev } 327