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