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  * Virtual Network Interface Card (VNIC)
  28  */
  29 
  30 #include <sys/conf.h>
  31 #include <sys/modctl.h>
  32 #include <sys/vnic.h>
  33 #include <sys/vnic_impl.h>
  34 #include <sys/policy.h>
  35 
  36 /* module description */
  37 #define VNIC_LINKINFO           "Virtual NIC"
  38 
  39 /* device info ptr, only one for instance 0 */
  40 static dev_info_t *vnic_dip = NULL;
  41 static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
  42 static int vnic_attach(dev_info_t *, ddi_attach_cmd_t);
  43 static int vnic_detach(dev_info_t *, ddi_detach_cmd_t);
  44 
  45 static int vnic_ioc_create(void *, intptr_t, int, cred_t *, int *);
  46 static int vnic_ioc_delete(void *, intptr_t, int, cred_t *, int *);
  47 static int vnic_ioc_info(void *, intptr_t, int, cred_t *, int *);
  48 static int vnic_ioc_modify(void *, intptr_t, int, cred_t *, int *);
  49 
  50 static dld_ioc_info_t vnic_ioc_list[] = {
  51         {VNIC_IOC_CREATE, DLDCOPYINOUT, sizeof (vnic_ioc_create_t),
  52             vnic_ioc_create, secpolicy_dl_config},
  53         {VNIC_IOC_DELETE, DLDCOPYIN, sizeof (vnic_ioc_delete_t),
  54             vnic_ioc_delete, secpolicy_dl_config},
  55         {VNIC_IOC_INFO, DLDCOPYINOUT, sizeof (vnic_ioc_info_t),
  56             vnic_ioc_info, NULL},
  57         {VNIC_IOC_MODIFY, DLDCOPYIN, sizeof (vnic_ioc_modify_t),
  58             vnic_ioc_modify, secpolicy_dl_config}
  59 };
  60 
  61 DDI_DEFINE_STREAM_OPS(vnic_dev_ops, nulldev, nulldev, vnic_attach, vnic_detach,
  62     nodev, vnic_getinfo, D_MP, NULL, ddi_quiesce_not_supported);
  63 
  64 static struct modldrv vnic_modldrv = {
  65         &mod_driverops,             /* Type of module.  This one is a driver */
  66         VNIC_LINKINFO,          /* short description */
  67         &vnic_dev_ops               /* driver specific ops */
  68 };
  69 
  70 static struct modlinkage modlinkage = {
  71         MODREV_1, { &vnic_modldrv, NULL }
  72 };
  73 
  74 int
  75 _init(void)
  76 {
  77         int     status;
  78 
  79         mac_init_ops(&vnic_dev_ops, "vnic");
  80         status = mod_install(&modlinkage);
  81         if (status != DDI_SUCCESS)
  82                 mac_fini_ops(&vnic_dev_ops);
  83 
  84         return (status);
  85 }
  86 
  87 int
  88 _fini(void)
  89 {
  90         int     status;
  91 
  92         status = mod_remove(&modlinkage);
  93         if (status == DDI_SUCCESS)
  94                 mac_fini_ops(&vnic_dev_ops);
  95 
  96         return (status);
  97 }
  98 
  99 int
 100 _info(struct modinfo *modinfop)
 101 {
 102         return (mod_info(&modlinkage, modinfop));
 103 }
 104 
 105 static void
 106 vnic_init(void)
 107 {
 108         vnic_dev_init();
 109 }
 110 
 111 static void
 112 vnic_fini(void)
 113 {
 114         vnic_dev_fini();
 115 }
 116 
 117 dev_info_t *
 118 vnic_get_dip(void)
 119 {
 120         return (vnic_dip);
 121 }
 122 
 123 /*ARGSUSED*/
 124 static int
 125 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 126     void **result)
 127 {
 128         switch (infocmd) {
 129         case DDI_INFO_DEVT2DEVINFO:
 130                 *result = vnic_dip;
 131                 return (DDI_SUCCESS);
 132         case DDI_INFO_DEVT2INSTANCE:
 133                 *result = NULL;
 134                 return (DDI_SUCCESS);
 135         }
 136         return (DDI_FAILURE);
 137 }
 138 
 139 static int
 140 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 141 {
 142         switch (cmd) {
 143         case DDI_ATTACH:
 144                 if (ddi_get_instance(dip) != 0) {
 145                         /* we only allow instance 0 to attach */
 146                         return (DDI_FAILURE);
 147                 }
 148                 if (dld_ioc_register(VNIC_IOC, vnic_ioc_list,
 149                     DLDIOCCNT(vnic_ioc_list)) != 0)
 150                         return (DDI_FAILURE);
 151 
 152                 vnic_dip = dip;
 153                 vnic_init();
 154                 return (DDI_SUCCESS);
 155 
 156         case DDI_RESUME:
 157                 return (DDI_SUCCESS);
 158 
 159         default:
 160                 return (DDI_FAILURE);
 161         }
 162 }
 163 
 164 /*ARGSUSED*/
 165 static int
 166 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 167 {
 168         switch (cmd) {
 169         case DDI_DETACH:
 170                 /*
 171                  * Allow the VNIC instance to be detached only if there
 172                  * are not VNICs configured.
 173                  */
 174                 if (vnic_dev_count() > 0)
 175                         return (DDI_FAILURE);
 176 
 177                 vnic_dip = NULL;
 178                 vnic_fini();
 179                 dld_ioc_unregister(VNIC_IOC);
 180                 return (DDI_SUCCESS);
 181 
 182         case DDI_SUSPEND:
 183                 return (DDI_SUCCESS);
 184 
 185         default:
 186                 return (DDI_FAILURE);
 187         }
 188 }
 189 
 190 /*
 191  * Process a VNICIOC_CREATE request.
 192  */
 193 /* ARGSUSED */
 194 static int
 195 vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 196 {
 197         vnic_ioc_create_t *create_arg = karg;
 198         int err = 0, mac_len = 0, mac_slot;
 199         uchar_t mac_addr[MAXMACADDRLEN];
 200         uint_t mac_prefix_len;
 201         vnic_mac_addr_type_t mac_addr_type;
 202         vnic_ioc_diag_t diag = VNIC_IOC_DIAG_NONE;
 203         boolean_t is_anchor = create_arg->vc_flags & VNIC_IOC_CREATE_ANCHOR;
 204 
 205         /* MAC address */
 206         mac_addr_type = create_arg->vc_mac_addr_type;
 207 
 208         if (is_anchor)
 209                 goto create;
 210 
 211         switch (mac_addr_type) {
 212         case VNIC_MAC_ADDR_TYPE_FIXED:
 213         case VNIC_MAC_ADDR_TYPE_VRID:
 214                 mac_len = create_arg->vc_mac_len;
 215                 /*
 216                  * Sanity check the MAC address length. vnic_dev_create()
 217                  * will perform additional checks to ensure that the
 218                  * address is a valid unicast address of the appropriate
 219                  * length.
 220                  */
 221                 if (mac_len == 0 || mac_len > MAXMACADDRLEN) {
 222                         err = EINVAL;
 223                         diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID;
 224                         goto bail;
 225                 }
 226                 bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
 227                 break;
 228         case VNIC_MAC_ADDR_TYPE_FACTORY:
 229                 mac_slot = create_arg->vc_mac_slot;
 230                 /* sanity check the specified slot number */
 231                 if (mac_slot < 0 && mac_slot != -1) {
 232                         err = EINVAL;
 233                         diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
 234                         goto bail;
 235                 }
 236                 break;
 237         case VNIC_MAC_ADDR_TYPE_AUTO:
 238                 mac_slot = -1;
 239                 /* FALLTHROUGH */
 240         case VNIC_MAC_ADDR_TYPE_RANDOM:
 241                 mac_prefix_len = create_arg->vc_mac_prefix_len;
 242                 if (mac_prefix_len > MAXMACADDRLEN) {
 243                         err = EINVAL;
 244                         diag = VNIC_IOC_DIAG_MACPREFIXLEN_INVALID;
 245                         goto bail;
 246                 }
 247                 mac_len = create_arg->vc_mac_len;
 248                 if (mac_len > MAXMACADDRLEN) {
 249                         err = EINVAL;
 250                         diag = VNIC_IOC_DIAG_MACADDRLEN_INVALID;
 251                         goto bail;
 252                 }
 253                 bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
 254                 break;
 255         case VNIC_MAC_ADDR_TYPE_PRIMARY:
 256                 /*
 257                  * We will get the primary address when we add this
 258                  * client
 259                  */
 260                 break;
 261         default:
 262                 err = ENOTSUP;
 263                 goto bail;
 264         }
 265 
 266 create:
 267         err = vnic_dev_create(create_arg->vc_vnic_id, create_arg->vc_link_id,
 268             &mac_addr_type, &mac_len, mac_addr, &mac_slot, mac_prefix_len,
 269             create_arg->vc_vid, create_arg->vc_vrid, create_arg->vc_af,
 270             &create_arg->vc_resource_props, create_arg->vc_flags, &diag,
 271             cred);
 272 
 273 
 274         if (err != 0)
 275                 goto bail;
 276 
 277         create_arg->vc_mac_addr_type = mac_addr_type;
 278 
 279         if (is_anchor)
 280                 goto bail;
 281 
 282         switch (mac_addr_type) {
 283         case VNIC_MAC_ADDR_TYPE_FACTORY:
 284                 create_arg->vc_mac_slot = mac_slot;
 285                 break;
 286         case VNIC_MAC_ADDR_TYPE_RANDOM:
 287                 bcopy(mac_addr, create_arg->vc_mac_addr, MAXMACADDRLEN);
 288                 create_arg->vc_mac_len = mac_len;
 289                 break;
 290         }
 291 
 292 bail:
 293         create_arg->vc_diag = diag;
 294         create_arg->vc_status = err;
 295         return (err);
 296 }
 297 
 298 /* ARGSUSED */
 299 static int
 300 vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 301 {
 302         vnic_ioc_modify_t *modify_arg = karg;
 303 
 304         return (vnic_dev_modify(modify_arg->vm_vnic_id,
 305             modify_arg->vm_modify_mask, modify_arg->vm_mac_addr_type,
 306             modify_arg->vm_mac_len, modify_arg->vm_mac_addr,
 307             modify_arg->vm_mac_slot, &modify_arg->vm_resource_props));
 308 }
 309 
 310 /* ARGSUSED */
 311 static int
 312 vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 313 {
 314         vnic_ioc_delete_t *delete_arg = karg;
 315 
 316         return (vnic_dev_delete(delete_arg->vd_vnic_id, 0, cred));
 317 }
 318 
 319 /* ARGSUSED */
 320 static int
 321 vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
 322 {
 323         vnic_ioc_info_t *info_arg = karg;
 324 
 325         return (vnic_info(&info_arg->vi_info, cred));
 326 }