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