xref: /illumos-gate/usr/src/uts/common/io/simnet/simnet.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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 const struct ether_addr *mcastaddr_lookup(const simnet_dev_t *,
77     const uint8_t *);
78 
79 static dld_ioc_info_t simnet_ioc_list[] = {
80 	{SIMNET_IOC_CREATE, DLDCOPYINOUT, sizeof (simnet_ioc_create_t),
81 	    simnet_ioc_create, secpolicy_dl_config},
82 	{SIMNET_IOC_DELETE, DLDCOPYIN, sizeof (simnet_ioc_delete_t),
83 	    simnet_ioc_delete, secpolicy_dl_config},
84 	{SIMNET_IOC_INFO, DLDCOPYINOUT, sizeof (simnet_ioc_info_t),
85 	    simnet_ioc_info, NULL},
86 	{SIMNET_IOC_MODIFY, DLDCOPYIN, sizeof (simnet_ioc_modify_t),
87 	    simnet_ioc_modify, secpolicy_dl_config}
88 };
89 
90 DDI_DEFINE_STREAM_OPS(simnet_dev_ops, nulldev, nulldev, simnet_attach,
91     simnet_detach, nodev, simnet_getinfo, D_MP, NULL,
92     ddi_quiesce_not_supported);
93 
94 static struct modldrv simnet_modldrv = {
95 	&mod_driverops,		/* Type of module.  This one is a driver */
96 	SIMNETINFO,		/* short description */
97 	&simnet_dev_ops		/* driver specific ops */
98 };
99 
100 static struct modlinkage modlinkage = {
101 	MODREV_1, &simnet_modldrv, NULL
102 };
103 
104 /* MAC callback function declarations */
105 static int simnet_m_start(void *);
106 static void simnet_m_stop(void *);
107 static int simnet_m_promisc(void *, boolean_t);
108 static int simnet_m_multicst(void *, boolean_t, const uint8_t *);
109 static int simnet_m_unicst(void *, const uint8_t *);
110 static int simnet_m_stat(void *, uint_t, uint64_t *);
111 static void simnet_m_ioctl(void *, queue_t *, mblk_t *);
112 static mblk_t *simnet_m_tx(void *, mblk_t *);
113 static int simnet_m_setprop(void *, const char *, mac_prop_id_t,
114     const uint_t, const void *);
115 static int simnet_m_getprop(void *, const char *, mac_prop_id_t,
116     uint_t, void *);
117 static void simnet_m_propinfo(void *, const char *, mac_prop_id_t,
118     mac_prop_info_handle_t);
119 static boolean_t simnet_m_getcapab(void *, mac_capab_t, void *);
120 
121 static mac_callbacks_t simnet_m_callbacks = {
122 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO),
123 	simnet_m_stat,
124 	simnet_m_start,
125 	simnet_m_stop,
126 	simnet_m_promisc,
127 	simnet_m_multicst,
128 	simnet_m_unicst,
129 	simnet_m_tx,
130 	NULL,
131 	simnet_m_ioctl,
132 	simnet_m_getcapab,
133 	NULL,
134 	NULL,
135 	simnet_m_setprop,
136 	simnet_m_getprop,
137 	simnet_m_propinfo
138 };
139 
140 /*
141  * simnet_dev_lock protects the simnet device list.
142  * sd_instlock in each simnet_dev_t protects access to
143  * a single simnet_dev_t.
144  */
145 static krwlock_t	simnet_dev_lock;
146 static list_t		simnet_dev_list;
147 static int		simnet_count; /* Num of simnet instances */
148 
149 int
150 _init(void)
151 {
152 	int	status;
153 
154 	mac_init_ops(&simnet_dev_ops, "simnet");
155 	status = mod_install(&modlinkage);
156 	if (status != DDI_SUCCESS)
157 		mac_fini_ops(&simnet_dev_ops);
158 
159 	return (status);
160 }
161 
162 int
163 _fini(void)
164 {
165 	int	status;
166 
167 	status = mod_remove(&modlinkage);
168 	if (status == DDI_SUCCESS)
169 		mac_fini_ops(&simnet_dev_ops);
170 
171 	return (status);
172 }
173 
174 int
175 _info(struct modinfo *modinfop)
176 {
177 	return (mod_info(&modlinkage, modinfop));
178 }
179 
180 static boolean_t
181 simnet_init(void)
182 {
183 	if ((simnet_rxq = ddi_taskq_create(simnet_dip, "simnet", 1,
184 	    TASKQ_DEFAULTPRI, 0)) == NULL)
185 		return (B_FALSE);
186 	rw_init(&simnet_dev_lock, NULL, RW_DEFAULT, NULL);
187 	list_create(&simnet_dev_list, sizeof (simnet_dev_t),
188 	    offsetof(simnet_dev_t, sd_listnode));
189 	return (B_TRUE);
190 }
191 
192 static void
193 simnet_fini(void)
194 {
195 	ASSERT(simnet_count == 0);
196 	rw_destroy(&simnet_dev_lock);
197 	list_destroy(&simnet_dev_list);
198 	ddi_taskq_destroy(simnet_rxq);
199 }
200 
201 /*ARGSUSED*/
202 static int
203 simnet_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
204     void **result)
205 {
206 	switch (infocmd) {
207 	case DDI_INFO_DEVT2DEVINFO:
208 		*result = simnet_dip;
209 		return (DDI_SUCCESS);
210 	case DDI_INFO_DEVT2INSTANCE:
211 		*result = NULL;
212 		return (DDI_SUCCESS);
213 	}
214 	return (DDI_FAILURE);
215 }
216 
217 static int
218 simnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
219 {
220 	switch (cmd) {
221 	case DDI_ATTACH:
222 		if (ddi_get_instance(dip) != 0) {
223 			/* we only allow instance 0 to attach */
224 			return (DDI_FAILURE);
225 		}
226 
227 		if (dld_ioc_register(SIMNET_IOC, simnet_ioc_list,
228 		    DLDIOCCNT(simnet_ioc_list)) != 0)
229 			return (DDI_FAILURE);
230 
231 		simnet_dip = dip;
232 		if (!simnet_init())
233 			return (DDI_FAILURE);
234 		return (DDI_SUCCESS);
235 
236 	case DDI_RESUME:
237 		return (DDI_SUCCESS);
238 
239 	default:
240 		return (DDI_FAILURE);
241 	}
242 }
243 
244 /*ARGSUSED*/
245 static int
246 simnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
247 {
248 	switch (cmd) {
249 	case DDI_DETACH:
250 		/*
251 		 * Allow the simnet instance to be detached only if there
252 		 * are no simnets configured.
253 		 */
254 		if (simnet_count > 0)
255 			return (DDI_FAILURE);
256 
257 		dld_ioc_unregister(SIMNET_IOC);
258 		simnet_fini();
259 		simnet_dip = NULL;
260 		return (DDI_SUCCESS);
261 
262 	case DDI_SUSPEND:
263 		return (DDI_SUCCESS);
264 
265 	default:
266 		return (DDI_FAILURE);
267 	}
268 }
269 
270 /* Caller must hold simnet_dev_lock */
271 static simnet_dev_t *
272 simnet_dev_lookup(datalink_id_t link_id)
273 {
274 	simnet_dev_t *sdev;
275 
276 	ASSERT(RW_LOCK_HELD(&simnet_dev_lock));
277 	for (sdev = list_head(&simnet_dev_list); sdev != NULL;
278 	    sdev = list_next(&simnet_dev_list, sdev)) {
279 		if (!(sdev->sd_flags & SDF_SHUTDOWN) &&
280 		    (sdev->sd_link_id == link_id)) {
281 			atomic_inc_32(&sdev->sd_refcount);
282 			return (sdev);
283 		}
284 	}
285 
286 	return (NULL);
287 }
288 
289 static void
290 simnet_wifidev_free(simnet_dev_t *sdev)
291 {
292 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
293 	int i;
294 
295 	for (i = 0; i < wdev->swd_esslist_num; i++) {
296 		kmem_free(wdev->swd_esslist[i],
297 		    sizeof (wl_ess_conf_t));
298 	}
299 	kmem_free(wdev, sizeof (simnet_wifidev_t));
300 }
301 
302 static void
303 simnet_dev_unref(simnet_dev_t *sdev)
304 {
305 
306 	ASSERT(sdev->sd_refcount > 0);
307 	if (atomic_dec_32_nv(&sdev->sd_refcount) != 0)
308 		return;
309 
310 	if (sdev->sd_mh != NULL)
311 		(void) mac_unregister(sdev->sd_mh);
312 
313 	if (sdev->sd_wifidev != NULL) {
314 		ASSERT(sdev->sd_type == DL_WIFI);
315 		simnet_wifidev_free(sdev);
316 	}
317 
318 	mutex_destroy(&sdev->sd_instlock);
319 	cv_destroy(&sdev->sd_threadwait);
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 const struct ether_addr *
1065 mcastaddr_lookup(const simnet_dev_t *sdev, const uint8_t *addrp)
1066 {
1067 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1068 	for (uint_t i = 0; i < sdev->sd_mcastaddr_count; i++) {
1069 		const struct ether_addr *maddrp = &sdev->sd_mcastaddrs[i];
1070 
1071 		if (bcmp(maddrp->ether_addr_octet, addrp,
1072 		    sizeof (maddrp->ether_addr_octet)) == 0) {
1073 			return (maddrp);
1074 		}
1075 	}
1076 
1077 	return (NULL);
1078 }
1079 
1080 static int
1081 simnet_multicst_add(simnet_dev_t *sdev, const struct ether_addr *eap)
1082 {
1083 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1084 
1085 	if ((eap->ether_addr_octet[0] & 01) == 0) {
1086 		return (EINVAL);
1087 	}
1088 
1089 	if (sdev->sd_mcastaddr_count == SM_MAX_NUM_MCAST_ADDRS) {
1090 		return (ENOSPC);
1091 	}
1092 
1093 	bcopy(eap, &sdev->sd_mcastaddrs[sdev->sd_mcastaddr_count],
1094 	    sizeof (*eap));
1095 	sdev->sd_mcastaddr_count++;
1096 	return (0);
1097 }
1098 
1099 static int
1100 simnet_multicst_rm(simnet_dev_t *sdev, const struct ether_addr *eap)
1101 {
1102 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1103 
1104 	for (uint_t i = 0; i < sdev->sd_mcastaddr_count; i++) {
1105 		if (bcmp(eap, &sdev->sd_mcastaddrs[i], sizeof (*eap)) == 0) {
1106 			for (i++; i < sdev->sd_mcastaddr_count; i++) {
1107 				sdev->sd_mcastaddrs[i - 1] =
1108 				    sdev->sd_mcastaddrs[i];
1109 			}
1110 
1111 			/* Zero-out the last entry as it is no longer valid. */
1112 			bzero(&sdev->sd_mcastaddrs[i - 1],
1113 			    sizeof (sdev->sd_mcastaddrs[0]));
1114 
1115 			sdev->sd_mcastaddr_count--;
1116 			return (0);
1117 		}
1118 	}
1119 
1120 	return (EINVAL);
1121 }
1122 
1123 /* Add or remove Multicast addresses on simnet instance */
1124 static int
1125 simnet_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
1126 {
1127 	simnet_dev_t *sdev = arg;
1128 	struct ether_addr ea;
1129 	int ret;
1130 
1131 	bcopy(addrp, ea.ether_addr_octet, sizeof (ea.ether_addr_octet));
1132 	mutex_enter(&sdev->sd_instlock);
1133 
1134 	if (add) {
1135 		ret = simnet_multicst_add(sdev, &ea);
1136 	} else {
1137 		ret = simnet_multicst_rm(sdev, &ea);
1138 	}
1139 
1140 	ASSERT3U(sdev->sd_mcastaddr_count, <=, SM_MAX_NUM_MCAST_ADDRS);
1141 	mutex_exit(&sdev->sd_instlock);
1142 	return (ret);
1143 }
1144 
1145 static int
1146 simnet_m_unicst(void *arg, const uint8_t *macaddr)
1147 {
1148 	simnet_dev_t *sdev = arg;
1149 
1150 	(void) memcpy(sdev->sd_mac_addr, macaddr, ETHERADDRL);
1151 	return (0);
1152 }
1153 
1154 /* Parse WiFi scan list entry arguments and return the arg count */
1155 static int
1156 parse_esslist_args(const void *pr_val, uint_t pr_valsize,
1157     char args[][MAX_ESSLIST_ARGLEN])
1158 {
1159 	char *sep;
1160 	ptrdiff_t len = pr_valsize;
1161 	const char *piece = pr_val;
1162 	const char *end = (const char *)pr_val + pr_valsize - 1;
1163 	int arg = 0;
1164 
1165 	while (piece < end && (arg < MAX_ESSLIST_ARGS)) {
1166 		sep = strchr(piece, ',');
1167 		if (sep == NULL)
1168 			sep = (char *)end;
1169 		/* LINTED E_PTRDIFF_OVERFLOW */
1170 		len = sep - piece;
1171 		/* If first arg is zero then return none to delete all */
1172 		if (arg == 0 && strnlen(piece, len) == 1 && piece[0] == '0')
1173 			return (0);
1174 		if (len > MAX_ESSLIST_ARGLEN)
1175 			len = MAX_ESSLIST_ARGLEN - 1;
1176 		(void) memcpy(&args[arg][0], piece, len);
1177 		args[arg][len] = '\0';
1178 		piece = sep + 1;
1179 		arg++;
1180 	}
1181 
1182 	return (arg);
1183 }
1184 
1185 /* Set WiFi scan list entry from private property _wl_esslist */
1186 static int
1187 set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize,
1188     const void *pr_val)
1189 {
1190 	char essargs[MAX_ESSLIST_ARGS][MAX_ESSLIST_ARGLEN];
1191 	wl_ess_conf_t *wls;
1192 	long result;
1193 	int i;
1194 
1195 	bzero(essargs, sizeof (essargs));
1196 	if (parse_esslist_args(pr_val, pr_valsize, essargs) == 0) {
1197 		for (i = 0; i < wdev->swd_esslist_num; i++) {
1198 			kmem_free(wdev->swd_esslist[i], sizeof (wl_ess_conf_t));
1199 			wdev->swd_esslist[i] = NULL;
1200 		}
1201 		wdev->swd_esslist_num = 0;
1202 		return (0);
1203 	}
1204 
1205 	for (i = 0; i < wdev->swd_esslist_num; i++) {
1206 		wls = wdev->swd_esslist[i];
1207 		if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
1208 		    essargs[0]) == 0)
1209 			return (EEXIST);
1210 	}
1211 
1212 	if (wdev->swd_esslist_num >= MAX_SIMNET_ESSCONF)
1213 		return (EINVAL);
1214 
1215 	wls = kmem_zalloc(sizeof (wl_ess_conf_t), KM_SLEEP);
1216 	(void) strlcpy(wls->wl_ess_conf_essid.wl_essid_essid,
1217 	    essargs[0], sizeof (wls->wl_ess_conf_essid.wl_essid_essid));
1218 	wls->wl_ess_conf_essid.wl_essid_length =
1219 	    strlen(wls->wl_ess_conf_essid.wl_essid_essid);
1220 	(void) random_get_pseudo_bytes((uint8_t *)
1221 	    &wls->wl_ess_conf_bssid, sizeof (wl_bssid_t));
1222 	(void) ddi_strtol(essargs[1], (char **)NULL, 0, &result);
1223 	wls->wl_ess_conf_sl = (wl_rssi_t)
1224 	    ((result > MAX_RSSI || result < 0) ? 0:result);
1225 	wdev->swd_esslist[wdev->swd_esslist_num] = wls;
1226 	wdev->swd_esslist_num++;
1227 
1228 	return (0);
1229 }
1230 
1231 static int
1232 simnet_set_priv_prop_wifi(simnet_dev_t *sdev, const char *name,
1233     const uint_t len, const void *val)
1234 {
1235 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1236 	long result;
1237 
1238 	if (strcmp(name, "_wl_esslist") == 0) {
1239 		if (val == NULL)
1240 			return (EINVAL);
1241 		return (set_wl_esslist_priv_prop(wdev, len, val));
1242 	} else if (strcmp(name, "_wl_connected") == 0) {
1243 		if (val == NULL)
1244 			return (EINVAL);
1245 		(void) ddi_strtol(val, (char **)NULL, 0, &result);
1246 		wdev->swd_linkstatus = ((result == 1) ?
1247 		    WL_CONNECTED:WL_NOTCONNECTED);
1248 		return (0);
1249 	}
1250 
1251 	return (EINVAL);
1252 }
1253 
1254 /* ARGSUSED */
1255 static int
1256 simnet_set_priv_prop_ether(simnet_dev_t *sdev, const char *name,
1257     const uint_t len, const void *val)
1258 {
1259 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) {
1260 		if (val == NULL)
1261 			return (EINVAL);
1262 
1263 		if (strcmp(val, "off") == 0) {
1264 			sdev->sd_rx_cksum &= ~HCKSUM_IPHDRCKSUM;
1265 		} else if (strcmp(val, "on") == 0) {
1266 			sdev->sd_rx_cksum |= HCKSUM_IPHDRCKSUM;
1267 		} else {
1268 			return (EINVAL);
1269 		}
1270 
1271 		return (0);
1272 	} else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1273 		if (val == NULL)
1274 			return (EINVAL);
1275 
1276 		/*
1277 		 * Remember, full and partial checksum are mutually
1278 		 * exclusive.
1279 		 */
1280 		if (strcmp(val, "none") == 0) {
1281 			sdev->sd_tx_cksum &= ~HCKSUM_INET_FULL_V4;
1282 		} else if (strcmp(val, "fullv4") == 0) {
1283 			sdev->sd_tx_cksum &= ~HCKSUM_INET_PARTIAL;
1284 			sdev->sd_tx_cksum |= HCKSUM_INET_FULL_V4;
1285 		} else if (strcmp(val, "partial") == 0) {
1286 			sdev->sd_tx_cksum &= HCKSUM_INET_FULL_V4;
1287 			sdev->sd_tx_cksum |= HCKSUM_INET_PARTIAL;
1288 		} else {
1289 			return (EINVAL);
1290 		}
1291 
1292 		return (0);
1293 	} else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
1294 		if (val == NULL)
1295 			return (EINVAL);
1296 
1297 		if (strcmp(val, "off") == 0) {
1298 			sdev->sd_tx_cksum &= ~HCKSUM_IPHDRCKSUM;
1299 		} else if (strcmp(val, "on") == 0) {
1300 			sdev->sd_tx_cksum |= HCKSUM_IPHDRCKSUM;
1301 		} else {
1302 			return (EINVAL);
1303 		}
1304 
1305 		return (0);
1306 	} else if (strcmp(name, SD_PROP_LSO) == 0) {
1307 		if (val == NULL)
1308 			return (EINVAL);
1309 
1310 		if (strcmp(val, "off") == 0) {
1311 			sdev->sd_lso = B_FALSE;
1312 		} else if (strcmp(val, "on") == 0) {
1313 			sdev->sd_lso = B_TRUE;
1314 		} else {
1315 			return (EINVAL);
1316 		}
1317 
1318 		return (0);
1319 	}
1320 
1321 	return (ENOTSUP);
1322 }
1323 
1324 static int
1325 simnet_setprop_wifi(simnet_dev_t *sdev, const char *name,
1326     const mac_prop_id_t num, const uint_t len, const void *val)
1327 {
1328 	int err = 0;
1329 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1330 
1331 	switch (num) {
1332 	case MAC_PROP_WL_ESSID: {
1333 		int i;
1334 		wl_ess_conf_t *wls;
1335 
1336 		(void) memcpy(&wdev->swd_essid, val, sizeof (wl_essid_t));
1337 		wdev->swd_linkstatus = WL_CONNECTED;
1338 
1339 		/* Lookup the signal strength of the connected ESSID */
1340 		for (i = 0; i < wdev->swd_esslist_num; i++) {
1341 			wls = wdev->swd_esslist[i];
1342 			if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
1343 			    wdev->swd_essid.wl_essid_essid) == 0) {
1344 				wdev->swd_rssi = wls->wl_ess_conf_sl;
1345 				break;
1346 			}
1347 		}
1348 		break;
1349 	}
1350 	case MAC_PROP_WL_BSSID: {
1351 		(void) memcpy(&wdev->swd_bssid, val, sizeof (wl_bssid_t));
1352 		break;
1353 	}
1354 	case MAC_PROP_WL_PHY_CONFIG:
1355 	case MAC_PROP_WL_KEY_TAB:
1356 	case MAC_PROP_WL_AUTH_MODE:
1357 	case MAC_PROP_WL_ENCRYPTION:
1358 	case MAC_PROP_WL_BSSTYPE:
1359 	case MAC_PROP_WL_DESIRED_RATES:
1360 		break;
1361 	case MAC_PROP_PRIVATE:
1362 		err = simnet_set_priv_prop_wifi(sdev, name, len, val);
1363 		break;
1364 	default:
1365 		err = EINVAL;
1366 		break;
1367 	}
1368 
1369 	return (err);
1370 }
1371 
1372 static int
1373 simnet_setprop_ether(simnet_dev_t *sdev, const char *name,
1374     const mac_prop_id_t num, const uint_t len, const void *val)
1375 {
1376 	int err = 0;
1377 
1378 	switch (num) {
1379 	case MAC_PROP_PRIVATE:
1380 		err = simnet_set_priv_prop_ether(sdev, name, len, val);
1381 		break;
1382 	default:
1383 		err = EINVAL;
1384 		break;
1385 	}
1386 
1387 	return (err);
1388 }
1389 
1390 static int
1391 simnet_m_setprop(void *arg, const char *name, mac_prop_id_t num,
1392     const uint_t len, const void *val)
1393 {
1394 	simnet_dev_t *sdev = arg;
1395 	int err = 0;
1396 	uint32_t mtu;
1397 
1398 	switch (num) {
1399 	case MAC_PROP_MTU:
1400 		(void) memcpy(&mtu, val, sizeof (mtu));
1401 		if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU)
1402 			return (mac_maxsdu_update(sdev->sd_mh, mtu));
1403 		else
1404 			return (EINVAL);
1405 	default:
1406 		break;
1407 	}
1408 
1409 	switch (sdev->sd_type) {
1410 	case DL_ETHER:
1411 		err = simnet_setprop_ether(sdev, name, num, len, val);
1412 		break;
1413 	case DL_WIFI:
1414 		err = simnet_setprop_wifi(sdev, name, num, len, val);
1415 		break;
1416 	default:
1417 		err = EINVAL;
1418 		break;
1419 	}
1420 
1421 	/*
1422 	 * We may have modified the configuration of hardware
1423 	 * offloads. Make sure to renegotiate capabilities with the
1424 	 * upstream clients.
1425 	 */
1426 	mac_capab_update(sdev->sd_mh);
1427 	return (err);
1428 }
1429 
1430 static int
1431 simnet_get_priv_prop_wifi(const simnet_dev_t *sdev, const char *name,
1432     const uint_t len, void *val)
1433 {
1434 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1435 	int ret, value;
1436 
1437 	if (strcmp(name, "_wl_esslist") == 0) {
1438 		/* Returns num of _wl_ess_conf_t that have been set */
1439 		value = wdev->swd_esslist_num;
1440 	} else if (strcmp(name, "_wl_connected") == 0) {
1441 		value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0);
1442 	} else {
1443 		return (ENOTSUP);
1444 	}
1445 
1446 	ret = snprintf(val, len, "%d", value);
1447 
1448 	if (ret < 0 || ret >= len)
1449 		return (EOVERFLOW);
1450 
1451 	return (0);
1452 }
1453 
1454 static int
1455 simnet_get_priv_prop_ether(const simnet_dev_t *sdev, const char *name,
1456     const uint_t len, void *val)
1457 {
1458 	int ret;
1459 	char *value;
1460 
1461 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) {
1462 		if ((sdev->sd_rx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
1463 			value = "on";
1464 		} else {
1465 			value = "off";
1466 		}
1467 	} else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1468 		if ((sdev->sd_tx_cksum & HCKSUM_INET_FULL_V4) != 0) {
1469 			value = "fullv4";
1470 		} else if ((sdev->sd_tx_cksum & HCKSUM_INET_PARTIAL) != 0) {
1471 			value = "partial";
1472 		} else {
1473 			value = "none";
1474 		}
1475 	} else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
1476 		if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
1477 			value = "on";
1478 		} else {
1479 			value = "off";
1480 		}
1481 	} else if (strcmp(name, SD_PROP_LSO) == 0) {
1482 		value = sdev->sd_lso ? "on" : "off";
1483 	} else {
1484 		return (ENOTSUP);
1485 	}
1486 
1487 	ret = snprintf(val, len, "%s", value);
1488 
1489 	if (ret < 0 || ret >= len) {
1490 		return (EOVERFLOW);
1491 	}
1492 
1493 	return (0);
1494 }
1495 
1496 static int
1497 simnet_getprop_wifi(const simnet_dev_t *sdev, const char *name,
1498     const mac_prop_id_t num, const uint_t len, void *val)
1499 {
1500 	const simnet_wifidev_t *wdev = sdev->sd_wifidev;
1501 	int err = 0;
1502 
1503 	switch (num) {
1504 	case MAC_PROP_WL_ESSID:
1505 		(void) memcpy(val, &wdev->swd_essid, sizeof (wl_essid_t));
1506 		break;
1507 	case MAC_PROP_WL_BSSID:
1508 		(void) memcpy(val, &wdev->swd_bssid, sizeof (wl_bssid_t));
1509 		break;
1510 	case MAC_PROP_WL_PHY_CONFIG:
1511 	case MAC_PROP_WL_AUTH_MODE:
1512 	case MAC_PROP_WL_ENCRYPTION:
1513 		break;
1514 	case MAC_PROP_WL_LINKSTATUS:
1515 		(void) memcpy(val, &wdev->swd_linkstatus,
1516 		    sizeof (wdev->swd_linkstatus));
1517 		break;
1518 	case MAC_PROP_WL_ESS_LIST: {
1519 		wl_ess_conf_t *w_ess_conf;
1520 
1521 		((wl_ess_list_t *)val)->wl_ess_list_num = wdev->swd_esslist_num;
1522 		/* LINTED E_BAD_PTR_CAST_ALIGN */
1523 		w_ess_conf = (wl_ess_conf_t *)((char *)val +
1524 		    offsetof(wl_ess_list_t, wl_ess_list_ess));
1525 		for (uint_t i = 0; i < wdev->swd_esslist_num; i++) {
1526 			(void) memcpy(w_ess_conf, wdev->swd_esslist[i],
1527 			    sizeof (wl_ess_conf_t));
1528 			w_ess_conf++;
1529 		}
1530 		break;
1531 	}
1532 	case MAC_PROP_WL_RSSI:
1533 		*(wl_rssi_t *)val = wdev->swd_rssi;
1534 		break;
1535 	case MAC_PROP_WL_RADIO:
1536 		*(wl_radio_t *)val = B_TRUE;
1537 		break;
1538 	case MAC_PROP_WL_POWER_MODE:
1539 		break;
1540 	case MAC_PROP_WL_DESIRED_RATES:
1541 		break;
1542 	case MAC_PROP_PRIVATE:
1543 		err = simnet_get_priv_prop_wifi(sdev, name, len, val);
1544 		break;
1545 	default:
1546 		err = ENOTSUP;
1547 		break;
1548 	}
1549 
1550 	return (err);
1551 }
1552 
1553 static int
1554 simnet_getprop_ether(const simnet_dev_t *sdev, const char *name,
1555     const mac_prop_id_t num, const uint_t len, void *val)
1556 {
1557 	int err = 0;
1558 
1559 	switch (num) {
1560 	case MAC_PROP_PRIVATE:
1561 		err = simnet_get_priv_prop_ether(sdev, name, len, val);
1562 		break;
1563 	default:
1564 		err = ENOTSUP;
1565 		break;
1566 	}
1567 
1568 	return (err);
1569 }
1570 
1571 static int
1572 simnet_m_getprop(void *arg, const char *name, const mac_prop_id_t num,
1573     const uint_t len, void *val)
1574 {
1575 	const simnet_dev_t *sdev = arg;
1576 	int err = 0;
1577 
1578 	switch (sdev->sd_type) {
1579 	case DL_ETHER:
1580 		err = simnet_getprop_ether(sdev, name, num, len, val);
1581 		break;
1582 	case DL_WIFI:
1583 		err = simnet_getprop_wifi(sdev, name, num, len, val);
1584 		break;
1585 	default:
1586 		err = EINVAL;
1587 		break;
1588 	}
1589 
1590 	return (err);
1591 }
1592 
1593 static void
1594 simnet_priv_propinfo_wifi(const char *name, mac_prop_info_handle_t prh)
1595 {
1596 	char valstr[MAXNAMELEN];
1597 
1598 	bzero(valstr, sizeof (valstr));
1599 
1600 	if (strcmp(name, "_wl_esslist") == 0) {
1601 		(void) snprintf(valstr, sizeof (valstr), "%d", 0);
1602 	}
1603 
1604 	if (strlen(valstr) > 0)
1605 		mac_prop_info_set_default_str(prh, valstr);
1606 }
1607 
1608 static void
1609 simnet_propinfo_wifi(const char *name, const mac_prop_id_t num,
1610     mac_prop_info_handle_t prh)
1611 {
1612 	switch (num) {
1613 	case MAC_PROP_WL_BSSTYPE:
1614 	case MAC_PROP_WL_ESS_LIST:
1615 	case MAC_PROP_WL_SUPPORTED_RATES:
1616 	case MAC_PROP_WL_RSSI:
1617 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1618 		break;
1619 	case MAC_PROP_PRIVATE:
1620 		simnet_priv_propinfo_wifi(name, prh);
1621 		break;
1622 	}
1623 }
1624 
1625 static void
1626 simnet_priv_propinfo_ether(const char *name, mac_prop_info_handle_t prh)
1627 {
1628 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 ||
1629 	    strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0 ||
1630 	    strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
1631 	    strcmp(name, SD_PROP_LSO) == 0) {
1632 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1633 	}
1634 
1635 	if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1636 		mac_prop_info_set_default_str(prh, "none");
1637 	}
1638 
1639 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 ||
1640 	    strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
1641 	    strcmp(name, SD_PROP_LSO) == 0) {
1642 		mac_prop_info_set_default_str(prh, "off");
1643 	}
1644 }
1645 
1646 static void
1647 simnet_propinfo_ether(const char *name, const mac_prop_id_t num,
1648     mac_prop_info_handle_t prh)
1649 {
1650 	switch (num) {
1651 	case MAC_PROP_PRIVATE:
1652 		simnet_priv_propinfo_ether(name, prh);
1653 		break;
1654 	}
1655 }
1656 
1657 static void
1658 simnet_m_propinfo(void *arg, const char *name, const mac_prop_id_t num,
1659     const mac_prop_info_handle_t prh)
1660 {
1661 	simnet_dev_t *sdev = arg;
1662 
1663 	switch (sdev->sd_type) {
1664 	case DL_ETHER:
1665 		simnet_propinfo_ether(name, num, prh);
1666 		break;
1667 	case DL_WIFI:
1668 		simnet_propinfo_wifi(name, num, prh);
1669 		break;
1670 	}
1671 }
1672