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