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 * Simulated network device (simnet) driver: simulates a pseudo GLDv3 network 28 * device. Can simulate an Ethernet or WiFi network device. In addition, another 29 * simnet instance can be attached as a peer to create a point-to-point link on 30 * the same system. 31 */ 32 33 #include <sys/policy.h> 34 #include <sys/conf.h> 35 #include <sys/modctl.h> 36 #include <sys/priv_names.h> 37 #include <sys/dlpi.h> 38 #include <net/simnet.h> 39 #include <sys/ethernet.h> 40 #include <sys/mac.h> 41 #include <sys/dls.h> 42 #include <sys/mac_ether.h> 43 #include <sys/mac_provider.h> 44 #include <sys/mac_client_priv.h> 45 #include <sys/vlan.h> 46 #include <sys/random.h> 47 #include <sys/sysmacros.h> 48 #include <sys/list.h> 49 #include <sys/strsubr.h> 50 #include <sys/strsun.h> 51 #include <sys/atomic.h> 52 #include <sys/mac_wifi.h> 53 #include <sys/mac_impl.h> 54 #include <inet/wifi_ioctl.h> 55 #include <sys/thread.h> 56 #include <sys/synch.h> 57 58 #include "simnet_impl.h" 59 60 #define SIMNETINFO "Simulated Network Driver" 61 62 static dev_info_t *simnet_dip; 63 64 static int simnet_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 65 static int simnet_attach(dev_info_t *, ddi_attach_cmd_t); 66 static int simnet_detach(dev_info_t *, ddi_detach_cmd_t); 67 static int simnet_ioc_create(void *, intptr_t, int, cred_t *, int *); 68 static int simnet_ioc_delete(void *, intptr_t, int, cred_t *, int *); 69 static int simnet_ioc_info(void *, intptr_t, int, cred_t *, int *); 70 static int simnet_ioc_modify(void *, intptr_t, int, cred_t *, int *); 71 static uint8_t *mcastaddr_lookup(simnet_dev_t *, const uint8_t *); 72 73 static dld_ioc_info_t simnet_ioc_list[] = { 74 {SIMNET_IOC_CREATE, DLDCOPYINOUT, sizeof (simnet_ioc_create_t), 75 simnet_ioc_create, {PRIV_SYS_DL_CONFIG}}, 76 {SIMNET_IOC_DELETE, DLDCOPYIN, sizeof (simnet_ioc_delete_t), 77 simnet_ioc_delete, {PRIV_SYS_DL_CONFIG}}, 78 {SIMNET_IOC_INFO, DLDCOPYINOUT, sizeof (simnet_ioc_info_t), 79 simnet_ioc_info, {NULL}}, 80 {SIMNET_IOC_MODIFY, DLDCOPYIN, sizeof (simnet_ioc_modify_t), 81 simnet_ioc_modify, {PRIV_SYS_DL_CONFIG}}, 82 }; 83 84 DDI_DEFINE_STREAM_OPS(simnet_dev_ops, nulldev, nulldev, simnet_attach, 85 simnet_detach, nodev, simnet_getinfo, D_MP, NULL, 86 ddi_quiesce_not_supported); 87 88 static struct modldrv simnet_modldrv = { 89 &mod_driverops, /* Type of module. This one is a driver */ 90 SIMNETINFO, /* short description */ 91 &simnet_dev_ops /* driver specific ops */ 92 }; 93 94 static struct modlinkage modlinkage = { 95 MODREV_1, &simnet_modldrv, NULL 96 }; 97 98 /* MAC callback function declarations */ 99 static int simnet_m_start(void *); 100 static void simnet_m_stop(void *); 101 static int simnet_m_promisc(void *, boolean_t); 102 static int simnet_m_multicst(void *, boolean_t, const uint8_t *); 103 static int simnet_m_unicst(void *, const uint8_t *); 104 static int simnet_m_stat(void *, uint_t, uint64_t *); 105 static void simnet_m_ioctl(void *, queue_t *, mblk_t *); 106 static mblk_t *simnet_m_tx(void *, mblk_t *); 107 static int simnet_m_setprop(void *, const char *, mac_prop_id_t, 108 uint_t, const void *); 109 static int simnet_m_getprop(void *, const char *, mac_prop_id_t, 110 uint_t, uint_t, void *, uint_t *); 111 112 static mac_callbacks_t simnet_m_callbacks = { 113 (MC_IOCTL | MC_SETPROP | MC_GETPROP), 114 simnet_m_stat, 115 simnet_m_start, 116 simnet_m_stop, 117 simnet_m_promisc, 118 simnet_m_multicst, 119 simnet_m_unicst, 120 simnet_m_tx, 121 simnet_m_ioctl, 122 NULL, 123 NULL, 124 NULL, 125 simnet_m_setprop, 126 simnet_m_getprop 127 }; 128 129 /* 130 * simnet_dev_lock protects the simnet device list. 131 * sd_instlock in each simnet_dev_t protects access to 132 * a single simnet_dev_t. 133 */ 134 static krwlock_t simnet_dev_lock; 135 static list_t simnet_dev_list; 136 static int simnet_count; /* Num of simnet instances */ 137 138 int 139 _init(void) 140 { 141 int status; 142 143 mac_init_ops(&simnet_dev_ops, "simnet"); 144 status = mod_install(&modlinkage); 145 if (status != DDI_SUCCESS) 146 mac_fini_ops(&simnet_dev_ops); 147 148 return (status); 149 } 150 151 int 152 _fini(void) 153 { 154 int status; 155 156 status = mod_remove(&modlinkage); 157 if (status == DDI_SUCCESS) 158 mac_fini_ops(&simnet_dev_ops); 159 160 return (status); 161 } 162 163 int 164 _info(struct modinfo *modinfop) 165 { 166 return (mod_info(&modlinkage, modinfop)); 167 } 168 169 static void 170 simnet_init(void) 171 { 172 rw_init(&simnet_dev_lock, NULL, RW_DEFAULT, NULL); 173 list_create(&simnet_dev_list, sizeof (simnet_dev_t), 174 offsetof(simnet_dev_t, sd_listnode)); 175 simnet_count = 0; 176 } 177 178 static void 179 simnet_fini(void) 180 { 181 ASSERT(simnet_count == 0); 182 rw_destroy(&simnet_dev_lock); 183 list_destroy(&simnet_dev_list); 184 } 185 186 /*ARGSUSED*/ 187 static int 188 simnet_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 189 void **result) 190 { 191 switch (infocmd) { 192 case DDI_INFO_DEVT2DEVINFO: 193 *result = simnet_dip; 194 return (DDI_SUCCESS); 195 case DDI_INFO_DEVT2INSTANCE: 196 *result = NULL; 197 return (DDI_SUCCESS); 198 } 199 return (DDI_FAILURE); 200 } 201 202 static int 203 simnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 204 { 205 switch (cmd) { 206 case DDI_ATTACH: 207 if (ddi_get_instance(dip) != 0) { 208 /* we only allow instance 0 to attach */ 209 return (DDI_FAILURE); 210 } 211 if (dld_ioc_register(SIMNET_IOC, simnet_ioc_list, 212 DLDIOCCNT(simnet_ioc_list)) != 0) 213 return (DDI_FAILURE); 214 215 simnet_dip = dip; 216 simnet_init(); 217 return (DDI_SUCCESS); 218 219 case DDI_RESUME: 220 return (DDI_SUCCESS); 221 222 default: 223 return (DDI_FAILURE); 224 } 225 } 226 227 /*ARGSUSED*/ 228 static int 229 simnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 230 { 231 switch (cmd) { 232 case DDI_DETACH: 233 /* 234 * Allow the simnet instance to be detached only if there 235 * are no simnets configured. 236 */ 237 if (simnet_count > 0) 238 return (DDI_FAILURE); 239 240 simnet_dip = NULL; 241 simnet_fini(); 242 dld_ioc_unregister(SIMNET_IOC); 243 return (DDI_SUCCESS); 244 245 case DDI_SUSPEND: 246 return (DDI_SUCCESS); 247 248 default: 249 return (DDI_FAILURE); 250 } 251 } 252 253 /* Caller must hold simnet_dev_lock */ 254 static simnet_dev_t * 255 simnet_dev_lookup(datalink_id_t link_id) 256 { 257 simnet_dev_t *sdev; 258 259 for (sdev = list_head(&simnet_dev_list); sdev != NULL; 260 sdev = list_next(&simnet_dev_list, sdev)) { 261 if (!(sdev->sd_flags & SDF_SHUTDOWN) && 262 (sdev->sd_link_id == link_id)) { 263 atomic_inc_32(&sdev->sd_refcount); 264 return (sdev); 265 } 266 } 267 268 return (NULL); 269 } 270 271 static void 272 simnet_wifidev_free(simnet_dev_t *sdev) 273 { 274 simnet_wifidev_t *wdev = sdev->sd_wifidev; 275 int i; 276 277 for (i = 0; i < wdev->swd_esslist_num; i++) { 278 kmem_free(wdev->swd_esslist[i], 279 sizeof (wl_ess_conf_t)); 280 } 281 kmem_free(wdev, sizeof (simnet_wifidev_t)); 282 } 283 284 static void 285 simnet_dev_unref(simnet_dev_t *sdev) 286 { 287 288 ASSERT(sdev->sd_refcount > 0); 289 if (atomic_dec_32_nv(&sdev->sd_refcount) != 0) 290 return; 291 292 if (sdev->sd_mh != NULL) 293 (void) mac_unregister(sdev->sd_mh); 294 295 if (sdev->sd_wifidev != NULL) { 296 ASSERT(sdev->sd_type == DL_WIFI); 297 simnet_wifidev_free(sdev); 298 } 299 300 mutex_destroy(&sdev->sd_instlock); 301 cv_destroy(&sdev->sd_threadwait); 302 kmem_free(sdev->sd_mcastaddrs, ETHERADDRL * sdev->sd_mcastaddr_count); 303 kmem_free(sdev, sizeof (*sdev)); 304 simnet_count--; 305 } 306 307 static int 308 simnet_init_wifi(simnet_dev_t *sdev, mac_register_t *mac) 309 { 310 wifi_data_t wd = { 0 }; 311 int err; 312 313 sdev->sd_wifidev = kmem_zalloc(sizeof (simnet_wifidev_t), KM_NOSLEEP); 314 if (sdev->sd_wifidev == NULL) 315 return (ENOMEM); 316 317 sdev->sd_wifidev->swd_sdev = sdev; 318 sdev->sd_wifidev->swd_linkstatus = WL_NOTCONNECTED; 319 wd.wd_secalloc = WIFI_SEC_NONE; 320 wd.wd_opmode = IEEE80211_M_STA; 321 mac->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 322 mac->m_max_sdu = IEEE80211_MTU; 323 mac->m_pdata = &wd; 324 mac->m_pdata_size = sizeof (wd); 325 err = mac_register(mac, &sdev->sd_mh); 326 return (err); 327 } 328 329 static int 330 simnet_init_ether(simnet_dev_t *sdev, mac_register_t *mac) 331 { 332 int err; 333 334 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 335 mac->m_max_sdu = SIMNET_MAX_MTU; 336 mac->m_margin = VLAN_TAGSZ; 337 err = mac_register(mac, &sdev->sd_mh); 338 return (err); 339 } 340 341 static int 342 simnet_init_mac(simnet_dev_t *sdev) 343 { 344 mac_register_t *mac; 345 int err; 346 347 if ((mac = mac_alloc(MAC_VERSION)) == NULL) 348 return (ENOMEM); 349 350 mac->m_driver = sdev; 351 mac->m_dip = simnet_dip; 352 mac->m_instance = (uint_t)-1; 353 mac->m_src_addr = sdev->sd_mac_addr; 354 mac->m_callbacks = &simnet_m_callbacks; 355 mac->m_min_sdu = 0; 356 357 if (sdev->sd_type == DL_ETHER) 358 err = simnet_init_ether(sdev, mac); 359 else if (sdev->sd_type == DL_WIFI) 360 err = simnet_init_wifi(sdev, mac); 361 else 362 err = EINVAL; 363 364 mac_free(mac); 365 return (err); 366 } 367 368 /* ARGSUSED */ 369 static int 370 simnet_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 371 { 372 simnet_ioc_create_t *create_arg = karg; 373 simnet_dev_t *sdev; 374 simnet_dev_t *sdev_tmp; 375 int err = 0; 376 377 sdev = kmem_zalloc(sizeof (*sdev), KM_NOSLEEP); 378 if (sdev == NULL) 379 return (ENOMEM); 380 381 rw_enter(&simnet_dev_lock, RW_WRITER); 382 if ((sdev_tmp = simnet_dev_lookup(create_arg->sic_link_id)) != NULL) { 383 simnet_dev_unref(sdev_tmp); 384 rw_exit(&simnet_dev_lock); 385 kmem_free(sdev, sizeof (*sdev)); 386 return (EEXIST); 387 } 388 389 sdev->sd_type = create_arg->sic_type; 390 sdev->sd_link_id = create_arg->sic_link_id; 391 sdev->sd_refcount++; 392 mutex_init(&sdev->sd_instlock, NULL, MUTEX_DRIVER, NULL); 393 cv_init(&sdev->sd_threadwait, NULL, CV_DRIVER, NULL); 394 simnet_count++; 395 396 /* Simnets created from configuration on boot pass saved MAC address */ 397 if (create_arg->sic_mac_len == 0) { 398 /* Generate random MAC address */ 399 (void) random_get_pseudo_bytes(sdev->sd_mac_addr, ETHERADDRL); 400 /* Ensure MAC address is not multicast and is local */ 401 sdev->sd_mac_addr[0] = (sdev->sd_mac_addr[0] & ~1) | 2; 402 sdev->sd_mac_len = ETHERADDRL; 403 } else { 404 (void) memcpy(sdev->sd_mac_addr, create_arg->sic_mac_addr, 405 create_arg->sic_mac_len); 406 sdev->sd_mac_len = create_arg->sic_mac_len; 407 } 408 409 if ((err = simnet_init_mac(sdev)) != 0) { 410 simnet_dev_unref(sdev); 411 goto exit; 412 } 413 414 if ((err = dls_devnet_create(sdev->sd_mh, sdev->sd_link_id)) != 0) { 415 simnet_dev_unref(sdev); 416 goto exit; 417 } 418 419 mac_link_update(sdev->sd_mh, LINK_STATE_UP); 420 mac_tx_update(sdev->sd_mh); 421 list_insert_tail(&simnet_dev_list, sdev); 422 423 /* Always return MAC address back to caller */ 424 (void) memcpy(create_arg->sic_mac_addr, sdev->sd_mac_addr, 425 sdev->sd_mac_len); 426 create_arg->sic_mac_len = sdev->sd_mac_len; 427 exit: 428 rw_exit(&simnet_dev_lock); 429 return (err); 430 } 431 432 /* Caller must hold writer simnet_dev_lock */ 433 static datalink_id_t 434 simnet_remove_peer(simnet_dev_t *sdev) 435 { 436 simnet_dev_t *sdev_peer; 437 datalink_id_t peer_link_id = DATALINK_INVALID_LINKID; 438 439 if ((sdev_peer = sdev->sd_peer_dev) != NULL) { 440 ASSERT(sdev == sdev_peer->sd_peer_dev); 441 sdev_peer->sd_peer_dev = NULL; 442 sdev->sd_peer_dev = NULL; 443 peer_link_id = sdev_peer->sd_link_id; 444 /* Release previous references held on both simnets */ 445 simnet_dev_unref(sdev_peer); 446 simnet_dev_unref(sdev); 447 } 448 449 return (peer_link_id); 450 } 451 452 /* ARGSUSED */ 453 static int 454 simnet_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 455 { 456 simnet_ioc_modify_t *modify_arg = karg; 457 simnet_dev_t *sdev; 458 simnet_dev_t *sdev_peer = NULL; 459 460 rw_enter(&simnet_dev_lock, RW_WRITER); 461 if ((sdev = simnet_dev_lookup(modify_arg->sim_link_id)) == NULL) { 462 rw_exit(&simnet_dev_lock); 463 return (ENOENT); 464 } 465 466 if (sdev->sd_link_id == modify_arg->sim_peer_link_id) { 467 /* Cannot peer with self */ 468 rw_exit(&simnet_dev_lock); 469 simnet_dev_unref(sdev); 470 return (EINVAL); 471 } 472 473 if (sdev->sd_peer_dev != NULL && sdev->sd_peer_dev->sd_link_id == 474 modify_arg->sim_peer_link_id) { 475 /* Nothing to modify */ 476 rw_exit(&simnet_dev_lock); 477 simnet_dev_unref(sdev); 478 return (0); 479 } 480 481 if (modify_arg->sim_peer_link_id != DATALINK_INVALID_LINKID && 482 (sdev_peer = simnet_dev_lookup(modify_arg->sim_peer_link_id)) == 483 NULL) { 484 /* Peer simnet device not available */ 485 rw_exit(&simnet_dev_lock); 486 simnet_dev_unref(sdev); 487 return (ENOENT); 488 } 489 490 /* First remove any previous peer */ 491 (void) simnet_remove_peer(sdev); 492 493 if (sdev_peer != NULL) { 494 /* Remove any previous peer of sdev_peer */ 495 (void) simnet_remove_peer(sdev_peer); 496 /* Update both devices with the new peer */ 497 sdev_peer->sd_peer_dev = sdev; 498 sdev->sd_peer_dev = sdev_peer; 499 /* Hold references on both devices */ 500 } else { 501 /* Release sdev lookup reference */ 502 simnet_dev_unref(sdev); 503 } 504 505 rw_exit(&simnet_dev_lock); 506 return (0); 507 } 508 509 /* ARGSUSED */ 510 static int 511 simnet_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 512 { 513 int err; 514 simnet_dev_t *sdev; 515 simnet_dev_t *sdev_peer; 516 simnet_ioc_delete_t *delete_arg = karg; 517 datalink_id_t tmpid; 518 datalink_id_t peerid; 519 520 rw_enter(&simnet_dev_lock, RW_WRITER); 521 if ((sdev = simnet_dev_lookup(delete_arg->sid_link_id)) == NULL) { 522 rw_exit(&simnet_dev_lock); 523 return (ENOENT); 524 } 525 526 if ((err = dls_devnet_destroy(sdev->sd_mh, &tmpid, B_TRUE)) != 0) { 527 rw_exit(&simnet_dev_lock); 528 simnet_dev_unref(sdev); 529 return (err); 530 } 531 532 ASSERT(sdev->sd_link_id == tmpid); 533 /* Remove any attached peer link */ 534 peerid = simnet_remove_peer(sdev); 535 536 /* Prevent new threads from using the instance */ 537 mutex_enter(&sdev->sd_instlock); 538 sdev->sd_flags |= SDF_SHUTDOWN; 539 /* Wait until all active threads using the instance exit */ 540 while (sdev->sd_threadcount > 0) { 541 if (cv_wait_sig(&sdev->sd_threadwait, 542 &sdev->sd_instlock) == 0) { 543 /* Signaled */ 544 mutex_exit(&sdev->sd_instlock); 545 err = EINTR; 546 goto fail; 547 } 548 } 549 mutex_exit(&sdev->sd_instlock); 550 551 /* Try disabling the MAC */ 552 if ((err = mac_disable(sdev->sd_mh)) != 0) 553 goto fail; 554 555 list_remove(&simnet_dev_list, sdev); 556 rw_exit(&simnet_dev_lock); 557 simnet_dev_unref(sdev); /* Release lookup ref */ 558 /* Releasing the last ref performs sdev/mem free */ 559 simnet_dev_unref(sdev); 560 return (err); 561 fail: 562 /* Re-create simnet instance and add any previous peer */ 563 (void) dls_devnet_create(sdev->sd_mh, sdev->sd_link_id); 564 sdev->sd_flags &= ~SDF_SHUTDOWN; 565 566 ASSERT(sdev->sd_peer_dev == NULL); 567 if (peerid != DATALINK_INVALID_LINKID && 568 ((sdev_peer = simnet_dev_lookup(peerid)) != NULL)) { 569 /* Attach peer device back */ 570 ASSERT(sdev_peer->sd_peer_dev == NULL); 571 sdev_peer->sd_peer_dev = sdev; 572 sdev->sd_peer_dev = sdev_peer; 573 /* Hold reference on both devices */ 574 } else { 575 /* 576 * No previous peer or previous peer no longer 577 * available so release lookup reference. 578 */ 579 simnet_dev_unref(sdev); 580 } 581 582 rw_exit(&simnet_dev_lock); 583 return (err); 584 } 585 586 /* ARGSUSED */ 587 static int 588 simnet_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 589 { 590 simnet_ioc_info_t *info_arg = karg; 591 simnet_dev_t *sdev; 592 593 rw_enter(&simnet_dev_lock, RW_READER); 594 if ((sdev = simnet_dev_lookup(info_arg->sii_link_id)) == NULL) { 595 rw_exit(&simnet_dev_lock); 596 return (ENOENT); 597 } 598 599 (void) memcpy(info_arg->sii_mac_addr, sdev->sd_mac_addr, 600 sdev->sd_mac_len); 601 info_arg->sii_mac_len = sdev->sd_mac_len; 602 info_arg->sii_type = sdev->sd_type; 603 if (sdev->sd_peer_dev != NULL) 604 info_arg->sii_peer_link_id = sdev->sd_peer_dev->sd_link_id; 605 rw_exit(&simnet_dev_lock); 606 simnet_dev_unref(sdev); 607 return (0); 608 } 609 610 static void 611 simnet_rx(simnet_dev_t *sdev, mblk_t *mp) 612 { 613 mac_header_info_t hdr_info; 614 615 /* Check for valid packet header */ 616 if (mac_header_info(sdev->sd_mh, mp, &hdr_info) != 0) { 617 freemsg(mp); 618 sdev->sd_stats.recv_errors++; 619 return; 620 } 621 622 /* 623 * When we are NOT in promiscuous mode we only receive 624 * unicast packets addressed to us and multicast packets that 625 * MAC clients have requested. 626 */ 627 if (!sdev->sd_promisc && 628 hdr_info.mhi_dsttype != MAC_ADDRTYPE_BROADCAST) { 629 if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_UNICAST && 630 bcmp(hdr_info.mhi_daddr, sdev->sd_mac_addr, 631 ETHERADDRL) != 0) { 632 freemsg(mp); 633 return; 634 } else if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) { 635 mutex_enter(&sdev->sd_instlock); 636 if (mcastaddr_lookup(sdev, hdr_info.mhi_daddr) == 637 NULL) { 638 mutex_exit(&sdev->sd_instlock); 639 freemsg(mp); 640 return; 641 } 642 mutex_exit(&sdev->sd_instlock); 643 } 644 } 645 646 sdev->sd_stats.recv_count++; 647 sdev->sd_stats.rbytes += msgdsize(mp); 648 mac_rx(sdev->sd_mh, NULL, mp); 649 } 650 651 static mblk_t * 652 simnet_m_tx(void *arg, mblk_t *mp_chain) 653 { 654 simnet_dev_t *sdev = arg; 655 simnet_dev_t *sdev_rx; 656 mblk_t *mpnext = mp_chain; 657 mblk_t *mp; 658 659 rw_enter(&simnet_dev_lock, RW_READER); 660 if ((sdev_rx = sdev->sd_peer_dev) == NULL) { 661 /* Discard packets when no peer exists */ 662 rw_exit(&simnet_dev_lock); 663 freemsgchain(mp_chain); 664 return (NULL); 665 } 666 667 /* 668 * Discard packets when either device is shutting down or not ready. 669 * Though MAC layer ensures a reference is held on the MAC while we 670 * process the packet chain, there is no guarantee the peer MAC will 671 * remain enabled. So we increment per-instance threadcount to ensure 672 * either MAC instance is not disabled while we handle the chain of 673 * packets. It is okay if the peer device is disconnected while we are 674 * here since we lookup the peer device while holding simnet_dev_lock 675 * (reader lock) and increment the threadcount of the peer, the peer 676 * MAC cannot be disabled in simnet_ioc_delete. 677 */ 678 mutex_enter(&sdev_rx->sd_instlock); 679 if (sdev_rx->sd_flags & SDF_SHUTDOWN || 680 !(sdev_rx->sd_flags & SDF_STARTED)) { 681 mutex_exit(&sdev_rx->sd_instlock); 682 rw_exit(&simnet_dev_lock); 683 freemsgchain(mp_chain); 684 return (NULL); 685 } 686 sdev_rx->sd_threadcount++; 687 mutex_exit(&sdev_rx->sd_instlock); 688 rw_exit(&simnet_dev_lock); 689 690 mutex_enter(&sdev->sd_instlock); 691 if (sdev->sd_flags & SDF_SHUTDOWN || 692 !(sdev->sd_flags & SDF_STARTED)) { 693 mutex_exit(&sdev->sd_instlock); 694 mutex_enter(&sdev_rx->sd_instlock); 695 if (--sdev_rx->sd_threadcount == 0) 696 cv_broadcast(&sdev_rx->sd_threadwait); 697 mutex_exit(&sdev_rx->sd_instlock); 698 freemsgchain(mp_chain); 699 return (NULL); 700 } 701 sdev->sd_threadcount++; 702 mutex_exit(&sdev->sd_instlock); 703 704 while ((mp = mpnext) != NULL) { 705 int len; 706 int size; 707 mblk_t *mp_new; 708 mblk_t *mp_tmp; 709 710 mpnext = mp->b_next; 711 mp->b_next = NULL; 712 len = msgdsize(mp); 713 714 /* Pad packet to minimum Ethernet frame size */ 715 if (len < ETHERMIN) { 716 size = ETHERMIN - len; 717 mp_new = allocb(size, BPRI_HI); 718 if (mp_new == NULL) { 719 sdev->sd_stats.xmit_errors++; 720 freemsg(mp); 721 continue; 722 } 723 bzero(mp_new->b_wptr, size); 724 mp_new->b_wptr += size; 725 726 mp_tmp = mp; 727 while (mp_tmp->b_cont != NULL) 728 mp_tmp = mp_tmp->b_cont; 729 mp_tmp->b_cont = mp_new; 730 len += size; 731 } 732 733 /* Pullup packet into a single mblk */ 734 if (!pullupmsg(mp, -1)) { 735 sdev->sd_stats.xmit_errors++; 736 freemsg(mp); 737 continue; 738 } 739 740 /* Fix mblk checksum as the pkt dest is local */ 741 if ((mp = mac_fix_cksum(mp)) == NULL) { 742 sdev->sd_stats.xmit_errors++; 743 continue; 744 } 745 746 sdev->sd_stats.xmit_count++; 747 sdev->sd_stats.obytes += len; 748 simnet_rx(sdev_rx, mp); 749 } 750 751 mutex_enter(&sdev->sd_instlock); 752 if (--sdev->sd_threadcount == 0) 753 cv_broadcast(&sdev->sd_threadwait); 754 mutex_exit(&sdev->sd_instlock); 755 mutex_enter(&sdev_rx->sd_instlock); 756 if (--sdev_rx->sd_threadcount == 0) 757 cv_broadcast(&sdev_rx->sd_threadwait); 758 mutex_exit(&sdev_rx->sd_instlock); 759 return (NULL); 760 } 761 762 static int 763 simnet_wifi_ioctl(simnet_dev_t *sdev, mblk_t *mp) 764 { 765 int rc = WL_SUCCESS; 766 simnet_wifidev_t *wdev = sdev->sd_wifidev; 767 768 /* LINTED E_BAD_PTR_CAST_ALIGN */ 769 switch (((wldp_t *)mp->b_rptr)->wldp_id) { 770 case WL_DISASSOCIATE: 771 wdev->swd_linkstatus = WL_NOTCONNECTED; 772 break; 773 default: 774 break; 775 } 776 return (rc); 777 } 778 779 static void 780 simnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 781 { 782 simnet_dev_t *sdev = arg; 783 struct iocblk *iocp; 784 mblk_t *mp1; 785 uint32_t cmd; 786 int rc; 787 788 if (sdev->sd_type != DL_WIFI) { 789 miocnak(q, mp, 0, ENOTSUP); 790 return; 791 } 792 793 /* LINTED E_BAD_PTR_CAST_ALIGN */ 794 iocp = (struct iocblk *)mp->b_rptr; 795 if (iocp->ioc_count == 0) { 796 miocnak(q, mp, 0, EINVAL); 797 return; 798 } 799 800 /* We only claim support for WiFi operation commands */ 801 cmd = iocp->ioc_cmd; 802 switch (cmd) { 803 default: 804 miocnak(q, mp, 0, EINVAL); 805 return; 806 case WLAN_GET_PARAM: 807 case WLAN_SET_PARAM: 808 case WLAN_COMMAND: 809 break; 810 } 811 812 mp1 = mp->b_cont; 813 freemsg(mp1->b_cont); 814 mp1->b_cont = NULL; 815 /* overwrite everything */ 816 mp1->b_wptr = mp1->b_rptr; 817 rc = simnet_wifi_ioctl(sdev, mp1); 818 miocack(q, mp, msgdsize(mp1), rc); 819 } 820 821 static int 822 simnet_m_stat(void *arg, uint_t stat, uint64_t *val) 823 { 824 int rval = 0; 825 simnet_dev_t *sdev = arg; 826 827 ASSERT(sdev->sd_mh != NULL); 828 829 switch (stat) { 830 case MAC_STAT_IFSPEED: 831 *val = 100 * 1000000ull; /* 100 Mbps */ 832 break; 833 case MAC_STAT_LINK_STATE: 834 *val = LINK_DUPLEX_FULL; 835 break; 836 case MAC_STAT_LINK_UP: 837 if (sdev->sd_flags & SDF_STARTED) 838 *val = LINK_STATE_UP; 839 else 840 *val = LINK_STATE_DOWN; 841 break; 842 case MAC_STAT_PROMISC: 843 case MAC_STAT_MULTIRCV: 844 case MAC_STAT_MULTIXMT: 845 case MAC_STAT_BRDCSTRCV: 846 case MAC_STAT_BRDCSTXMT: 847 rval = ENOTSUP; 848 break; 849 case MAC_STAT_OPACKETS: 850 *val = sdev->sd_stats.xmit_count; 851 break; 852 case MAC_STAT_OBYTES: 853 *val = sdev->sd_stats.obytes; 854 break; 855 case MAC_STAT_IERRORS: 856 *val = sdev->sd_stats.recv_errors; 857 break; 858 case MAC_STAT_OERRORS: 859 *val = sdev->sd_stats.xmit_errors; 860 break; 861 case MAC_STAT_RBYTES: 862 *val = sdev->sd_stats.rbytes; 863 break; 864 case MAC_STAT_IPACKETS: 865 *val = sdev->sd_stats.recv_count; 866 break; 867 case WIFI_STAT_FCS_ERRORS: 868 case WIFI_STAT_WEP_ERRORS: 869 case WIFI_STAT_TX_FRAGS: 870 case WIFI_STAT_MCAST_TX: 871 case WIFI_STAT_RTS_SUCCESS: 872 case WIFI_STAT_RTS_FAILURE: 873 case WIFI_STAT_ACK_FAILURE: 874 case WIFI_STAT_RX_FRAGS: 875 case WIFI_STAT_MCAST_RX: 876 case WIFI_STAT_RX_DUPS: 877 rval = ENOTSUP; 878 break; 879 default: 880 rval = ENOTSUP; 881 break; 882 } 883 884 return (rval); 885 } 886 887 static int 888 simnet_m_start(void *arg) 889 { 890 simnet_dev_t *sdev = arg; 891 892 sdev->sd_flags |= SDF_STARTED; 893 return (0); 894 } 895 896 static void 897 simnet_m_stop(void *arg) 898 { 899 simnet_dev_t *sdev = arg; 900 901 sdev->sd_flags &= ~SDF_STARTED; 902 } 903 904 static int 905 simnet_m_promisc(void *arg, boolean_t on) 906 { 907 simnet_dev_t *sdev = arg; 908 909 sdev->sd_promisc = on; 910 return (0); 911 } 912 913 /* 914 * Returns matching multicast address enabled on the simnet instance. 915 * Assumes simnet instance mutex lock is held. 916 */ 917 static uint8_t * 918 mcastaddr_lookup(simnet_dev_t *sdev, const uint8_t *addrp) 919 { 920 int idx; 921 uint8_t *maddrptr; 922 923 maddrptr = sdev->sd_mcastaddrs; 924 for (idx = 0; idx < sdev->sd_mcastaddr_count; idx++) { 925 if (bcmp(maddrptr, addrp, ETHERADDRL) == 0) 926 return (maddrptr); 927 maddrptr += ETHERADDRL; 928 } 929 930 return (NULL); 931 } 932 933 /* Add or remove Multicast addresses on simnet instance */ 934 static int 935 simnet_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 936 { 937 simnet_dev_t *sdev = arg; 938 uint8_t *maddrptr; 939 uint8_t *newbuf; 940 size_t prevsize; 941 size_t newsize; 942 ptrdiff_t len; 943 ptrdiff_t len2; 944 945 alloc_retry: 946 prevsize = sdev->sd_mcastaddr_count * ETHERADDRL; 947 newsize = prevsize + (add ? ETHERADDRL:-ETHERADDRL); 948 newbuf = kmem_alloc(newsize, KM_SLEEP); 949 950 mutex_enter(&sdev->sd_instlock); 951 if (prevsize != (sdev->sd_mcastaddr_count * ETHERADDRL)) { 952 mutex_exit(&sdev->sd_instlock); 953 kmem_free(newbuf, newsize); 954 goto alloc_retry; 955 } 956 957 maddrptr = mcastaddr_lookup(sdev, addrp); 958 if (!add && maddrptr != NULL) { 959 /* Removing a Multicast address */ 960 if (newbuf != NULL) { 961 /* LINTED: E_PTRDIFF_OVERFLOW */ 962 len = maddrptr - sdev->sd_mcastaddrs; 963 (void) memcpy(newbuf, sdev->sd_mcastaddrs, len); 964 len2 = prevsize - len - ETHERADDRL; 965 (void) memcpy(newbuf + len, 966 maddrptr + ETHERADDRL, len2); 967 } 968 sdev->sd_mcastaddr_count--; 969 } else if (add && maddrptr == NULL) { 970 /* Adding a new Multicast address */ 971 (void) memcpy(newbuf, sdev->sd_mcastaddrs, prevsize); 972 (void) memcpy(newbuf + prevsize, addrp, ETHERADDRL); 973 sdev->sd_mcastaddr_count++; 974 } else { 975 /* Error: removing a non-existing Multicast address */ 976 mutex_exit(&sdev->sd_instlock); 977 kmem_free(newbuf, newsize); 978 cmn_err(CE_WARN, "simnet: MAC call to remove a " 979 "Multicast address failed"); 980 return (EINVAL); 981 } 982 983 kmem_free(sdev->sd_mcastaddrs, prevsize); 984 sdev->sd_mcastaddrs = newbuf; 985 mutex_exit(&sdev->sd_instlock); 986 return (0); 987 } 988 989 static int 990 simnet_m_unicst(void *arg, const uint8_t *macaddr) 991 { 992 simnet_dev_t *sdev = arg; 993 994 (void) memcpy(sdev->sd_mac_addr, macaddr, ETHERADDRL); 995 return (0); 996 } 997 998 /* Parse WiFi scan list entry arguments and return the arg count */ 999 static int 1000 parse_esslist_args(const void *pr_val, uint_t pr_valsize, 1001 char args[][MAX_ESSLIST_ARGLEN]) 1002 { 1003 char *sep; 1004 ptrdiff_t len = pr_valsize; 1005 const char *piece = pr_val; 1006 const char *end = (const char *)pr_val + pr_valsize - 1; 1007 int arg = 0; 1008 1009 while (piece < end && (arg < MAX_ESSLIST_ARGS)) { 1010 sep = strchr(piece, ','); 1011 if (sep == NULL) 1012 sep = (char *)end; 1013 /* LINTED E_PTRDIFF_OVERFLOW */ 1014 len = sep - piece; 1015 /* If first arg is zero then return none to delete all */ 1016 if (arg == 0 && strnlen(piece, len) == 1 && piece[0] == '0') 1017 return (0); 1018 if (len > MAX_ESSLIST_ARGLEN) 1019 len = MAX_ESSLIST_ARGLEN - 1; 1020 (void) memcpy(&args[arg][0], piece, len); 1021 args[arg][len] = '\0'; 1022 piece = sep + 1; 1023 arg++; 1024 } 1025 1026 return (arg); 1027 } 1028 1029 /* Set WiFi scan list entry from private property _wl_esslist */ 1030 static int 1031 set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize, 1032 const void *pr_val) 1033 { 1034 char essargs[MAX_ESSLIST_ARGS][MAX_ESSLIST_ARGLEN]; 1035 wl_ess_conf_t *wls; 1036 long result; 1037 int i; 1038 1039 bzero(essargs, sizeof (essargs)); 1040 if (parse_esslist_args(pr_val, pr_valsize, essargs) == 0) { 1041 for (i = 0; i < wdev->swd_esslist_num; i++) { 1042 kmem_free(wdev->swd_esslist[i], sizeof (wl_ess_conf_t)); 1043 wdev->swd_esslist[i] = NULL; 1044 } 1045 wdev->swd_esslist_num = 0; 1046 return (0); 1047 } 1048 1049 for (i = 0; i < wdev->swd_esslist_num; i++) { 1050 wls = wdev->swd_esslist[i]; 1051 if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid, 1052 essargs[0]) == 0) 1053 return (EEXIST); 1054 } 1055 1056 if (wdev->swd_esslist_num >= MAX_SIMNET_ESSCONF) 1057 return (EINVAL); 1058 1059 wls = kmem_zalloc(sizeof (wl_ess_conf_t), KM_SLEEP); 1060 (void) strlcpy(wls->wl_ess_conf_essid.wl_essid_essid, 1061 essargs[0], sizeof (wls->wl_ess_conf_essid.wl_essid_essid)); 1062 wls->wl_ess_conf_essid.wl_essid_length = 1063 strlen(wls->wl_ess_conf_essid.wl_essid_essid); 1064 (void) random_get_pseudo_bytes((uint8_t *) 1065 &wls->wl_ess_conf_bssid, sizeof (wl_bssid_t)); 1066 (void) ddi_strtol(essargs[1], (char **)NULL, 0, &result); 1067 wls->wl_ess_conf_sl = (wl_rssi_t) 1068 ((result > MAX_RSSI || result < 0) ? 0:result); 1069 wdev->swd_esslist[wdev->swd_esslist_num] = wls; 1070 wdev->swd_esslist_num++; 1071 1072 return (0); 1073 } 1074 1075 static int 1076 simnet_set_priv_prop(simnet_dev_t *sdev, const char *pr_name, 1077 uint_t pr_valsize, const void *pr_val) 1078 { 1079 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1080 long result; 1081 1082 if (strcmp(pr_name, "_wl_esslist") == 0) { 1083 if (pr_val == NULL) 1084 return (EINVAL); 1085 return (set_wl_esslist_priv_prop(wdev, pr_valsize, pr_val)); 1086 } else if (strcmp(pr_name, "_wl_connected") == 0) { 1087 if (pr_val == NULL) 1088 return (EINVAL); 1089 (void) ddi_strtol(pr_val, (char **)NULL, 0, &result); 1090 wdev->swd_linkstatus = ((result == 1) ? 1091 WL_CONNECTED:WL_NOTCONNECTED); 1092 return (0); 1093 } 1094 1095 return (EINVAL); 1096 } 1097 1098 static int 1099 simnet_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 1100 uint_t wldp_length, const void *wldp_buf) 1101 { 1102 simnet_dev_t *sdev = arg; 1103 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1104 int err = 0; 1105 uint32_t mtu; 1106 1107 switch (wldp_pr_num) { 1108 case MAC_PROP_MTU: 1109 (void) memcpy(&mtu, wldp_buf, sizeof (mtu)); 1110 if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU) 1111 return (mac_maxsdu_update(sdev->sd_mh, mtu)); 1112 else 1113 return (EINVAL); 1114 default: 1115 break; 1116 } 1117 1118 if (sdev->sd_type == DL_ETHER) 1119 return (ENOTSUP); 1120 1121 /* mac_prop_id */ 1122 switch (wldp_pr_num) { 1123 case MAC_PROP_WL_ESSID: { 1124 int i; 1125 wl_ess_conf_t *wls; 1126 1127 (void) memcpy(&wdev->swd_essid, wldp_buf, 1128 sizeof (wl_essid_t)); 1129 wdev->swd_linkstatus = WL_CONNECTED; 1130 1131 /* Lookup the signal strength of the connected ESSID */ 1132 for (i = 0; i < wdev->swd_esslist_num; i++) { 1133 wls = wdev->swd_esslist[i]; 1134 if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid, 1135 wdev->swd_essid.wl_essid_essid) == 0) { 1136 wdev->swd_rssi = wls->wl_ess_conf_sl; 1137 break; 1138 } 1139 } 1140 break; 1141 } 1142 case MAC_PROP_WL_BSSID: { 1143 (void) memcpy(&wdev->swd_bssid, wldp_buf, 1144 sizeof (wl_bssid_t)); 1145 break; 1146 } 1147 case MAC_PROP_WL_PHY_CONFIG: 1148 case MAC_PROP_WL_KEY_TAB: 1149 case MAC_PROP_WL_AUTH_MODE: 1150 case MAC_PROP_WL_ENCRYPTION: 1151 case MAC_PROP_WL_BSSTYPE: 1152 case MAC_PROP_WL_DESIRED_RATES: 1153 break; 1154 case MAC_PROP_PRIVATE: 1155 err = simnet_set_priv_prop(sdev, pr_name, 1156 wldp_length, wldp_buf); 1157 break; 1158 default: 1159 break; 1160 } 1161 1162 return (err); 1163 } 1164 1165 static int 1166 simnet_get_priv_prop(simnet_dev_t *sdev, const char *pr_name, uint_t pr_flags, 1167 uint_t pr_valsize, void *pr_val) 1168 { 1169 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1170 boolean_t is_default = ((pr_flags & MAC_PROP_DEFAULT) != 0); 1171 int err = 0; 1172 int value; 1173 1174 if (strcmp(pr_name, "_wl_esslist") == 0) { 1175 /* Returns num of _wl_ess_conf_t that have been set */ 1176 value = (is_default ? 0:wdev->swd_esslist_num); 1177 } else if (strcmp(pr_name, "_wl_connected") == 0) { 1178 value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0); 1179 } else { 1180 err = ENOTSUP; 1181 } 1182 1183 if (err == 0) 1184 (void) snprintf(pr_val, pr_valsize, "%d", value); 1185 return (err); 1186 } 1187 1188 static int 1189 simnet_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 1190 uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) 1191 { 1192 simnet_dev_t *sdev = arg; 1193 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1194 int err = 0; 1195 int i; 1196 1197 if (sdev->sd_type == DL_ETHER) 1198 return (ENOTSUP); 1199 1200 /* mac_prop_id */ 1201 switch (wldp_pr_num) { 1202 case MAC_PROP_WL_ESSID: 1203 (void) memcpy(wldp_buf, &wdev->swd_essid, 1204 sizeof (wl_essid_t)); 1205 break; 1206 case MAC_PROP_WL_BSSID: 1207 (void) memcpy(wldp_buf, &wdev->swd_bssid, 1208 sizeof (wl_bssid_t)); 1209 break; 1210 case MAC_PROP_WL_PHY_CONFIG: 1211 case MAC_PROP_WL_AUTH_MODE: 1212 case MAC_PROP_WL_ENCRYPTION: 1213 break; 1214 case MAC_PROP_WL_BSSTYPE: 1215 *perm = MAC_PROP_PERM_READ; 1216 break; 1217 case MAC_PROP_WL_LINKSTATUS: 1218 (void) memcpy(wldp_buf, &wdev->swd_linkstatus, 1219 sizeof (wdev->swd_linkstatus)); 1220 break; 1221 case MAC_PROP_WL_ESS_LIST: { 1222 wl_ess_conf_t *w_ess_conf; 1223 1224 *perm = MAC_PROP_PERM_READ; 1225 ((wl_ess_list_t *)wldp_buf)->wl_ess_list_num = 1226 wdev->swd_esslist_num; 1227 /* LINTED E_BAD_PTR_CAST_ALIGN */ 1228 w_ess_conf = (wl_ess_conf_t *)((char *)wldp_buf + 1229 offsetof(wl_ess_list_t, wl_ess_list_ess)); 1230 for (i = 0; i < wdev->swd_esslist_num; i++) { 1231 (void) memcpy(w_ess_conf, wdev->swd_esslist[i], 1232 sizeof (wl_ess_conf_t)); 1233 w_ess_conf++; 1234 } 1235 break; 1236 } 1237 case MAC_PROP_WL_SUPPORTED_RATES: 1238 *perm = MAC_PROP_PERM_READ; 1239 break; 1240 case MAC_PROP_WL_RSSI: 1241 *perm = MAC_PROP_PERM_READ; 1242 *(wl_rssi_t *)wldp_buf = wdev->swd_rssi; 1243 break; 1244 case MAC_PROP_WL_RADIO: 1245 *(wl_radio_t *)wldp_buf = B_TRUE; 1246 break; 1247 case MAC_PROP_WL_POWER_MODE: 1248 break; 1249 case MAC_PROP_WL_DESIRED_RATES: 1250 break; 1251 case MAC_PROP_PRIVATE: 1252 err = simnet_get_priv_prop(sdev, pr_name, pr_flags, 1253 wldp_length, wldp_buf); 1254 break; 1255 default: 1256 err = ENOTSUP; 1257 break; 1258 } 1259 1260 return (err); 1261 } 1262