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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2019 Joyent, Inc. 26 */ 27 28 /* 29 * Simulated network device (simnet) driver: simulates a pseudo GLDv3 network 30 * device. Can simulate an Ethernet or WiFi network device. In addition, another 31 * simnet instance can be attached as a peer to create a point-to-point link on 32 * the same system. 33 */ 34 35 #include <sys/policy.h> 36 #include <sys/conf.h> 37 #include <sys/modctl.h> 38 #include <sys/priv_names.h> 39 #include <sys/dlpi.h> 40 #include <net/simnet.h> 41 #include <sys/ethernet.h> 42 #include <sys/mac.h> 43 #include <sys/dls.h> 44 #include <sys/mac_ether.h> 45 #include <sys/mac_provider.h> 46 #include <sys/mac_client_priv.h> 47 #include <sys/vlan.h> 48 #include <sys/random.h> 49 #include <sys/sysmacros.h> 50 #include <sys/list.h> 51 #include <sys/strsubr.h> 52 #include <sys/strsun.h> 53 #include <sys/atomic.h> 54 #include <sys/mac_wifi.h> 55 #include <sys/mac_impl.h> 56 #include <sys/pattr.h> 57 #include <inet/wifi_ioctl.h> 58 #include <sys/thread.h> 59 #include <sys/synch.h> 60 #include <sys/sunddi.h> 61 62 #include "simnet_impl.h" 63 64 #define SIMNETINFO "Simulated Network Driver" 65 66 static dev_info_t *simnet_dip; 67 static ddi_taskq_t *simnet_rxq; 68 69 static int simnet_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 70 static int simnet_attach(dev_info_t *, ddi_attach_cmd_t); 71 static int simnet_detach(dev_info_t *, ddi_detach_cmd_t); 72 static int simnet_ioc_create(void *, intptr_t, int, cred_t *, int *); 73 static int simnet_ioc_delete(void *, intptr_t, int, cred_t *, int *); 74 static int simnet_ioc_info(void *, intptr_t, int, cred_t *, int *); 75 static int simnet_ioc_modify(void *, intptr_t, int, cred_t *, int *); 76 static uint8_t *mcastaddr_lookup(simnet_dev_t *, const uint8_t *); 77 78 static dld_ioc_info_t simnet_ioc_list[] = { 79 {SIMNET_IOC_CREATE, DLDCOPYINOUT, sizeof (simnet_ioc_create_t), 80 simnet_ioc_create, secpolicy_dl_config}, 81 {SIMNET_IOC_DELETE, DLDCOPYIN, sizeof (simnet_ioc_delete_t), 82 simnet_ioc_delete, secpolicy_dl_config}, 83 {SIMNET_IOC_INFO, DLDCOPYINOUT, sizeof (simnet_ioc_info_t), 84 simnet_ioc_info, NULL}, 85 {SIMNET_IOC_MODIFY, DLDCOPYIN, sizeof (simnet_ioc_modify_t), 86 simnet_ioc_modify, secpolicy_dl_config} 87 }; 88 89 DDI_DEFINE_STREAM_OPS(simnet_dev_ops, nulldev, nulldev, simnet_attach, 90 simnet_detach, nodev, simnet_getinfo, D_MP, NULL, 91 ddi_quiesce_not_supported); 92 93 static struct modldrv simnet_modldrv = { 94 &mod_driverops, /* Type of module. This one is a driver */ 95 SIMNETINFO, /* short description */ 96 &simnet_dev_ops /* driver specific ops */ 97 }; 98 99 static struct modlinkage modlinkage = { 100 MODREV_1, &simnet_modldrv, NULL 101 }; 102 103 /* MAC callback function declarations */ 104 static int simnet_m_start(void *); 105 static void simnet_m_stop(void *); 106 static int simnet_m_promisc(void *, boolean_t); 107 static int simnet_m_multicst(void *, boolean_t, const uint8_t *); 108 static int simnet_m_unicst(void *, const uint8_t *); 109 static int simnet_m_stat(void *, uint_t, uint64_t *); 110 static void simnet_m_ioctl(void *, queue_t *, mblk_t *); 111 static mblk_t *simnet_m_tx(void *, mblk_t *); 112 static int simnet_m_setprop(void *, const char *, mac_prop_id_t, 113 const uint_t, const void *); 114 static int simnet_m_getprop(void *, const char *, mac_prop_id_t, 115 uint_t, void *); 116 static void simnet_m_propinfo(void *, const char *, mac_prop_id_t, 117 mac_prop_info_handle_t); 118 static boolean_t simnet_m_getcapab(void *, mac_capab_t, void *); 119 120 static mac_callbacks_t simnet_m_callbacks = { 121 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO), 122 simnet_m_stat, 123 simnet_m_start, 124 simnet_m_stop, 125 simnet_m_promisc, 126 simnet_m_multicst, 127 simnet_m_unicst, 128 simnet_m_tx, 129 NULL, 130 simnet_m_ioctl, 131 simnet_m_getcapab, 132 NULL, 133 NULL, 134 simnet_m_setprop, 135 simnet_m_getprop, 136 simnet_m_propinfo 137 }; 138 139 /* 140 * simnet_dev_lock protects the simnet device list. 141 * sd_instlock in each simnet_dev_t protects access to 142 * a single simnet_dev_t. 143 */ 144 static krwlock_t simnet_dev_lock; 145 static list_t simnet_dev_list; 146 static int simnet_count; /* Num of simnet instances */ 147 148 int 149 _init(void) 150 { 151 int status; 152 153 mac_init_ops(&simnet_dev_ops, "simnet"); 154 status = mod_install(&modlinkage); 155 if (status != DDI_SUCCESS) 156 mac_fini_ops(&simnet_dev_ops); 157 158 return (status); 159 } 160 161 int 162 _fini(void) 163 { 164 int status; 165 166 status = mod_remove(&modlinkage); 167 if (status == DDI_SUCCESS) 168 mac_fini_ops(&simnet_dev_ops); 169 170 return (status); 171 } 172 173 int 174 _info(struct modinfo *modinfop) 175 { 176 return (mod_info(&modlinkage, modinfop)); 177 } 178 179 static boolean_t 180 simnet_init(void) 181 { 182 if ((simnet_rxq = ddi_taskq_create(simnet_dip, "simnet", 1, 183 TASKQ_DEFAULTPRI, 0)) == NULL) 184 return (B_FALSE); 185 rw_init(&simnet_dev_lock, NULL, RW_DEFAULT, NULL); 186 list_create(&simnet_dev_list, sizeof (simnet_dev_t), 187 offsetof(simnet_dev_t, sd_listnode)); 188 return (B_TRUE); 189 } 190 191 static void 192 simnet_fini(void) 193 { 194 ASSERT(simnet_count == 0); 195 rw_destroy(&simnet_dev_lock); 196 list_destroy(&simnet_dev_list); 197 ddi_taskq_destroy(simnet_rxq); 198 } 199 200 /*ARGSUSED*/ 201 static int 202 simnet_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 203 void **result) 204 { 205 switch (infocmd) { 206 case DDI_INFO_DEVT2DEVINFO: 207 *result = simnet_dip; 208 return (DDI_SUCCESS); 209 case DDI_INFO_DEVT2INSTANCE: 210 *result = NULL; 211 return (DDI_SUCCESS); 212 } 213 return (DDI_FAILURE); 214 } 215 216 static int 217 simnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 218 { 219 switch (cmd) { 220 case DDI_ATTACH: 221 if (ddi_get_instance(dip) != 0) { 222 /* we only allow instance 0 to attach */ 223 return (DDI_FAILURE); 224 } 225 226 if (dld_ioc_register(SIMNET_IOC, simnet_ioc_list, 227 DLDIOCCNT(simnet_ioc_list)) != 0) 228 return (DDI_FAILURE); 229 230 simnet_dip = dip; 231 if (!simnet_init()) 232 return (DDI_FAILURE); 233 return (DDI_SUCCESS); 234 235 case DDI_RESUME: 236 return (DDI_SUCCESS); 237 238 default: 239 return (DDI_FAILURE); 240 } 241 } 242 243 /*ARGSUSED*/ 244 static int 245 simnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 246 { 247 switch (cmd) { 248 case DDI_DETACH: 249 /* 250 * Allow the simnet instance to be detached only if there 251 * are no simnets configured. 252 */ 253 if (simnet_count > 0) 254 return (DDI_FAILURE); 255 256 dld_ioc_unregister(SIMNET_IOC); 257 simnet_fini(); 258 simnet_dip = NULL; 259 return (DDI_SUCCESS); 260 261 case DDI_SUSPEND: 262 return (DDI_SUCCESS); 263 264 default: 265 return (DDI_FAILURE); 266 } 267 } 268 269 /* Caller must hold simnet_dev_lock */ 270 static simnet_dev_t * 271 simnet_dev_lookup(datalink_id_t link_id) 272 { 273 simnet_dev_t *sdev; 274 275 ASSERT(RW_LOCK_HELD(&simnet_dev_lock)); 276 for (sdev = list_head(&simnet_dev_list); sdev != NULL; 277 sdev = list_next(&simnet_dev_list, sdev)) { 278 if (!(sdev->sd_flags & SDF_SHUTDOWN) && 279 (sdev->sd_link_id == link_id)) { 280 atomic_inc_32(&sdev->sd_refcount); 281 return (sdev); 282 } 283 } 284 285 return (NULL); 286 } 287 288 static void 289 simnet_wifidev_free(simnet_dev_t *sdev) 290 { 291 simnet_wifidev_t *wdev = sdev->sd_wifidev; 292 int i; 293 294 for (i = 0; i < wdev->swd_esslist_num; i++) { 295 kmem_free(wdev->swd_esslist[i], 296 sizeof (wl_ess_conf_t)); 297 } 298 kmem_free(wdev, sizeof (simnet_wifidev_t)); 299 } 300 301 static void 302 simnet_dev_unref(simnet_dev_t *sdev) 303 { 304 305 ASSERT(sdev->sd_refcount > 0); 306 if (atomic_dec_32_nv(&sdev->sd_refcount) != 0) 307 return; 308 309 if (sdev->sd_mh != NULL) 310 (void) mac_unregister(sdev->sd_mh); 311 312 if (sdev->sd_wifidev != NULL) { 313 ASSERT(sdev->sd_type == DL_WIFI); 314 simnet_wifidev_free(sdev); 315 } 316 317 mutex_destroy(&sdev->sd_instlock); 318 cv_destroy(&sdev->sd_threadwait); 319 kmem_free(sdev->sd_mcastaddrs, ETHERADDRL * sdev->sd_mcastaddr_count); 320 kmem_free(sdev, sizeof (*sdev)); 321 simnet_count--; 322 } 323 324 static int 325 simnet_init_wifi(simnet_dev_t *sdev, mac_register_t *mac) 326 { 327 wifi_data_t wd = { 0 }; 328 int err; 329 330 sdev->sd_wifidev = kmem_zalloc(sizeof (simnet_wifidev_t), KM_NOSLEEP); 331 if (sdev->sd_wifidev == NULL) 332 return (ENOMEM); 333 334 sdev->sd_wifidev->swd_sdev = sdev; 335 sdev->sd_wifidev->swd_linkstatus = WL_NOTCONNECTED; 336 wd.wd_secalloc = WIFI_SEC_NONE; 337 wd.wd_opmode = IEEE80211_M_STA; 338 mac->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 339 mac->m_max_sdu = IEEE80211_MTU; 340 mac->m_pdata = &wd; 341 mac->m_pdata_size = sizeof (wd); 342 err = mac_register(mac, &sdev->sd_mh); 343 return (err); 344 } 345 346 static int 347 simnet_init_ether(simnet_dev_t *sdev, mac_register_t *mac) 348 { 349 int err; 350 351 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 352 mac->m_max_sdu = SIMNET_MAX_MTU; 353 mac->m_margin = VLAN_TAGSZ; 354 err = mac_register(mac, &sdev->sd_mh); 355 return (err); 356 } 357 358 static int 359 simnet_init_mac(simnet_dev_t *sdev) 360 { 361 mac_register_t *mac; 362 int err; 363 364 if ((mac = mac_alloc(MAC_VERSION)) == NULL) 365 return (ENOMEM); 366 367 mac->m_driver = sdev; 368 mac->m_dip = simnet_dip; 369 mac->m_instance = (uint_t)-1; 370 mac->m_src_addr = sdev->sd_mac_addr; 371 mac->m_callbacks = &simnet_m_callbacks; 372 mac->m_min_sdu = 0; 373 374 if (sdev->sd_type == DL_ETHER) 375 err = simnet_init_ether(sdev, mac); 376 else if (sdev->sd_type == DL_WIFI) 377 err = simnet_init_wifi(sdev, mac); 378 else 379 err = EINVAL; 380 381 mac_free(mac); 382 return (err); 383 } 384 385 /* ARGSUSED */ 386 static int 387 simnet_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 388 { 389 simnet_ioc_create_t *create_arg = karg; 390 simnet_dev_t *sdev; 391 simnet_dev_t *sdev_tmp; 392 int err = 0; 393 394 sdev = kmem_zalloc(sizeof (*sdev), KM_NOSLEEP); 395 if (sdev == NULL) 396 return (ENOMEM); 397 398 rw_enter(&simnet_dev_lock, RW_WRITER); 399 if ((sdev_tmp = simnet_dev_lookup(create_arg->sic_link_id)) != NULL) { 400 simnet_dev_unref(sdev_tmp); 401 rw_exit(&simnet_dev_lock); 402 kmem_free(sdev, sizeof (*sdev)); 403 return (EEXIST); 404 } 405 406 sdev->sd_type = create_arg->sic_type; 407 sdev->sd_link_id = create_arg->sic_link_id; 408 sdev->sd_zoneid = crgetzoneid(cred); 409 sdev->sd_refcount++; 410 mutex_init(&sdev->sd_instlock, NULL, MUTEX_DRIVER, NULL); 411 cv_init(&sdev->sd_threadwait, NULL, CV_DRIVER, NULL); 412 simnet_count++; 413 414 /* Simnets created from configuration on boot pass saved MAC address */ 415 if (create_arg->sic_mac_len == 0) { 416 /* Generate random MAC address */ 417 (void) random_get_pseudo_bytes(sdev->sd_mac_addr, ETHERADDRL); 418 /* Ensure MAC address is not multicast and is local */ 419 sdev->sd_mac_addr[0] = (sdev->sd_mac_addr[0] & ~1) | 2; 420 sdev->sd_mac_len = ETHERADDRL; 421 } else { 422 (void) memcpy(sdev->sd_mac_addr, create_arg->sic_mac_addr, 423 create_arg->sic_mac_len); 424 sdev->sd_mac_len = create_arg->sic_mac_len; 425 } 426 427 if ((err = simnet_init_mac(sdev)) != 0) { 428 simnet_dev_unref(sdev); 429 goto exit; 430 } 431 432 if ((err = dls_devnet_create(sdev->sd_mh, sdev->sd_link_id, 433 crgetzoneid(cred))) != 0) { 434 simnet_dev_unref(sdev); 435 goto exit; 436 } 437 438 mac_link_update(sdev->sd_mh, LINK_STATE_UP); 439 mac_tx_update(sdev->sd_mh); 440 list_insert_tail(&simnet_dev_list, sdev); 441 442 /* Always return MAC address back to caller */ 443 (void) memcpy(create_arg->sic_mac_addr, sdev->sd_mac_addr, 444 sdev->sd_mac_len); 445 create_arg->sic_mac_len = sdev->sd_mac_len; 446 exit: 447 rw_exit(&simnet_dev_lock); 448 return (err); 449 } 450 451 /* Caller must hold writer simnet_dev_lock */ 452 static datalink_id_t 453 simnet_remove_peer(simnet_dev_t *sdev) 454 { 455 simnet_dev_t *sdev_peer; 456 datalink_id_t peer_link_id = DATALINK_INVALID_LINKID; 457 458 ASSERT(RW_WRITE_HELD(&simnet_dev_lock)); 459 if ((sdev_peer = sdev->sd_peer_dev) != NULL) { 460 ASSERT(sdev == sdev_peer->sd_peer_dev); 461 sdev_peer->sd_peer_dev = NULL; 462 sdev->sd_peer_dev = NULL; 463 peer_link_id = sdev_peer->sd_link_id; 464 /* Release previous references held on both simnets */ 465 simnet_dev_unref(sdev_peer); 466 simnet_dev_unref(sdev); 467 } 468 469 return (peer_link_id); 470 } 471 472 /* ARGSUSED */ 473 static int 474 simnet_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 475 { 476 simnet_ioc_modify_t *modify_arg = karg; 477 simnet_dev_t *sdev; 478 simnet_dev_t *sdev_peer = NULL; 479 480 rw_enter(&simnet_dev_lock, RW_WRITER); 481 if ((sdev = simnet_dev_lookup(modify_arg->sim_link_id)) == NULL) { 482 rw_exit(&simnet_dev_lock); 483 return (ENOENT); 484 } 485 486 if (sdev->sd_zoneid != crgetzoneid(cred)) { 487 rw_exit(&simnet_dev_lock); 488 simnet_dev_unref(sdev); 489 return (ENOENT); 490 } 491 492 if (sdev->sd_link_id == modify_arg->sim_peer_link_id) { 493 /* Cannot peer with self */ 494 rw_exit(&simnet_dev_lock); 495 simnet_dev_unref(sdev); 496 return (EINVAL); 497 } 498 499 if (sdev->sd_peer_dev != NULL && sdev->sd_peer_dev->sd_link_id == 500 modify_arg->sim_peer_link_id) { 501 /* Nothing to modify */ 502 rw_exit(&simnet_dev_lock); 503 simnet_dev_unref(sdev); 504 return (0); 505 } 506 507 if (modify_arg->sim_peer_link_id != DATALINK_INVALID_LINKID) { 508 sdev_peer = simnet_dev_lookup(modify_arg->sim_peer_link_id); 509 if (sdev_peer == NULL) { 510 /* Peer simnet device not available */ 511 rw_exit(&simnet_dev_lock); 512 simnet_dev_unref(sdev); 513 return (ENOENT); 514 } 515 if (sdev_peer->sd_zoneid != sdev->sd_zoneid) { 516 /* The two peers must be in the same zone (for now). */ 517 rw_exit(&simnet_dev_lock); 518 simnet_dev_unref(sdev); 519 simnet_dev_unref(sdev_peer); 520 return (EACCES); 521 } 522 } 523 524 /* First remove any previous peer */ 525 (void) simnet_remove_peer(sdev); 526 527 if (sdev_peer != NULL) { 528 /* Remove any previous peer of sdev_peer */ 529 (void) simnet_remove_peer(sdev_peer); 530 /* Update both devices with the new peer */ 531 sdev_peer->sd_peer_dev = sdev; 532 sdev->sd_peer_dev = sdev_peer; 533 /* Hold references on both devices */ 534 } else { 535 /* Release sdev lookup reference */ 536 simnet_dev_unref(sdev); 537 } 538 539 rw_exit(&simnet_dev_lock); 540 return (0); 541 } 542 543 /* ARGSUSED */ 544 static int 545 simnet_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 546 { 547 int err; 548 simnet_dev_t *sdev; 549 simnet_dev_t *sdev_peer; 550 simnet_ioc_delete_t *delete_arg = karg; 551 datalink_id_t tmpid; 552 datalink_id_t peerid; 553 554 rw_enter(&simnet_dev_lock, RW_WRITER); 555 if ((sdev = simnet_dev_lookup(delete_arg->sid_link_id)) == NULL) { 556 rw_exit(&simnet_dev_lock); 557 return (ENOENT); 558 } 559 560 if (sdev->sd_zoneid != crgetzoneid(cred)) { 561 rw_exit(&simnet_dev_lock); 562 simnet_dev_unref(sdev); 563 return (ENOENT); 564 } 565 566 if ((err = dls_devnet_destroy(sdev->sd_mh, &tmpid, B_TRUE)) != 0) { 567 rw_exit(&simnet_dev_lock); 568 simnet_dev_unref(sdev); 569 return (err); 570 } 571 572 ASSERT(sdev->sd_link_id == tmpid); 573 /* Remove any attached peer link */ 574 peerid = simnet_remove_peer(sdev); 575 576 /* Prevent new threads from using the instance */ 577 mutex_enter(&sdev->sd_instlock); 578 sdev->sd_flags |= SDF_SHUTDOWN; 579 /* Wait until all active threads using the instance exit */ 580 while (sdev->sd_threadcount > 0) { 581 if (cv_wait_sig(&sdev->sd_threadwait, 582 &sdev->sd_instlock) == 0) { 583 /* Signaled */ 584 mutex_exit(&sdev->sd_instlock); 585 err = EINTR; 586 goto fail; 587 } 588 } 589 mutex_exit(&sdev->sd_instlock); 590 591 /* Try disabling the MAC */ 592 if ((err = mac_disable(sdev->sd_mh)) != 0) 593 goto fail; 594 595 list_remove(&simnet_dev_list, sdev); 596 rw_exit(&simnet_dev_lock); 597 simnet_dev_unref(sdev); /* Release lookup ref */ 598 /* Releasing the last ref performs sdev/mem free */ 599 simnet_dev_unref(sdev); 600 return (err); 601 fail: 602 /* Re-create simnet instance and add any previous peer */ 603 (void) dls_devnet_create(sdev->sd_mh, sdev->sd_link_id, 604 crgetzoneid(cred)); 605 sdev->sd_flags &= ~SDF_SHUTDOWN; 606 607 ASSERT(sdev->sd_peer_dev == NULL); 608 if (peerid != DATALINK_INVALID_LINKID && 609 ((sdev_peer = simnet_dev_lookup(peerid)) != NULL)) { 610 /* Attach peer device back */ 611 ASSERT(sdev_peer->sd_peer_dev == NULL); 612 sdev_peer->sd_peer_dev = sdev; 613 sdev->sd_peer_dev = sdev_peer; 614 /* Hold reference on both devices */ 615 } else { 616 /* 617 * No previous peer or previous peer no longer 618 * available so release lookup reference. 619 */ 620 simnet_dev_unref(sdev); 621 } 622 623 rw_exit(&simnet_dev_lock); 624 return (err); 625 } 626 627 /* ARGSUSED */ 628 static int 629 simnet_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp) 630 { 631 simnet_ioc_info_t *info_arg = karg; 632 simnet_dev_t *sdev; 633 634 /* Make sure that the simnet link is visible from the caller's zone. */ 635 if (!dls_devnet_islinkvisible(info_arg->sii_link_id, crgetzoneid(cred))) 636 return (ENOENT); 637 638 rw_enter(&simnet_dev_lock, RW_READER); 639 if ((sdev = simnet_dev_lookup(info_arg->sii_link_id)) == NULL) { 640 rw_exit(&simnet_dev_lock); 641 return (ENOENT); 642 } 643 644 (void) memcpy(info_arg->sii_mac_addr, sdev->sd_mac_addr, 645 sdev->sd_mac_len); 646 info_arg->sii_mac_len = sdev->sd_mac_len; 647 info_arg->sii_type = sdev->sd_type; 648 if (sdev->sd_peer_dev != NULL) 649 info_arg->sii_peer_link_id = sdev->sd_peer_dev->sd_link_id; 650 rw_exit(&simnet_dev_lock); 651 simnet_dev_unref(sdev); 652 return (0); 653 } 654 655 static boolean_t 656 simnet_thread_ref(simnet_dev_t *sdev) 657 { 658 mutex_enter(&sdev->sd_instlock); 659 if (sdev->sd_flags & SDF_SHUTDOWN || 660 !(sdev->sd_flags & SDF_STARTED)) { 661 mutex_exit(&sdev->sd_instlock); 662 return (B_FALSE); 663 } 664 sdev->sd_threadcount++; 665 mutex_exit(&sdev->sd_instlock); 666 return (B_TRUE); 667 } 668 669 static void 670 simnet_thread_unref(simnet_dev_t *sdev) 671 { 672 mutex_enter(&sdev->sd_instlock); 673 if (--sdev->sd_threadcount == 0) 674 cv_broadcast(&sdev->sd_threadwait); 675 mutex_exit(&sdev->sd_instlock); 676 } 677 678 /* 679 * TODO: Add properties to set Rx checksum flag behavior. 680 * 681 * o HCK_PARTIALCKSUM. 682 * o HCK_FULLCKSUM_OK. 683 */ 684 static void 685 simnet_rx(void *arg) 686 { 687 mblk_t *mp = arg; 688 mac_header_info_t hdr_info; 689 simnet_dev_t *sdev; 690 691 sdev = (simnet_dev_t *)mp->b_next; 692 mp->b_next = NULL; 693 694 /* Check for valid packet header */ 695 if (mac_header_info(sdev->sd_mh, mp, &hdr_info) != 0) { 696 mac_drop_pkt(mp, "invalid L2 header"); 697 sdev->sd_stats.recv_errors++; 698 goto rx_done; 699 } 700 701 /* 702 * When we are NOT in promiscuous mode we only receive 703 * unicast packets addressed to us and multicast packets that 704 * MAC clients have requested. 705 */ 706 if (!sdev->sd_promisc && 707 hdr_info.mhi_dsttype != MAC_ADDRTYPE_BROADCAST) { 708 if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_UNICAST && 709 bcmp(hdr_info.mhi_daddr, sdev->sd_mac_addr, 710 ETHERADDRL) != 0) { 711 freemsg(mp); 712 goto rx_done; 713 } else if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) { 714 mutex_enter(&sdev->sd_instlock); 715 if (mcastaddr_lookup(sdev, hdr_info.mhi_daddr) == 716 NULL) { 717 mutex_exit(&sdev->sd_instlock); 718 freemsg(mp); 719 goto rx_done; 720 } 721 mutex_exit(&sdev->sd_instlock); 722 } 723 } 724 725 /* 726 * We don't actually calculate and verify the IP header 727 * checksum because the nature of simnet makes it redundant to 728 * do so. The point is to test the presence of the flags. The 729 * Tx side will have already populated the checksum field. 730 */ 731 if ((sdev->sd_rx_cksum & HCKSUM_IPHDRCKSUM) != 0) { 732 mac_hcksum_set(mp, 0, 0, 0, 0, HCK_IPV4_HDRCKSUM_OK); 733 } 734 735 sdev->sd_stats.recv_count++; 736 sdev->sd_stats.rbytes += msgdsize(mp); 737 mac_rx(sdev->sd_mh, NULL, mp); 738 rx_done: 739 simnet_thread_unref(sdev); 740 } 741 742 #define SIMNET_ULP_CKSUM (HCKSUM_INET_FULL_V4 | HCKSUM_INET_PARTIAL) 743 744 static mblk_t * 745 simnet_m_tx(void *arg, mblk_t *mp_chain) 746 { 747 simnet_dev_t *sdev = arg; 748 simnet_dev_t *sdev_rx; 749 mblk_t *mpnext = mp_chain; 750 mblk_t *mp, *nmp; 751 mac_emul_t emul = 0; 752 753 rw_enter(&simnet_dev_lock, RW_READER); 754 if ((sdev_rx = sdev->sd_peer_dev) == NULL) { 755 /* Discard packets when no peer exists */ 756 rw_exit(&simnet_dev_lock); 757 mac_drop_chain(mp_chain, "no peer"); 758 return (NULL); 759 } 760 761 /* 762 * Discard packets when either device is shutting down or not ready. 763 * Though MAC layer ensures a reference is held on the MAC while we 764 * process the packet chain, there is no guarantee the peer MAC will 765 * remain enabled. So we increment per-instance threadcount to ensure 766 * either MAC instance is not disabled while we handle the chain of 767 * packets. It is okay if the peer device is disconnected while we are 768 * here since we lookup the peer device while holding simnet_dev_lock 769 * (reader lock) and increment the threadcount of the peer, the peer 770 * MAC cannot be disabled in simnet_ioc_delete. 771 */ 772 if (!simnet_thread_ref(sdev_rx)) { 773 rw_exit(&simnet_dev_lock); 774 mac_drop_chain(mp_chain, "simnet peer dev not ready"); 775 return (NULL); 776 } 777 rw_exit(&simnet_dev_lock); 778 779 if (!simnet_thread_ref(sdev)) { 780 simnet_thread_unref(sdev_rx); 781 mac_drop_chain(mp_chain, "simnet dev not ready"); 782 return (NULL); 783 } 784 785 while ((mp = mpnext) != NULL) { 786 size_t len; 787 size_t size; 788 mblk_t *mp_new; 789 mblk_t *mp_tmp; 790 791 mpnext = mp->b_next; 792 mp->b_next = NULL; 793 len = msgdsize(mp); 794 795 /* Pad packet to minimum Ethernet frame size */ 796 if (len < ETHERMIN) { 797 size = ETHERMIN - len; 798 mp_new = allocb(size, BPRI_HI); 799 if (mp_new == NULL) { 800 sdev->sd_stats.xmit_errors++; 801 mac_drop_pkt(mp, "allocb failed"); 802 continue; 803 } 804 bzero(mp_new->b_wptr, size); 805 mp_new->b_wptr += size; 806 807 mp_tmp = mp; 808 while (mp_tmp->b_cont != NULL) 809 mp_tmp = mp_tmp->b_cont; 810 mp_tmp->b_cont = mp_new; 811 len += size; 812 } 813 814 /* Pullup packet into a single mblk */ 815 if ((nmp = msgpullup(mp, -1)) == NULL) { 816 sdev->sd_stats.xmit_errors++; 817 mac_drop_pkt(mp, "msgpullup failed"); 818 continue; 819 } else { 820 mac_hcksum_clone(mp, nmp); 821 freemsg(mp); 822 mp = nmp; 823 } 824 825 /* Hold reference for taskq receive processing per-pkt */ 826 if (!simnet_thread_ref(sdev_rx)) { 827 mac_drop_pkt(mp, "failed to get thread ref"); 828 mac_drop_chain(mpnext, "failed to get thread ref"); 829 break; 830 } 831 832 if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0) 833 emul |= MAC_IPCKSUM_EMUL; 834 if ((sdev->sd_tx_cksum & SIMNET_ULP_CKSUM) != 0) 835 emul |= MAC_HWCKSUM_EMUL; 836 if (sdev->sd_lso) 837 emul |= MAC_LSO_EMUL; 838 839 if (emul != 0) 840 mac_hw_emul(&mp, NULL, NULL, emul); 841 842 if (mp == NULL) { 843 sdev->sd_stats.xmit_errors++; 844 continue; 845 } 846 847 /* 848 * Remember, we are emulating a real NIC here; the 849 * checksum flags can't make the trip across the link. 850 */ 851 DB_CKSUMFLAGS(mp) = 0; 852 853 /* Use taskq for pkt receive to avoid kernel stack explosion */ 854 mp->b_next = (mblk_t *)sdev_rx; 855 if (ddi_taskq_dispatch(simnet_rxq, simnet_rx, mp, 856 DDI_NOSLEEP) == DDI_SUCCESS) { 857 sdev->sd_stats.xmit_count++; 858 sdev->sd_stats.obytes += len; 859 } else { 860 simnet_thread_unref(sdev_rx); 861 mp->b_next = NULL; 862 freemsg(mp); 863 sdev_rx->sd_stats.recv_errors++; 864 } 865 } 866 867 simnet_thread_unref(sdev); 868 simnet_thread_unref(sdev_rx); 869 return (NULL); 870 } 871 872 static int 873 simnet_wifi_ioctl(simnet_dev_t *sdev, mblk_t *mp) 874 { 875 int rc = WL_SUCCESS; 876 simnet_wifidev_t *wdev = sdev->sd_wifidev; 877 878 /* LINTED E_BAD_PTR_CAST_ALIGN */ 879 switch (((wldp_t *)mp->b_rptr)->wldp_id) { 880 case WL_DISASSOCIATE: 881 wdev->swd_linkstatus = WL_NOTCONNECTED; 882 break; 883 default: 884 break; 885 } 886 return (rc); 887 } 888 889 static void 890 simnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 891 { 892 simnet_dev_t *sdev = arg; 893 struct iocblk *iocp; 894 mblk_t *mp1; 895 uint32_t cmd; 896 int rc; 897 898 if (sdev->sd_type != DL_WIFI) { 899 miocnak(q, mp, 0, ENOTSUP); 900 return; 901 } 902 903 /* LINTED E_BAD_PTR_CAST_ALIGN */ 904 iocp = (struct iocblk *)mp->b_rptr; 905 if (iocp->ioc_count == 0) { 906 miocnak(q, mp, 0, EINVAL); 907 return; 908 } 909 910 /* We only claim support for WiFi operation commands */ 911 cmd = iocp->ioc_cmd; 912 switch (cmd) { 913 default: 914 miocnak(q, mp, 0, EINVAL); 915 return; 916 case WLAN_GET_PARAM: 917 case WLAN_SET_PARAM: 918 case WLAN_COMMAND: 919 break; 920 } 921 922 mp1 = mp->b_cont; 923 freemsg(mp1->b_cont); 924 mp1->b_cont = NULL; 925 /* overwrite everything */ 926 mp1->b_wptr = mp1->b_rptr; 927 rc = simnet_wifi_ioctl(sdev, mp1); 928 miocack(q, mp, msgdsize(mp1), rc); 929 } 930 931 static boolean_t 932 simnet_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) 933 { 934 simnet_dev_t *sdev = arg; 935 const uint_t tcp_cksums = HCKSUM_INET_FULL_V4 | HCKSUM_INET_PARTIAL; 936 937 switch (cap) { 938 case MAC_CAPAB_HCKSUM: { 939 uint32_t *tx_cksum_flags = cap_data; 940 *tx_cksum_flags = sdev->sd_tx_cksum; 941 break; 942 } 943 case MAC_CAPAB_LSO: { 944 mac_capab_lso_t *cap_lso = cap_data; 945 946 if (sdev->sd_lso && 947 (sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0 && 948 (sdev->sd_tx_cksum & tcp_cksums) != 0) { 949 /* 950 * The LSO configuration is hardwried for now, 951 * but there's no reason we couldn't also make 952 * this configurable in the future. 953 */ 954 cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4; 955 cap_lso->lso_basic_tcp_ipv4.lso_max = SD_LSO_MAXLEN; 956 break; 957 } else { 958 return (B_FALSE); 959 } 960 } 961 default: 962 return (B_FALSE); 963 } 964 965 return (B_TRUE); 966 } 967 968 static int 969 simnet_m_stat(void *arg, uint_t stat, uint64_t *val) 970 { 971 int rval = 0; 972 simnet_dev_t *sdev = arg; 973 974 ASSERT(sdev->sd_mh != NULL); 975 976 switch (stat) { 977 case MAC_STAT_IFSPEED: 978 *val = 100 * 1000000ull; /* 100 Mbps */ 979 break; 980 case MAC_STAT_LINK_STATE: 981 *val = LINK_DUPLEX_FULL; 982 break; 983 case MAC_STAT_LINK_UP: 984 if (sdev->sd_flags & SDF_STARTED) 985 *val = LINK_STATE_UP; 986 else 987 *val = LINK_STATE_DOWN; 988 break; 989 case MAC_STAT_PROMISC: 990 case MAC_STAT_MULTIRCV: 991 case MAC_STAT_MULTIXMT: 992 case MAC_STAT_BRDCSTRCV: 993 case MAC_STAT_BRDCSTXMT: 994 rval = ENOTSUP; 995 break; 996 case MAC_STAT_OPACKETS: 997 *val = sdev->sd_stats.xmit_count; 998 break; 999 case MAC_STAT_OBYTES: 1000 *val = sdev->sd_stats.obytes; 1001 break; 1002 case MAC_STAT_IERRORS: 1003 *val = sdev->sd_stats.recv_errors; 1004 break; 1005 case MAC_STAT_OERRORS: 1006 *val = sdev->sd_stats.xmit_errors; 1007 break; 1008 case MAC_STAT_RBYTES: 1009 *val = sdev->sd_stats.rbytes; 1010 break; 1011 case MAC_STAT_IPACKETS: 1012 *val = sdev->sd_stats.recv_count; 1013 break; 1014 case WIFI_STAT_FCS_ERRORS: 1015 case WIFI_STAT_WEP_ERRORS: 1016 case WIFI_STAT_TX_FRAGS: 1017 case WIFI_STAT_MCAST_TX: 1018 case WIFI_STAT_RTS_SUCCESS: 1019 case WIFI_STAT_RTS_FAILURE: 1020 case WIFI_STAT_ACK_FAILURE: 1021 case WIFI_STAT_RX_FRAGS: 1022 case WIFI_STAT_MCAST_RX: 1023 case WIFI_STAT_RX_DUPS: 1024 rval = ENOTSUP; 1025 break; 1026 default: 1027 rval = ENOTSUP; 1028 break; 1029 } 1030 1031 return (rval); 1032 } 1033 1034 static int 1035 simnet_m_start(void *arg) 1036 { 1037 simnet_dev_t *sdev = arg; 1038 1039 sdev->sd_flags |= SDF_STARTED; 1040 return (0); 1041 } 1042 1043 static void 1044 simnet_m_stop(void *arg) 1045 { 1046 simnet_dev_t *sdev = arg; 1047 1048 sdev->sd_flags &= ~SDF_STARTED; 1049 } 1050 1051 static int 1052 simnet_m_promisc(void *arg, boolean_t on) 1053 { 1054 simnet_dev_t *sdev = arg; 1055 1056 sdev->sd_promisc = on; 1057 return (0); 1058 } 1059 1060 /* 1061 * Returns matching multicast address enabled on the simnet instance. 1062 * Assumes simnet instance mutex lock is held. 1063 */ 1064 static uint8_t * 1065 mcastaddr_lookup(simnet_dev_t *sdev, const uint8_t *addrp) 1066 { 1067 int idx; 1068 uint8_t *maddrptr; 1069 1070 ASSERT(MUTEX_HELD(&sdev->sd_instlock)); 1071 maddrptr = sdev->sd_mcastaddrs; 1072 for (idx = 0; idx < sdev->sd_mcastaddr_count; idx++) { 1073 if (bcmp(maddrptr, addrp, ETHERADDRL) == 0) 1074 return (maddrptr); 1075 maddrptr += ETHERADDRL; 1076 } 1077 1078 return (NULL); 1079 } 1080 1081 /* Add or remove Multicast addresses on simnet instance */ 1082 static int 1083 simnet_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 1084 { 1085 simnet_dev_t *sdev = arg; 1086 uint8_t *maddrptr; 1087 uint8_t *newbuf; 1088 size_t prevsize; 1089 size_t newsize; 1090 ptrdiff_t len; 1091 ptrdiff_t len2; 1092 1093 alloc_retry: 1094 prevsize = sdev->sd_mcastaddr_count * ETHERADDRL; 1095 newsize = prevsize + (add ? ETHERADDRL:-ETHERADDRL); 1096 newbuf = kmem_alloc(newsize, KM_SLEEP); 1097 1098 mutex_enter(&sdev->sd_instlock); 1099 if (prevsize != (sdev->sd_mcastaddr_count * ETHERADDRL)) { 1100 mutex_exit(&sdev->sd_instlock); 1101 kmem_free(newbuf, newsize); 1102 goto alloc_retry; 1103 } 1104 1105 maddrptr = mcastaddr_lookup(sdev, addrp); 1106 if (!add && maddrptr != NULL) { 1107 /* Removing a Multicast address */ 1108 if (newbuf != NULL) { 1109 /* LINTED: E_PTRDIFF_OVERFLOW */ 1110 len = maddrptr - sdev->sd_mcastaddrs; 1111 (void) memcpy(newbuf, sdev->sd_mcastaddrs, len); 1112 len2 = prevsize - len - ETHERADDRL; 1113 (void) memcpy(newbuf + len, 1114 maddrptr + ETHERADDRL, len2); 1115 } 1116 sdev->sd_mcastaddr_count--; 1117 } else if (add && maddrptr == NULL) { 1118 /* Adding a new Multicast address */ 1119 (void) memcpy(newbuf, sdev->sd_mcastaddrs, prevsize); 1120 (void) memcpy(newbuf + prevsize, addrp, ETHERADDRL); 1121 sdev->sd_mcastaddr_count++; 1122 } else { 1123 /* Error: removing a non-existing Multicast address */ 1124 mutex_exit(&sdev->sd_instlock); 1125 kmem_free(newbuf, newsize); 1126 cmn_err(CE_WARN, "simnet: MAC call to remove a " 1127 "Multicast address failed"); 1128 return (EINVAL); 1129 } 1130 1131 kmem_free(sdev->sd_mcastaddrs, prevsize); 1132 sdev->sd_mcastaddrs = newbuf; 1133 mutex_exit(&sdev->sd_instlock); 1134 return (0); 1135 } 1136 1137 static int 1138 simnet_m_unicst(void *arg, const uint8_t *macaddr) 1139 { 1140 simnet_dev_t *sdev = arg; 1141 1142 (void) memcpy(sdev->sd_mac_addr, macaddr, ETHERADDRL); 1143 return (0); 1144 } 1145 1146 /* Parse WiFi scan list entry arguments and return the arg count */ 1147 static int 1148 parse_esslist_args(const void *pr_val, uint_t pr_valsize, 1149 char args[][MAX_ESSLIST_ARGLEN]) 1150 { 1151 char *sep; 1152 ptrdiff_t len = pr_valsize; 1153 const char *piece = pr_val; 1154 const char *end = (const char *)pr_val + pr_valsize - 1; 1155 int arg = 0; 1156 1157 while (piece < end && (arg < MAX_ESSLIST_ARGS)) { 1158 sep = strchr(piece, ','); 1159 if (sep == NULL) 1160 sep = (char *)end; 1161 /* LINTED E_PTRDIFF_OVERFLOW */ 1162 len = sep - piece; 1163 /* If first arg is zero then return none to delete all */ 1164 if (arg == 0 && strnlen(piece, len) == 1 && piece[0] == '0') 1165 return (0); 1166 if (len > MAX_ESSLIST_ARGLEN) 1167 len = MAX_ESSLIST_ARGLEN - 1; 1168 (void) memcpy(&args[arg][0], piece, len); 1169 args[arg][len] = '\0'; 1170 piece = sep + 1; 1171 arg++; 1172 } 1173 1174 return (arg); 1175 } 1176 1177 /* Set WiFi scan list entry from private property _wl_esslist */ 1178 static int 1179 set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize, 1180 const void *pr_val) 1181 { 1182 char essargs[MAX_ESSLIST_ARGS][MAX_ESSLIST_ARGLEN]; 1183 wl_ess_conf_t *wls; 1184 long result; 1185 int i; 1186 1187 bzero(essargs, sizeof (essargs)); 1188 if (parse_esslist_args(pr_val, pr_valsize, essargs) == 0) { 1189 for (i = 0; i < wdev->swd_esslist_num; i++) { 1190 kmem_free(wdev->swd_esslist[i], sizeof (wl_ess_conf_t)); 1191 wdev->swd_esslist[i] = NULL; 1192 } 1193 wdev->swd_esslist_num = 0; 1194 return (0); 1195 } 1196 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 essargs[0]) == 0) 1201 return (EEXIST); 1202 } 1203 1204 if (wdev->swd_esslist_num >= MAX_SIMNET_ESSCONF) 1205 return (EINVAL); 1206 1207 wls = kmem_zalloc(sizeof (wl_ess_conf_t), KM_SLEEP); 1208 (void) strlcpy(wls->wl_ess_conf_essid.wl_essid_essid, 1209 essargs[0], sizeof (wls->wl_ess_conf_essid.wl_essid_essid)); 1210 wls->wl_ess_conf_essid.wl_essid_length = 1211 strlen(wls->wl_ess_conf_essid.wl_essid_essid); 1212 (void) random_get_pseudo_bytes((uint8_t *) 1213 &wls->wl_ess_conf_bssid, sizeof (wl_bssid_t)); 1214 (void) ddi_strtol(essargs[1], (char **)NULL, 0, &result); 1215 wls->wl_ess_conf_sl = (wl_rssi_t) 1216 ((result > MAX_RSSI || result < 0) ? 0:result); 1217 wdev->swd_esslist[wdev->swd_esslist_num] = wls; 1218 wdev->swd_esslist_num++; 1219 1220 return (0); 1221 } 1222 1223 static int 1224 simnet_set_priv_prop_wifi(simnet_dev_t *sdev, const char *name, 1225 const uint_t len, const void *val) 1226 { 1227 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1228 long result; 1229 1230 if (strcmp(name, "_wl_esslist") == 0) { 1231 if (val == NULL) 1232 return (EINVAL); 1233 return (set_wl_esslist_priv_prop(wdev, len, val)); 1234 } else if (strcmp(name, "_wl_connected") == 0) { 1235 if (val == NULL) 1236 return (EINVAL); 1237 (void) ddi_strtol(val, (char **)NULL, 0, &result); 1238 wdev->swd_linkstatus = ((result == 1) ? 1239 WL_CONNECTED:WL_NOTCONNECTED); 1240 return (0); 1241 } 1242 1243 return (EINVAL); 1244 } 1245 1246 /* ARGSUSED */ 1247 static int 1248 simnet_set_priv_prop_ether(simnet_dev_t *sdev, const char *name, 1249 const uint_t len, const void *val) 1250 { 1251 if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) { 1252 if (val == NULL) 1253 return (EINVAL); 1254 1255 if (strcmp(val, "off") == 0) { 1256 sdev->sd_rx_cksum &= ~HCKSUM_IPHDRCKSUM; 1257 } else if (strcmp(val, "on") == 0) { 1258 sdev->sd_rx_cksum |= HCKSUM_IPHDRCKSUM; 1259 } else { 1260 return (EINVAL); 1261 } 1262 1263 return (0); 1264 } else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) { 1265 if (val == NULL) 1266 return (EINVAL); 1267 1268 /* 1269 * Remember, full and partial checksum are mutually 1270 * exclusive. 1271 */ 1272 if (strcmp(val, "none") == 0) { 1273 sdev->sd_tx_cksum &= ~HCKSUM_INET_FULL_V4; 1274 } else if (strcmp(val, "fullv4") == 0) { 1275 sdev->sd_tx_cksum &= ~HCKSUM_INET_PARTIAL; 1276 sdev->sd_tx_cksum |= HCKSUM_INET_FULL_V4; 1277 } else if (strcmp(val, "partial") == 0) { 1278 sdev->sd_tx_cksum &= HCKSUM_INET_FULL_V4; 1279 sdev->sd_tx_cksum |= HCKSUM_INET_PARTIAL; 1280 } else { 1281 return (EINVAL); 1282 } 1283 1284 return (0); 1285 } else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) { 1286 if (val == NULL) 1287 return (EINVAL); 1288 1289 if (strcmp(val, "off") == 0) { 1290 sdev->sd_tx_cksum &= ~HCKSUM_IPHDRCKSUM; 1291 } else if (strcmp(val, "on") == 0) { 1292 sdev->sd_tx_cksum |= HCKSUM_IPHDRCKSUM; 1293 } else { 1294 return (EINVAL); 1295 } 1296 1297 return (0); 1298 } else if (strcmp(name, SD_PROP_LSO) == 0) { 1299 if (val == NULL) 1300 return (EINVAL); 1301 1302 if (strcmp(val, "off") == 0) { 1303 sdev->sd_lso = B_FALSE; 1304 } else if (strcmp(val, "on") == 0) { 1305 sdev->sd_lso = B_TRUE; 1306 } else { 1307 return (EINVAL); 1308 } 1309 1310 return (0); 1311 } 1312 1313 return (ENOTSUP); 1314 } 1315 1316 static int 1317 simnet_setprop_wifi(simnet_dev_t *sdev, const char *name, 1318 const mac_prop_id_t num, const uint_t len, const void *val) 1319 { 1320 int err = 0; 1321 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1322 1323 switch (num) { 1324 case MAC_PROP_WL_ESSID: { 1325 int i; 1326 wl_ess_conf_t *wls; 1327 1328 (void) memcpy(&wdev->swd_essid, val, sizeof (wl_essid_t)); 1329 wdev->swd_linkstatus = WL_CONNECTED; 1330 1331 /* Lookup the signal strength of the connected ESSID */ 1332 for (i = 0; i < wdev->swd_esslist_num; i++) { 1333 wls = wdev->swd_esslist[i]; 1334 if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid, 1335 wdev->swd_essid.wl_essid_essid) == 0) { 1336 wdev->swd_rssi = wls->wl_ess_conf_sl; 1337 break; 1338 } 1339 } 1340 break; 1341 } 1342 case MAC_PROP_WL_BSSID: { 1343 (void) memcpy(&wdev->swd_bssid, val, sizeof (wl_bssid_t)); 1344 break; 1345 } 1346 case MAC_PROP_WL_PHY_CONFIG: 1347 case MAC_PROP_WL_KEY_TAB: 1348 case MAC_PROP_WL_AUTH_MODE: 1349 case MAC_PROP_WL_ENCRYPTION: 1350 case MAC_PROP_WL_BSSTYPE: 1351 case MAC_PROP_WL_DESIRED_RATES: 1352 break; 1353 case MAC_PROP_PRIVATE: 1354 err = simnet_set_priv_prop_wifi(sdev, name, len, val); 1355 break; 1356 default: 1357 err = EINVAL; 1358 break; 1359 } 1360 1361 return (err); 1362 } 1363 1364 static int 1365 simnet_setprop_ether(simnet_dev_t *sdev, const char *name, 1366 const mac_prop_id_t num, const uint_t len, const void *val) 1367 { 1368 int err = 0; 1369 1370 switch (num) { 1371 case MAC_PROP_PRIVATE: 1372 err = simnet_set_priv_prop_ether(sdev, name, len, val); 1373 break; 1374 default: 1375 err = EINVAL; 1376 break; 1377 } 1378 1379 return (err); 1380 } 1381 1382 static int 1383 simnet_m_setprop(void *arg, const char *name, mac_prop_id_t num, 1384 const uint_t len, const void *val) 1385 { 1386 simnet_dev_t *sdev = arg; 1387 int err = 0; 1388 uint32_t mtu; 1389 1390 switch (num) { 1391 case MAC_PROP_MTU: 1392 (void) memcpy(&mtu, val, sizeof (mtu)); 1393 if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU) 1394 return (mac_maxsdu_update(sdev->sd_mh, mtu)); 1395 else 1396 return (EINVAL); 1397 default: 1398 break; 1399 } 1400 1401 switch (sdev->sd_type) { 1402 case DL_ETHER: 1403 err = simnet_setprop_ether(sdev, name, num, len, val); 1404 break; 1405 case DL_WIFI: 1406 err = simnet_setprop_wifi(sdev, name, num, len, val); 1407 break; 1408 default: 1409 err = EINVAL; 1410 break; 1411 } 1412 1413 /* 1414 * We may have modified the configuration of hardware 1415 * offloads. Make sure to renegotiate capabilities with the 1416 * upstream clients. 1417 */ 1418 mac_capab_update(sdev->sd_mh); 1419 return (err); 1420 } 1421 1422 static int 1423 simnet_get_priv_prop_wifi(const simnet_dev_t *sdev, const char *name, 1424 const uint_t len, void *val) 1425 { 1426 simnet_wifidev_t *wdev = sdev->sd_wifidev; 1427 int ret, value; 1428 1429 if (strcmp(name, "_wl_esslist") == 0) { 1430 /* Returns num of _wl_ess_conf_t that have been set */ 1431 value = wdev->swd_esslist_num; 1432 } else if (strcmp(name, "_wl_connected") == 0) { 1433 value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0); 1434 } else { 1435 return (ENOTSUP); 1436 } 1437 1438 ret = snprintf(val, len, "%d", value); 1439 1440 if (ret < 0 || ret >= len) 1441 return (EOVERFLOW); 1442 1443 return (0); 1444 } 1445 1446 static int 1447 simnet_get_priv_prop_ether(const simnet_dev_t *sdev, const char *name, 1448 const uint_t len, void *val) 1449 { 1450 int ret; 1451 char *value; 1452 1453 if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) { 1454 if ((sdev->sd_rx_cksum & HCKSUM_IPHDRCKSUM) != 0) { 1455 value = "on"; 1456 } else { 1457 value = "off"; 1458 } 1459 } else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) { 1460 if ((sdev->sd_tx_cksum & HCKSUM_INET_FULL_V4) != 0) { 1461 value = "fullv4"; 1462 } else if ((sdev->sd_tx_cksum & HCKSUM_INET_PARTIAL) != 0) { 1463 value = "partial"; 1464 } else { 1465 value = "none"; 1466 } 1467 } else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) { 1468 if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0) { 1469 value = "on"; 1470 } else { 1471 value = "off"; 1472 } 1473 } else if (strcmp(name, SD_PROP_LSO) == 0) { 1474 value = sdev->sd_lso ? "on" : "off"; 1475 } else { 1476 return (ENOTSUP); 1477 } 1478 1479 ret = snprintf(val, len, "%s", value); 1480 1481 if (ret < 0 || ret >= len) { 1482 return (EOVERFLOW); 1483 } 1484 1485 return (0); 1486 } 1487 1488 static int 1489 simnet_getprop_wifi(const simnet_dev_t *sdev, const char *name, 1490 const mac_prop_id_t num, const uint_t len, void *val) 1491 { 1492 const simnet_wifidev_t *wdev = sdev->sd_wifidev; 1493 int err = 0; 1494 1495 switch (num) { 1496 case MAC_PROP_WL_ESSID: 1497 (void) memcpy(val, &wdev->swd_essid, sizeof (wl_essid_t)); 1498 break; 1499 case MAC_PROP_WL_BSSID: 1500 (void) memcpy(val, &wdev->swd_bssid, sizeof (wl_bssid_t)); 1501 break; 1502 case MAC_PROP_WL_PHY_CONFIG: 1503 case MAC_PROP_WL_AUTH_MODE: 1504 case MAC_PROP_WL_ENCRYPTION: 1505 break; 1506 case MAC_PROP_WL_LINKSTATUS: 1507 (void) memcpy(val, &wdev->swd_linkstatus, 1508 sizeof (wdev->swd_linkstatus)); 1509 break; 1510 case MAC_PROP_WL_ESS_LIST: { 1511 wl_ess_conf_t *w_ess_conf; 1512 1513 ((wl_ess_list_t *)val)->wl_ess_list_num = wdev->swd_esslist_num; 1514 /* LINTED E_BAD_PTR_CAST_ALIGN */ 1515 w_ess_conf = (wl_ess_conf_t *)((char *)val + 1516 offsetof(wl_ess_list_t, wl_ess_list_ess)); 1517 for (uint_t i = 0; i < wdev->swd_esslist_num; i++) { 1518 (void) memcpy(w_ess_conf, wdev->swd_esslist[i], 1519 sizeof (wl_ess_conf_t)); 1520 w_ess_conf++; 1521 } 1522 break; 1523 } 1524 case MAC_PROP_WL_RSSI: 1525 *(wl_rssi_t *)val = wdev->swd_rssi; 1526 break; 1527 case MAC_PROP_WL_RADIO: 1528 *(wl_radio_t *)val = B_TRUE; 1529 break; 1530 case MAC_PROP_WL_POWER_MODE: 1531 break; 1532 case MAC_PROP_WL_DESIRED_RATES: 1533 break; 1534 case MAC_PROP_PRIVATE: 1535 err = simnet_get_priv_prop_wifi(sdev, name, len, val); 1536 break; 1537 default: 1538 err = ENOTSUP; 1539 break; 1540 } 1541 1542 return (err); 1543 } 1544 1545 static int 1546 simnet_getprop_ether(const simnet_dev_t *sdev, const char *name, 1547 const mac_prop_id_t num, const uint_t len, void *val) 1548 { 1549 int err = 0; 1550 1551 switch (num) { 1552 case MAC_PROP_PRIVATE: 1553 err = simnet_get_priv_prop_ether(sdev, name, len, val); 1554 break; 1555 default: 1556 err = ENOTSUP; 1557 break; 1558 } 1559 1560 return (err); 1561 } 1562 1563 static int 1564 simnet_m_getprop(void *arg, const char *name, const mac_prop_id_t num, 1565 const uint_t len, void *val) 1566 { 1567 const simnet_dev_t *sdev = arg; 1568 int err = 0; 1569 1570 switch (sdev->sd_type) { 1571 case DL_ETHER: 1572 err = simnet_getprop_ether(sdev, name, num, len, val); 1573 break; 1574 case DL_WIFI: 1575 err = simnet_getprop_wifi(sdev, name, num, len, val); 1576 break; 1577 default: 1578 err = EINVAL; 1579 break; 1580 } 1581 1582 return (err); 1583 } 1584 1585 static void 1586 simnet_priv_propinfo_wifi(const char *name, mac_prop_info_handle_t prh) 1587 { 1588 char valstr[MAXNAMELEN]; 1589 1590 bzero(valstr, sizeof (valstr)); 1591 1592 if (strcmp(name, "_wl_esslist") == 0) { 1593 (void) snprintf(valstr, sizeof (valstr), "%d", 0); 1594 } 1595 1596 if (strlen(valstr) > 0) 1597 mac_prop_info_set_default_str(prh, valstr); 1598 } 1599 1600 static void 1601 simnet_propinfo_wifi(const char *name, const mac_prop_id_t num, 1602 mac_prop_info_handle_t prh) 1603 { 1604 switch (num) { 1605 case MAC_PROP_WL_BSSTYPE: 1606 case MAC_PROP_WL_ESS_LIST: 1607 case MAC_PROP_WL_SUPPORTED_RATES: 1608 case MAC_PROP_WL_RSSI: 1609 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 1610 break; 1611 case MAC_PROP_PRIVATE: 1612 simnet_priv_propinfo_wifi(name, prh); 1613 break; 1614 } 1615 } 1616 1617 static void 1618 simnet_priv_propinfo_ether(const char *name, mac_prop_info_handle_t prh) 1619 { 1620 if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 || 1621 strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0 || 1622 strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 || 1623 strcmp(name, SD_PROP_LSO) == 0) { 1624 mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW); 1625 } 1626 1627 if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) { 1628 mac_prop_info_set_default_str(prh, "none"); 1629 } 1630 1631 if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 || 1632 strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 || 1633 strcmp(name, SD_PROP_LSO) == 0) { 1634 mac_prop_info_set_default_str(prh, "off"); 1635 } 1636 } 1637 1638 static void 1639 simnet_propinfo_ether(const char *name, const mac_prop_id_t num, 1640 mac_prop_info_handle_t prh) 1641 { 1642 switch (num) { 1643 case MAC_PROP_PRIVATE: 1644 simnet_priv_propinfo_ether(name, prh); 1645 break; 1646 } 1647 } 1648 1649 static void 1650 simnet_m_propinfo(void *arg, const char *name, const mac_prop_id_t num, 1651 const mac_prop_info_handle_t prh) 1652 { 1653 simnet_dev_t *sdev = arg; 1654 1655 switch (sdev->sd_type) { 1656 case DL_ETHER: 1657 simnet_propinfo_ether(name, num, prh); 1658 break; 1659 case DL_WIFI: 1660 simnet_propinfo_wifi(name, num, prh); 1661 break; 1662 } 1663 } 1664