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