xref: /illumos-gate/usr/src/uts/common/io/simnet/simnet.c (revision b1d7ec75953cd517f5b7c3d9cb427ff8ec5d7d07)
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 }
1359