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