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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Virtual Network Interface Card (VNIC) 30 */ 31 32 #include <sys/ddi.h> 33 #include <sys/strsun.h> 34 #include <sys/dld.h> 35 #include <sys/dld_impl.h> 36 #include <sys/conf.h> 37 #include <sys/modctl.h> 38 #include <sys/stat.h> 39 #include <sys/vnic.h> 40 #include <sys/vnic_impl.h> 41 #include <inet/common.h> 42 43 /* module description */ 44 #define VNIC_LINKINFO "VNIC MAC" 45 #define VNIC_DRIVER_NAME "vnic" 46 47 /* device info ptr, only one for instance 0 */ 48 static dev_info_t *vnic_dip = NULL; 49 50 /* for control interface */ 51 static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 52 static int vnic_attach(dev_info_t *, ddi_attach_cmd_t); 53 static int vnic_detach(dev_info_t *, ddi_detach_cmd_t); 54 static int vnic_open(queue_t *, dev_t *, int, int, cred_t *); 55 static int vnic_close(queue_t *); 56 static void vnic_wput(queue_t *, mblk_t *); 57 static void vnic_ioctl(queue_t *, mblk_t *); 58 59 static int vnic_ioc_create(mblk_t *, int); 60 static int vnic_ioc_modify(mblk_t *, int); 61 static int vnic_ioc_delete(mblk_t *, int); 62 static int vnic_ioc_info(mblk_t *, int); 63 64 typedef struct ioc_cmd_s { 65 int ic_cmd; 66 int (*ic_func)(mblk_t *, int); 67 } ioc_cmd_t; 68 69 static ioc_cmd_t ioc_cmd[] = { 70 {VNIC_IOC_CREATE, vnic_ioc_create}, 71 {VNIC_IOC_DELETE, vnic_ioc_delete}, 72 {VNIC_IOC_INFO, vnic_ioc_info}, 73 {VNIC_IOC_MODIFY, vnic_ioc_modify} 74 }; 75 76 #define IOC_CMD_SZ (sizeof (ioc_cmd) / sizeof (ioc_cmd_t)) 77 78 /* 79 * mi_hiwat is set to 1 because of the flow control mechanism implemented 80 * in dld. refer to the comments in dld_str.c for details. 81 */ 82 static struct module_info vnic_module_info = { 83 0, 84 VNIC_DRIVER_NAME, 85 0, 86 INFPSZ, 87 1, 88 0 89 }; 90 91 static struct qinit vnic_r_qinit = { /* read queues */ 92 NULL, 93 NULL, 94 vnic_open, 95 vnic_close, 96 NULL, 97 &vnic_module_info 98 }; 99 100 static struct qinit vnic_w_qinit = { /* write queues */ 101 (pfi_t)dld_wput, 102 (pfi_t)dld_wsrv, 103 NULL, 104 NULL, 105 NULL, 106 &vnic_module_info 107 }; 108 109 /* 110 * Entry points for vnic control node 111 */ 112 static struct qinit vnic_w_ctl_qinit = { 113 (pfi_t)vnic_wput, 114 NULL, 115 NULL, 116 NULL, 117 NULL, 118 &vnic_module_info 119 }; 120 121 static struct streamtab vnic_streamtab = { 122 &vnic_r_qinit, 123 &vnic_w_qinit 124 }; 125 126 DDI_DEFINE_STREAM_OPS(vnic_dev_ops, nulldev, nulldev, vnic_attach, vnic_detach, 127 nodev, vnic_getinfo, D_MP, &vnic_streamtab); 128 129 static struct modldrv vnic_modldrv = { 130 &mod_driverops, /* Type of module. This one is a driver */ 131 VNIC_LINKINFO, /* short description */ 132 &vnic_dev_ops /* driver specific ops */ 133 }; 134 135 static struct modlinkage modlinkage = { 136 MODREV_1, 137 &vnic_modldrv, 138 NULL 139 }; 140 141 int 142 _init(void) 143 { 144 return (mod_install(&modlinkage)); 145 } 146 147 int 148 _fini(void) 149 { 150 return (mod_remove(&modlinkage)); 151 } 152 153 int 154 _info(struct modinfo *modinfop) 155 { 156 return (mod_info(&modlinkage, modinfop)); 157 } 158 159 static int 160 vnic_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 161 { 162 if (q->q_ptr != NULL) 163 return (EBUSY); 164 165 if (getminor(*devp) == VNIC_CTL_NODE_MINOR) { 166 dld_str_t *dsp; 167 168 dsp = dld_str_create(q, DLD_CONTROL, getmajor(*devp), 169 DL_STYLE1); 170 if (dsp == NULL) 171 return (ENOSR); 172 173 /* 174 * The ioctl handling callback to process control ioctl 175 * messages; see comments above dld_ioctl() for details. 176 */ 177 dsp->ds_ioctl = vnic_ioctl; 178 179 /* 180 * The VNIC control node uses its own set of entry points. 181 */ 182 WR(q)->q_qinfo = &vnic_w_ctl_qinit; 183 *devp = makedevice(getmajor(*devp), dsp->ds_minor); 184 qprocson(q); 185 return (0); 186 } 187 return (dld_open(q, devp, flag, sflag, credp)); 188 } 189 190 static int 191 vnic_close(queue_t *q) 192 { 193 dld_str_t *dsp = q->q_ptr; 194 195 if (dsp->ds_type == DLD_CONTROL) { 196 qprocsoff(q); 197 dld_finish_pending_task(dsp); 198 dsp->ds_ioctl = NULL; 199 dld_str_destroy(dsp); 200 return (0); 201 } 202 return (dld_close(q)); 203 } 204 205 static void 206 vnic_ioctl(queue_t *wq, mblk_t *mp) 207 { 208 /* LINTED alignment */ 209 struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 210 int i, err = EINVAL; 211 mblk_t *nmp; 212 213 if (mp->b_cont == NULL) { 214 err = EINVAL; 215 goto done; 216 } 217 218 /* 219 * Construct contiguous message 220 */ 221 if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) { 222 freemsg(mp->b_cont); 223 err = ENOMEM; 224 goto done; 225 } 226 227 freemsg(mp->b_cont); 228 mp->b_cont = nmp; 229 230 for (i = 0; i < IOC_CMD_SZ; i++) { 231 if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) { 232 err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag); 233 break; 234 } 235 } 236 237 if (err == 0) { 238 int len = 0; 239 240 if (mp->b_cont != NULL) 241 len = MBLKL(mp->b_cont); 242 miocack(wq, mp, len, 0); 243 return; 244 } 245 246 done: 247 miocnak(wq, mp, 0, err); 248 } 249 250 static void 251 vnic_wput(queue_t *q, mblk_t *mp) 252 { 253 if (DB_TYPE(mp) == M_IOCTL) 254 dld_ioctl(q, mp); 255 else 256 freemsg(mp); 257 } 258 259 static void 260 vnic_init(void) 261 { 262 vnic_dev_init(); 263 vnic_bcast_init(); 264 vnic_classifier_init(); 265 } 266 267 static void 268 vnic_fini(void) 269 { 270 vnic_dev_fini(); 271 vnic_bcast_fini(); 272 vnic_classifier_fini(); 273 } 274 275 dev_info_t * 276 vnic_get_dip(void) 277 { 278 return (vnic_dip); 279 } 280 281 /*ARGSUSED*/ 282 static int 283 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 284 void **result) 285 { 286 switch (infocmd) { 287 case DDI_INFO_DEVT2DEVINFO: 288 *result = vnic_dip; 289 return (DDI_SUCCESS); 290 case DDI_INFO_DEVT2INSTANCE: 291 *result = NULL; 292 return (DDI_SUCCESS); 293 } 294 return (DDI_FAILURE); 295 } 296 297 static int 298 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 299 { 300 switch (cmd) { 301 case DDI_ATTACH: 302 if (ddi_get_instance(dip) != 0) { 303 /* we only allow instance 0 to attach */ 304 return (DDI_FAILURE); 305 } 306 307 /* create minor node for control interface */ 308 if (ddi_create_minor_node(dip, VNIC_CTL_NODE_NAME, S_IFCHR, 309 VNIC_CTL_NODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) { 310 return (DDI_FAILURE); 311 } 312 313 vnic_dip = dip; 314 vnic_init(); 315 316 return (DDI_SUCCESS); 317 318 case DDI_RESUME: 319 return (DDI_SUCCESS); 320 321 default: 322 return (DDI_FAILURE); 323 } 324 } 325 326 /*ARGSUSED*/ 327 static int 328 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 329 { 330 switch (cmd) { 331 case DDI_DETACH: 332 /* 333 * Allow the VNIC instance to be detached only if there 334 * are not VNICs configured. 335 */ 336 if (vnic_dev_count() > 0) 337 return (DDI_FAILURE); 338 339 vnic_dip = NULL; 340 vnic_fini(); 341 ddi_remove_minor_node(dip, VNIC_CTL_NODE_NAME); 342 343 return (DDI_SUCCESS); 344 345 case DDI_SUSPEND: 346 return (DDI_SUCCESS); 347 348 default: 349 return (DDI_FAILURE); 350 } 351 } 352 353 /* 354 * Process a VNICIOC_CREATE request. 355 */ 356 static int 357 vnic_ioc_create(mblk_t *mp, int mode) 358 { 359 STRUCT_HANDLE(vnic_ioc_create, create_arg); 360 int rc = 0; 361 int mac_len; 362 uchar_t mac_addr[MAXMACADDRLEN]; 363 datalink_id_t vnic_id, linkid; 364 vnic_mac_addr_type_t mac_addr_type; 365 366 STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr); 367 if (MBLKL(mp->b_cont) < STRUCT_SIZE(create_arg)) 368 return (EINVAL); 369 370 /* 371 * VNIC link id 372 */ 373 vnic_id = STRUCT_FGET(create_arg, vc_vnic_id); 374 375 /* 376 * Linkid of the link the VNIC is defined on top of. 377 */ 378 linkid = STRUCT_FGET(create_arg, vc_link_id); 379 380 /* MAC address */ 381 mac_addr_type = STRUCT_FGET(create_arg, vc_mac_addr_type); 382 mac_len = STRUCT_FGET(create_arg, vc_mac_len); 383 384 switch (mac_addr_type) { 385 case VNIC_MAC_ADDR_TYPE_FIXED: 386 bcopy(STRUCT_FGET(create_arg, vc_mac_addr), mac_addr, 387 MAXMACADDRLEN); 388 break; 389 default: 390 return (ENOTSUP); 391 } 392 393 rc = vnic_dev_create(vnic_id, linkid, mac_len, mac_addr); 394 return (rc); 395 } 396 397 static int 398 vnic_ioc_modify(mblk_t *mp, int mode) 399 { 400 STRUCT_HANDLE(vnic_ioc_modify, modify_arg); 401 int err = 0; 402 datalink_id_t vnic_id; 403 uint_t modify_mask; 404 vnic_mac_addr_type_t mac_addr_type; 405 uint_t mac_len; 406 uchar_t mac_addr[MAXMACADDRLEN]; 407 408 STRUCT_SET_HANDLE(modify_arg, mode, (void *)mp->b_cont->b_rptr); 409 if (MBLKL(mp->b_cont) < STRUCT_SIZE(modify_arg)) 410 return (EINVAL); 411 412 vnic_id = STRUCT_FGET(modify_arg, vm_vnic_id); 413 modify_mask = STRUCT_FGET(modify_arg, vm_modify_mask); 414 415 if (modify_mask & VNIC_IOC_MODIFY_ADDR) { 416 mac_addr_type = STRUCT_FGET(modify_arg, vm_mac_addr_type); 417 mac_len = STRUCT_FGET(modify_arg, vm_mac_len); 418 bcopy(STRUCT_FGET(modify_arg, vm_mac_addr), mac_addr, 419 MAXMACADDRLEN); 420 } 421 422 err = vnic_dev_modify(vnic_id, modify_mask, mac_addr_type, 423 mac_len, mac_addr); 424 return (err); 425 } 426 427 static int 428 vnic_ioc_delete(mblk_t *mp, int mode) 429 { 430 STRUCT_HANDLE(vnic_ioc_delete, delete_arg); 431 datalink_id_t vnic_id; 432 int err = 0; 433 434 STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr); 435 if (STRUCT_SIZE(delete_arg) > MBLKL(mp)) 436 return (EINVAL); 437 438 vnic_id = STRUCT_FGET(delete_arg, vd_vnic_id); 439 err = vnic_dev_delete(vnic_id); 440 return (err); 441 } 442 443 typedef struct vnic_ioc_info_state { 444 uint32_t bytes_left; 445 uchar_t *where; 446 } vnic_ioc_info_state_t; 447 448 static int 449 vnic_ioc_info_new_vnic(void *arg, datalink_id_t id, 450 vnic_mac_addr_type_t addr_type, uint_t mac_len, uint8_t *mac_addr, 451 datalink_id_t linkid) 452 { 453 vnic_ioc_info_state_t *state = arg; 454 /*LINTED*/ 455 vnic_ioc_info_vnic_t *vn = (vnic_ioc_info_vnic_t *)state->where; 456 457 if (state->bytes_left < sizeof (*vn)) 458 return (ENOSPC); 459 460 vn->vn_vnic_id = id; 461 vn->vn_link_id = linkid; 462 vn->vn_mac_addr_type = addr_type; 463 vn->vn_mac_len = mac_len; 464 bcopy(mac_addr, &(vn->vn_mac_addr), mac_len); 465 466 state->where += sizeof (*vn); 467 state->bytes_left -= sizeof (*vn); 468 469 return (0); 470 } 471 472 /* ARGSUSED */ 473 static int 474 vnic_ioc_info(mblk_t *mp, int mode) 475 { 476 vnic_ioc_info_t *info_argp; 477 int rc, len; 478 uint32_t nvnics; 479 datalink_id_t vnic_id, linkid; 480 vnic_ioc_info_state_t state; 481 482 if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp)) 483 return (EINVAL); 484 485 /* LINTED alignment */ 486 info_argp = (vnic_ioc_info_t *)mp->b_cont->b_rptr; 487 488 /* 489 * ID of the vnic to return or vnic device. 490 * If zero, the call returns information 491 * regarding all vnics currently defined. 492 */ 493 vnic_id = info_argp->vi_vnic_id; 494 linkid = info_argp->vi_linkid; 495 496 state.bytes_left = len - sizeof (vnic_ioc_info_t); 497 state.where = (uchar_t *)(info_argp +1); 498 499 rc = vnic_info(&nvnics, vnic_id, linkid, &state, 500 vnic_ioc_info_new_vnic); 501 return (rc); 502 } 503