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