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 2008 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 <inet/common.h> 35 36 /* module description */ 37 #define VNIC_LINKINFO "VNIC MAC" 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 static dld_ioc_func_t vnic_ioc_create, vnic_ioc_modify, vnic_ioc_delete, 45 vnic_ioc_info; 46 47 static dld_ioc_info_t vnic_ioc_list[] = { 48 {VNIC_IOC_CREATE, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_create_t), 49 vnic_ioc_create}, 50 {VNIC_IOC_DELETE, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_delete_t), 51 vnic_ioc_delete}, 52 {VNIC_IOC_INFO, DLDCOPYINOUT, sizeof (vnic_ioc_info_t), 53 vnic_ioc_info}, 54 {VNIC_IOC_MODIFY, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_modify_t), 55 vnic_ioc_modify} 56 }; 57 58 static struct cb_ops vnic_cb_ops = { 59 nulldev, /* open */ 60 nulldev, /* close */ 61 nulldev, /* strategy */ 62 nulldev, /* print */ 63 nodev, /* dump */ 64 nodev, /* read */ 65 nodev, /* write */ 66 nodev, /* ioctl */ 67 nodev, /* devmap */ 68 nodev, /* mmap */ 69 nodev, /* segmap */ 70 nochpoll, /* poll */ 71 ddi_prop_op, /* cb_prop_op */ 72 0, /* streamtab */ 73 D_MP /* Driver compatibility flag */ 74 }; 75 76 static struct dev_ops vnic_dev_ops = { 77 DEVO_REV, /* devo_rev */ 78 0, /* refcnt */ 79 vnic_getinfo, /* get_dev_info */ 80 nulldev, /* identify */ 81 nulldev, /* probe */ 82 vnic_attach, /* attach */ 83 vnic_detach, /* detach */ 84 nodev, /* reset */ 85 &vnic_cb_ops, /* driver operations */ 86 NULL, /* bus operations */ 87 nodev, /* dev power */ 88 ddi_quiesce_not_supported, /* dev quiesce */ 89 }; 90 91 static struct modldrv vnic_modldrv = { 92 &mod_driverops, /* Type of module. This one is a driver */ 93 VNIC_LINKINFO, /* short description */ 94 &vnic_dev_ops /* driver specific ops */ 95 }; 96 97 static struct modlinkage modlinkage = { 98 MODREV_1, 99 &vnic_modldrv, 100 NULL 101 }; 102 103 int 104 _init(void) 105 { 106 int err; 107 108 mac_init_ops(&vnic_dev_ops, "vnic"); 109 if ((err = mod_install(&modlinkage)) != 0) 110 mac_fini_ops(&vnic_dev_ops); 111 return (err); 112 } 113 114 int 115 _fini(void) 116 { 117 int err; 118 119 if ((err = mod_remove(&modlinkage)) == 0) 120 mac_fini_ops(&vnic_dev_ops); 121 return (err); 122 } 123 124 int 125 _info(struct modinfo *modinfop) 126 { 127 return (mod_info(&modlinkage, modinfop)); 128 } 129 130 static void 131 vnic_init(void) 132 { 133 vnic_dev_init(); 134 vnic_bcast_init(); 135 vnic_classifier_init(); 136 } 137 138 static void 139 vnic_fini(void) 140 { 141 vnic_dev_fini(); 142 vnic_bcast_fini(); 143 vnic_classifier_fini(); 144 } 145 146 dev_info_t * 147 vnic_get_dip(void) 148 { 149 return (vnic_dip); 150 } 151 152 /*ARGSUSED*/ 153 static int 154 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 155 void **result) 156 { 157 switch (infocmd) { 158 case DDI_INFO_DEVT2DEVINFO: 159 *result = vnic_dip; 160 return (DDI_SUCCESS); 161 case DDI_INFO_DEVT2INSTANCE: 162 *result = 0; 163 return (DDI_SUCCESS); 164 } 165 return (DDI_FAILURE); 166 } 167 168 static int 169 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 170 { 171 switch (cmd) { 172 case DDI_ATTACH: 173 if (ddi_get_instance(dip) != 0) { 174 /* we only allow instance 0 to attach */ 175 return (DDI_FAILURE); 176 } 177 178 if (dld_ioc_register(VNIC_IOC, vnic_ioc_list, 179 DLDIOCCNT(vnic_ioc_list)) != 0) 180 return (DDI_FAILURE); 181 182 vnic_dip = dip; 183 vnic_init(); 184 185 return (DDI_SUCCESS); 186 187 case DDI_RESUME: 188 return (DDI_SUCCESS); 189 190 default: 191 return (DDI_FAILURE); 192 } 193 } 194 195 /*ARGSUSED*/ 196 static int 197 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 198 { 199 switch (cmd) { 200 case DDI_DETACH: 201 /* 202 * Allow the VNIC instance to be detached only if there 203 * are not VNICs configured. 204 */ 205 if (vnic_dev_count() > 0) 206 return (DDI_FAILURE); 207 208 vnic_dip = NULL; 209 vnic_fini(); 210 dld_ioc_unregister(VNIC_IOC); 211 212 return (DDI_SUCCESS); 213 214 case DDI_SUSPEND: 215 return (DDI_SUCCESS); 216 217 default: 218 return (DDI_FAILURE); 219 } 220 } 221 222 /* 223 * Process a VNIC_IOC_CREATE request. 224 */ 225 /* ARGSUSED */ 226 static int 227 vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred) 228 { 229 vnic_ioc_create_t *create_arg = karg; 230 int mac_len; 231 uchar_t mac_addr[MAXMACADDRLEN]; 232 datalink_id_t vnic_id, linkid; 233 vnic_mac_addr_type_t mac_addr_type; 234 235 /* 236 * VNIC link id 237 */ 238 vnic_id = create_arg->vc_vnic_id; 239 240 /* 241 * Linkid of the link the VNIC is defined on top of. 242 */ 243 linkid = create_arg->vc_link_id; 244 245 /* MAC address */ 246 mac_addr_type = create_arg->vc_mac_addr_type; 247 mac_len = create_arg->vc_mac_len; 248 249 switch (mac_addr_type) { 250 case VNIC_MAC_ADDR_TYPE_FIXED: 251 bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN); 252 break; 253 default: 254 return (ENOTSUP); 255 } 256 257 return (vnic_dev_create(vnic_id, linkid, mac_len, mac_addr)); 258 } 259 260 /* ARGSUSED */ 261 static int 262 vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred) 263 { 264 vnic_ioc_modify_t *modify_arg = karg; 265 datalink_id_t vnic_id; 266 uint_t modify_mask; 267 vnic_mac_addr_type_t mac_addr_type; 268 uint_t mac_len; 269 uchar_t mac_addr[MAXMACADDRLEN]; 270 271 vnic_id = modify_arg->vm_vnic_id; 272 modify_mask = modify_arg->vm_modify_mask; 273 274 if (modify_mask & VNIC_IOC_MODIFY_ADDR) { 275 mac_addr_type = modify_arg->vm_mac_addr_type; 276 mac_len = modify_arg->vm_mac_len; 277 bcopy(modify_arg->vm_mac_addr, mac_addr, MAXMACADDRLEN); 278 } 279 280 return (vnic_dev_modify(vnic_id, modify_mask, mac_addr_type, 281 mac_len, mac_addr)); 282 } 283 284 /* ARGSUSED */ 285 static int 286 vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred) 287 { 288 vnic_ioc_delete_t *delete_arg = karg; 289 290 return (vnic_dev_delete(delete_arg->vd_vnic_id)); 291 } 292 293 typedef struct vnic_ioc_info_state { 294 uint32_t bytes_left; 295 uchar_t *where; 296 int mode; 297 } vnic_ioc_info_state_t; 298 299 static int 300 vnic_ioc_info_new_vnic(void *arg, datalink_id_t id, 301 vnic_mac_addr_type_t addr_type, uint_t mac_len, uint8_t *mac_addr, 302 datalink_id_t linkid) 303 { 304 vnic_ioc_info_state_t *state = arg; 305 /*LINTED*/ 306 vnic_ioc_info_vnic_t *vn = (vnic_ioc_info_vnic_t *)state->where; 307 308 if (state->bytes_left < sizeof (*vn)) 309 return (ENOSPC); 310 311 vn->vn_vnic_id = id; 312 vn->vn_link_id = linkid; 313 vn->vn_mac_addr_type = addr_type; 314 vn->vn_mac_len = mac_len; 315 if (ddi_copyout(mac_addr, &(vn->vn_mac_addr), mac_len, 316 state->mode) != 0) 317 return (EFAULT); 318 319 state->where += sizeof (*vn); 320 state->bytes_left -= sizeof (*vn); 321 322 return (0); 323 } 324 325 /* ARGSUSED */ 326 static int 327 vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred) 328 { 329 vnic_ioc_info_t *info_argp = karg; 330 uint32_t nvnics; 331 datalink_id_t vnic_id, linkid; 332 vnic_ioc_info_state_t state; 333 334 /* 335 * ID of the vnic to return or vnic device. 336 * If zero, the call returns information 337 * regarding all vnics currently defined. 338 */ 339 vnic_id = info_argp->vi_vnic_id; 340 linkid = info_argp->vi_linkid; 341 342 state.bytes_left = info_argp->vi_size; 343 state.where = (uchar_t *)(arg + sizeof (vnic_ioc_info_t)); 344 state.mode = mode; 345 346 return (vnic_info(&nvnics, vnic_id, linkid, &state, 347 vnic_ioc_info_new_vnic)); 348 } 349