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