xref: /illumos-gate/usr/src/uts/common/io/simnet/simnet.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
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_ls = LINK_STATE_UNKNOWN;
407 	sdev->sd_type = create_arg->sic_type;
408 	sdev->sd_link_id = create_arg->sic_link_id;
409 	sdev->sd_zoneid = crgetzoneid(cred);
410 	sdev->sd_refcount++;
411 	mutex_init(&sdev->sd_instlock, NULL, MUTEX_DRIVER, NULL);
412 	cv_init(&sdev->sd_threadwait, NULL, CV_DRIVER, NULL);
413 	simnet_count++;
414 
415 	/* Simnets created from configuration on boot pass saved MAC address */
416 	if (create_arg->sic_mac_len == 0) {
417 		/* Generate random MAC address */
418 		(void) random_get_pseudo_bytes(sdev->sd_mac_addr, ETHERADDRL);
419 		/* Ensure MAC address is not multicast and is local */
420 		sdev->sd_mac_addr[0] = (sdev->sd_mac_addr[0] & ~1) | 2;
421 		sdev->sd_mac_len = ETHERADDRL;
422 	} else {
423 		(void) memcpy(sdev->sd_mac_addr, create_arg->sic_mac_addr,
424 		    create_arg->sic_mac_len);
425 		sdev->sd_mac_len = create_arg->sic_mac_len;
426 	}
427 
428 	if ((err = simnet_init_mac(sdev)) != 0) {
429 		simnet_dev_unref(sdev);
430 		goto exit;
431 	}
432 
433 	if ((err = dls_devnet_create(sdev->sd_mh, sdev->sd_link_id,
434 	    crgetzoneid(cred))) != 0) {
435 		simnet_dev_unref(sdev);
436 		goto exit;
437 	}
438 
439 	sdev->sd_ls = LINK_STATE_UP;
440 	mac_link_update(sdev->sd_mh, LINK_STATE_UP);
441 	mac_tx_update(sdev->sd_mh);
442 	list_insert_tail(&simnet_dev_list, sdev);
443 
444 	/* Always return MAC address back to caller */
445 	(void) memcpy(create_arg->sic_mac_addr, sdev->sd_mac_addr,
446 	    sdev->sd_mac_len);
447 	create_arg->sic_mac_len = sdev->sd_mac_len;
448 exit:
449 	rw_exit(&simnet_dev_lock);
450 	return (err);
451 }
452 
453 /* Caller must hold writer simnet_dev_lock */
454 static datalink_id_t
455 simnet_remove_peer(simnet_dev_t *sdev)
456 {
457 	simnet_dev_t *sdev_peer;
458 	datalink_id_t peer_link_id = DATALINK_INVALID_LINKID;
459 
460 	ASSERT(RW_WRITE_HELD(&simnet_dev_lock));
461 	if ((sdev_peer = sdev->sd_peer_dev) != NULL) {
462 		ASSERT(sdev == sdev_peer->sd_peer_dev);
463 		sdev_peer->sd_peer_dev = NULL;
464 		sdev->sd_peer_dev = NULL;
465 		peer_link_id = sdev_peer->sd_link_id;
466 		/* Release previous references held on both simnets */
467 		simnet_dev_unref(sdev_peer);
468 		simnet_dev_unref(sdev);
469 	}
470 
471 	return (peer_link_id);
472 }
473 
474 /* ARGSUSED */
475 static int
476 simnet_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
477 {
478 	simnet_ioc_modify_t *modify_arg = karg;
479 	simnet_dev_t *sdev;
480 	simnet_dev_t *sdev_peer = NULL;
481 
482 	rw_enter(&simnet_dev_lock, RW_WRITER);
483 	if ((sdev = simnet_dev_lookup(modify_arg->sim_link_id)) == NULL) {
484 		rw_exit(&simnet_dev_lock);
485 		return (ENOENT);
486 	}
487 
488 	if (sdev->sd_zoneid != crgetzoneid(cred)) {
489 		rw_exit(&simnet_dev_lock);
490 		simnet_dev_unref(sdev);
491 		return (ENOENT);
492 	}
493 
494 	if (sdev->sd_link_id == modify_arg->sim_peer_link_id) {
495 		/* Cannot peer with self */
496 		rw_exit(&simnet_dev_lock);
497 		simnet_dev_unref(sdev);
498 		return (EINVAL);
499 	}
500 
501 	if (sdev->sd_peer_dev != NULL && sdev->sd_peer_dev->sd_link_id ==
502 	    modify_arg->sim_peer_link_id) {
503 		/* Nothing to modify */
504 		rw_exit(&simnet_dev_lock);
505 		simnet_dev_unref(sdev);
506 		return (0);
507 	}
508 
509 	if (modify_arg->sim_peer_link_id != DATALINK_INVALID_LINKID) {
510 		sdev_peer = simnet_dev_lookup(modify_arg->sim_peer_link_id);
511 		if (sdev_peer == NULL) {
512 			/* Peer simnet device not available */
513 			rw_exit(&simnet_dev_lock);
514 			simnet_dev_unref(sdev);
515 			return (ENOENT);
516 		}
517 		if (sdev_peer->sd_zoneid != sdev->sd_zoneid) {
518 			/* The two peers must be in the same zone (for now). */
519 			rw_exit(&simnet_dev_lock);
520 			simnet_dev_unref(sdev);
521 			simnet_dev_unref(sdev_peer);
522 			return (EACCES);
523 		}
524 	}
525 
526 	/* First remove any previous peer */
527 	(void) simnet_remove_peer(sdev);
528 
529 	if (sdev_peer != NULL) {
530 		/* Remove any previous peer of sdev_peer */
531 		(void) simnet_remove_peer(sdev_peer);
532 		/* Update both devices with the new peer */
533 		sdev_peer->sd_peer_dev = sdev;
534 		sdev->sd_peer_dev = sdev_peer;
535 		/* Hold references on both devices */
536 	} else {
537 		/* Release sdev lookup reference */
538 		simnet_dev_unref(sdev);
539 	}
540 
541 	rw_exit(&simnet_dev_lock);
542 	return (0);
543 }
544 
545 /* ARGSUSED */
546 static int
547 simnet_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
548 {
549 	int err;
550 	simnet_dev_t *sdev;
551 	simnet_dev_t *sdev_peer;
552 	simnet_ioc_delete_t *delete_arg = karg;
553 	datalink_id_t tmpid;
554 	datalink_id_t peerid;
555 
556 	rw_enter(&simnet_dev_lock, RW_WRITER);
557 	if ((sdev = simnet_dev_lookup(delete_arg->sid_link_id)) == NULL) {
558 		rw_exit(&simnet_dev_lock);
559 		return (ENOENT);
560 	}
561 
562 	if (sdev->sd_zoneid != crgetzoneid(cred)) {
563 		rw_exit(&simnet_dev_lock);
564 		simnet_dev_unref(sdev);
565 		return (ENOENT);
566 	}
567 
568 	if ((err = dls_devnet_destroy(sdev->sd_mh, &tmpid, B_TRUE)) != 0) {
569 		rw_exit(&simnet_dev_lock);
570 		simnet_dev_unref(sdev);
571 		return (err);
572 	}
573 
574 	ASSERT(sdev->sd_link_id == tmpid);
575 	/* Remove any attached peer link */
576 	peerid = simnet_remove_peer(sdev);
577 
578 	/* Prevent new threads from using the instance */
579 	mutex_enter(&sdev->sd_instlock);
580 	sdev->sd_flags |= SDF_SHUTDOWN;
581 	/* Wait until all active threads using the instance exit */
582 	while (sdev->sd_threadcount > 0) {
583 		if (cv_wait_sig(&sdev->sd_threadwait,
584 		    &sdev->sd_instlock) == 0)  {
585 			/* Signaled */
586 			mutex_exit(&sdev->sd_instlock);
587 			err = EINTR;
588 			goto fail;
589 		}
590 	}
591 	mutex_exit(&sdev->sd_instlock);
592 
593 	/* Try disabling the MAC */
594 	if ((err = mac_disable(sdev->sd_mh)) != 0)
595 		goto fail;
596 
597 	list_remove(&simnet_dev_list, sdev);
598 	rw_exit(&simnet_dev_lock);
599 	simnet_dev_unref(sdev); /* Release lookup ref */
600 	/* Releasing the last ref performs sdev/mem free */
601 	simnet_dev_unref(sdev);
602 	return (err);
603 fail:
604 	/* Re-create simnet instance and add any previous peer */
605 	(void) dls_devnet_create(sdev->sd_mh, sdev->sd_link_id,
606 	    crgetzoneid(cred));
607 	sdev->sd_flags &= ~SDF_SHUTDOWN;
608 
609 	ASSERT(sdev->sd_peer_dev == NULL);
610 	if (peerid != DATALINK_INVALID_LINKID &&
611 	    ((sdev_peer = simnet_dev_lookup(peerid)) != NULL)) {
612 		/* Attach peer device back */
613 		ASSERT(sdev_peer->sd_peer_dev == NULL);
614 		sdev_peer->sd_peer_dev = sdev;
615 		sdev->sd_peer_dev = sdev_peer;
616 		/* Hold reference on both devices */
617 	} else {
618 		/*
619 		 * No previous peer or previous peer no longer
620 		 * available so release lookup reference.
621 		 */
622 		simnet_dev_unref(sdev);
623 	}
624 
625 	rw_exit(&simnet_dev_lock);
626 	return (err);
627 }
628 
629 /* ARGSUSED */
630 static int
631 simnet_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
632 {
633 	simnet_ioc_info_t *info_arg = karg;
634 	simnet_dev_t *sdev;
635 
636 	/* Make sure that the simnet link is visible from the caller's zone. */
637 	if (!dls_devnet_islinkvisible(info_arg->sii_link_id, crgetzoneid(cred)))
638 		return (ENOENT);
639 
640 	rw_enter(&simnet_dev_lock, RW_READER);
641 	if ((sdev = simnet_dev_lookup(info_arg->sii_link_id)) == NULL) {
642 		rw_exit(&simnet_dev_lock);
643 		return (ENOENT);
644 	}
645 
646 	(void) memcpy(info_arg->sii_mac_addr, sdev->sd_mac_addr,
647 	    sdev->sd_mac_len);
648 	info_arg->sii_mac_len = sdev->sd_mac_len;
649 	info_arg->sii_type = sdev->sd_type;
650 	if (sdev->sd_peer_dev != NULL)
651 		info_arg->sii_peer_link_id = sdev->sd_peer_dev->sd_link_id;
652 	rw_exit(&simnet_dev_lock);
653 	simnet_dev_unref(sdev);
654 	return (0);
655 }
656 
657 static boolean_t
658 simnet_thread_ref(simnet_dev_t *sdev)
659 {
660 	mutex_enter(&sdev->sd_instlock);
661 	if (sdev->sd_flags & SDF_SHUTDOWN ||
662 	    !(sdev->sd_flags & SDF_STARTED)) {
663 		mutex_exit(&sdev->sd_instlock);
664 		return (B_FALSE);
665 	}
666 	sdev->sd_threadcount++;
667 	mutex_exit(&sdev->sd_instlock);
668 	return (B_TRUE);
669 }
670 
671 static void
672 simnet_thread_unref(simnet_dev_t *sdev)
673 {
674 	mutex_enter(&sdev->sd_instlock);
675 	if (--sdev->sd_threadcount == 0)
676 		cv_broadcast(&sdev->sd_threadwait);
677 	mutex_exit(&sdev->sd_instlock);
678 }
679 
680 /*
681  * TODO: Add properties to set Rx checksum flag behavior.
682  *
683  * o HCK_PARTIALCKSUM.
684  * o HCK_FULLCKSUM_OK.
685  */
686 static void
687 simnet_rx(void *arg)
688 {
689 	mblk_t *mp = arg;
690 	mac_header_info_t hdr_info;
691 	simnet_dev_t *sdev;
692 
693 	sdev = (simnet_dev_t *)mp->b_next;
694 	mp->b_next = NULL;
695 
696 	/* Check for valid packet header */
697 	if (mac_header_info(sdev->sd_mh, mp, &hdr_info) != 0) {
698 		mac_drop_pkt(mp, "invalid L2 header");
699 		sdev->sd_stats.recv_errors++;
700 		goto rx_done;
701 	}
702 
703 	/*
704 	 * When we are NOT in promiscuous mode we only receive
705 	 * unicast packets addressed to us and multicast packets that
706 	 * MAC clients have requested.
707 	 */
708 	if (!sdev->sd_promisc &&
709 	    hdr_info.mhi_dsttype != MAC_ADDRTYPE_BROADCAST) {
710 		if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_UNICAST &&
711 		    bcmp(hdr_info.mhi_daddr, sdev->sd_mac_addr,
712 		    ETHERADDRL) != 0) {
713 			freemsg(mp);
714 			goto rx_done;
715 		} else if (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) {
716 			mutex_enter(&sdev->sd_instlock);
717 			if (mcastaddr_lookup(sdev, hdr_info.mhi_daddr) ==
718 			    NULL) {
719 				mutex_exit(&sdev->sd_instlock);
720 				freemsg(mp);
721 				goto rx_done;
722 			}
723 			mutex_exit(&sdev->sd_instlock);
724 		}
725 	}
726 
727 	/*
728 	 * We don't actually calculate and verify the IP header
729 	 * checksum because the nature of simnet makes it redundant to
730 	 * do so. The point is to test the presence of the flags. The
731 	 * Tx side will have already populated the checksum field.
732 	 */
733 	if ((sdev->sd_rx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
734 		mac_hcksum_set(mp, 0, 0, 0, 0, HCK_IPV4_HDRCKSUM_OK);
735 	}
736 
737 	sdev->sd_stats.recv_count++;
738 	sdev->sd_stats.rbytes += msgdsize(mp);
739 	mac_rx(sdev->sd_mh, NULL, mp);
740 rx_done:
741 	simnet_thread_unref(sdev);
742 }
743 
744 #define	SIMNET_ULP_CKSUM	(HCKSUM_INET_FULL_V4 | HCKSUM_INET_PARTIAL)
745 
746 static mblk_t *
747 simnet_m_tx(void *arg, mblk_t *mp_chain)
748 {
749 	simnet_dev_t *sdev = arg;
750 	simnet_dev_t *sdev_rx;
751 	mblk_t *mpnext = mp_chain;
752 	mblk_t *mp, *nmp;
753 	mac_emul_t emul = 0;
754 
755 	rw_enter(&simnet_dev_lock, RW_READER);
756 	if ((sdev_rx = sdev->sd_peer_dev) == NULL) {
757 		/* Discard packets when no peer exists */
758 		rw_exit(&simnet_dev_lock);
759 		mac_drop_chain(mp_chain, "no peer");
760 		return (NULL);
761 	}
762 
763 	/*
764 	 * Discard packets when either device is shutting down or not ready.
765 	 * Though MAC layer ensures a reference is held on the MAC while we
766 	 * process the packet chain, there is no guarantee the peer MAC will
767 	 * remain enabled. So we increment per-instance threadcount to ensure
768 	 * either MAC instance is not disabled while we handle the chain of
769 	 * packets. It is okay if the peer device is disconnected while we are
770 	 * here since we lookup the peer device while holding simnet_dev_lock
771 	 * (reader lock) and increment the threadcount of the peer, the peer
772 	 * MAC cannot be disabled in simnet_ioc_delete.
773 	 */
774 	if (!simnet_thread_ref(sdev_rx)) {
775 		rw_exit(&simnet_dev_lock);
776 		mac_drop_chain(mp_chain, "simnet peer dev not ready");
777 		return (NULL);
778 	}
779 	rw_exit(&simnet_dev_lock);
780 
781 	if (!simnet_thread_ref(sdev)) {
782 		simnet_thread_unref(sdev_rx);
783 		mac_drop_chain(mp_chain, "simnet dev not ready");
784 		return (NULL);
785 	}
786 
787 	while ((mp = mpnext) != NULL) {
788 		size_t len;
789 		size_t size;
790 		mblk_t *mp_new;
791 		mblk_t *mp_tmp;
792 
793 		mpnext = mp->b_next;
794 		mp->b_next = NULL;
795 		len = msgdsize(mp);
796 
797 		/* Pad packet to minimum Ethernet frame size */
798 		if (len < ETHERMIN) {
799 			size = ETHERMIN - len;
800 			mp_new = allocb(size, BPRI_HI);
801 			if (mp_new == NULL) {
802 				sdev->sd_stats.xmit_errors++;
803 				mac_drop_pkt(mp, "allocb failed");
804 				continue;
805 			}
806 			bzero(mp_new->b_wptr, size);
807 			mp_new->b_wptr += size;
808 
809 			mp_tmp = mp;
810 			while (mp_tmp->b_cont != NULL)
811 				mp_tmp = mp_tmp->b_cont;
812 			mp_tmp->b_cont = mp_new;
813 			len += size;
814 		}
815 
816 		/* Pullup packet into a single mblk */
817 		if ((nmp = msgpullup(mp, -1)) == NULL) {
818 			sdev->sd_stats.xmit_errors++;
819 			mac_drop_pkt(mp, "msgpullup failed");
820 			continue;
821 		} else {
822 			mac_hcksum_clone(mp, nmp);
823 			freemsg(mp);
824 			mp = nmp;
825 		}
826 
827 		/* Hold reference for taskq receive processing per-pkt */
828 		if (!simnet_thread_ref(sdev_rx)) {
829 			mac_drop_pkt(mp, "failed to get thread ref");
830 			mac_drop_chain(mpnext, "failed to get thread ref");
831 			break;
832 		}
833 
834 		if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0)
835 			emul |= MAC_IPCKSUM_EMUL;
836 		if ((sdev->sd_tx_cksum & SIMNET_ULP_CKSUM) != 0)
837 			emul |= MAC_HWCKSUM_EMUL;
838 		if (sdev->sd_lso)
839 			emul |= MAC_LSO_EMUL;
840 
841 		if (emul != 0)
842 			mac_hw_emul(&mp, NULL, NULL, emul);
843 
844 		if (mp == NULL) {
845 			sdev->sd_stats.xmit_errors++;
846 			continue;
847 		}
848 
849 		/*
850 		 * Remember, we are emulating a real NIC here; the
851 		 * checksum flags can't make the trip across the link.
852 		 */
853 		DB_CKSUMFLAGS(mp) = 0;
854 
855 		/* Use taskq for pkt receive to avoid kernel stack explosion */
856 		mp->b_next = (mblk_t *)sdev_rx;
857 		if (ddi_taskq_dispatch(simnet_rxq, simnet_rx, mp,
858 		    DDI_NOSLEEP) == DDI_SUCCESS) {
859 			sdev->sd_stats.xmit_count++;
860 			sdev->sd_stats.obytes += len;
861 		} else {
862 			simnet_thread_unref(sdev_rx);
863 			mp->b_next = NULL;
864 			freemsg(mp);
865 			sdev_rx->sd_stats.recv_errors++;
866 		}
867 	}
868 
869 	simnet_thread_unref(sdev);
870 	simnet_thread_unref(sdev_rx);
871 	return (NULL);
872 }
873 
874 static int
875 simnet_wifi_ioctl(simnet_dev_t *sdev, mblk_t *mp)
876 {
877 	int rc = WL_SUCCESS;
878 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
879 
880 	/* LINTED E_BAD_PTR_CAST_ALIGN */
881 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
882 	case WL_DISASSOCIATE:
883 		wdev->swd_linkstatus = WL_NOTCONNECTED;
884 		break;
885 	default:
886 		break;
887 	}
888 	return (rc);
889 }
890 
891 static void
892 simnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
893 {
894 	simnet_dev_t *sdev = arg;
895 	struct	iocblk	*iocp;
896 	mblk_t	*mp1;
897 	uint32_t cmd;
898 	int rc;
899 
900 	if (sdev->sd_type != DL_WIFI) {
901 		miocnak(q, mp, 0, ENOTSUP);
902 		return;
903 	}
904 
905 	/* LINTED E_BAD_PTR_CAST_ALIGN */
906 	iocp = (struct iocblk *)mp->b_rptr;
907 	if (iocp->ioc_count == 0) {
908 		miocnak(q, mp, 0, EINVAL);
909 		return;
910 	}
911 
912 	/* We only claim support for WiFi operation commands */
913 	cmd = iocp->ioc_cmd;
914 	switch (cmd) {
915 	default:
916 		miocnak(q, mp, 0, EINVAL);
917 		return;
918 	case WLAN_GET_PARAM:
919 	case WLAN_SET_PARAM:
920 	case WLAN_COMMAND:
921 		break;
922 	}
923 
924 	mp1 = mp->b_cont;
925 	freemsg(mp1->b_cont);
926 	mp1->b_cont = NULL;
927 	/* overwrite everything */
928 	mp1->b_wptr = mp1->b_rptr;
929 	rc = simnet_wifi_ioctl(sdev, mp1);
930 	miocack(q, mp, msgdsize(mp1), rc);
931 }
932 
933 static boolean_t
934 simnet_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
935 {
936 	simnet_dev_t *sdev = arg;
937 	const uint_t tcp_cksums = HCKSUM_INET_FULL_V4 | HCKSUM_INET_PARTIAL;
938 
939 	switch (cap) {
940 	case MAC_CAPAB_HCKSUM: {
941 		uint32_t *tx_cksum_flags = cap_data;
942 		*tx_cksum_flags = sdev->sd_tx_cksum;
943 		break;
944 	}
945 	case MAC_CAPAB_LSO: {
946 		mac_capab_lso_t *cap_lso = cap_data;
947 
948 		if (sdev->sd_lso &&
949 		    (sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0 &&
950 		    (sdev->sd_tx_cksum & tcp_cksums) != 0) {
951 			/*
952 			 * The LSO configuration is hardwried for now,
953 			 * but there's no reason we couldn't also make
954 			 * this configurable in the future.
955 			 */
956 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
957 			cap_lso->lso_basic_tcp_ipv4.lso_max = SD_LSO_MAXLEN;
958 			break;
959 		} else {
960 			return (B_FALSE);
961 		}
962 	}
963 	default:
964 		return (B_FALSE);
965 	}
966 
967 	return (B_TRUE);
968 }
969 
970 static int
971 simnet_m_stat(void *arg, uint_t stat, uint64_t *val)
972 {
973 	int rval = 0;
974 	simnet_dev_t *sdev = arg;
975 
976 	ASSERT(sdev->sd_mh != NULL);
977 
978 	switch (stat) {
979 	case MAC_STAT_IFSPEED:
980 		*val = 100 * 1000000ull; /* 100 Mbps */
981 		break;
982 	case MAC_STAT_LINK_STATE:
983 		*val = LINK_DUPLEX_FULL;
984 		break;
985 	case MAC_STAT_LINK_UP:
986 		if (sdev->sd_flags & SDF_STARTED)
987 			*val = LINK_STATE_UP;
988 		else
989 			*val = LINK_STATE_DOWN;
990 		break;
991 	case MAC_STAT_PROMISC:
992 	case MAC_STAT_MULTIRCV:
993 	case MAC_STAT_MULTIXMT:
994 	case MAC_STAT_BRDCSTRCV:
995 	case MAC_STAT_BRDCSTXMT:
996 		rval = ENOTSUP;
997 		break;
998 	case MAC_STAT_OPACKETS:
999 		*val = sdev->sd_stats.xmit_count;
1000 		break;
1001 	case MAC_STAT_OBYTES:
1002 		*val = sdev->sd_stats.obytes;
1003 		break;
1004 	case MAC_STAT_IERRORS:
1005 		*val = sdev->sd_stats.recv_errors;
1006 		break;
1007 	case MAC_STAT_OERRORS:
1008 		*val = sdev->sd_stats.xmit_errors;
1009 		break;
1010 	case MAC_STAT_RBYTES:
1011 		*val = sdev->sd_stats.rbytes;
1012 		break;
1013 	case MAC_STAT_IPACKETS:
1014 		*val = sdev->sd_stats.recv_count;
1015 		break;
1016 	case WIFI_STAT_FCS_ERRORS:
1017 	case WIFI_STAT_WEP_ERRORS:
1018 	case WIFI_STAT_TX_FRAGS:
1019 	case WIFI_STAT_MCAST_TX:
1020 	case WIFI_STAT_RTS_SUCCESS:
1021 	case WIFI_STAT_RTS_FAILURE:
1022 	case WIFI_STAT_ACK_FAILURE:
1023 	case WIFI_STAT_RX_FRAGS:
1024 	case WIFI_STAT_MCAST_RX:
1025 	case WIFI_STAT_RX_DUPS:
1026 		rval = ENOTSUP;
1027 		break;
1028 	default:
1029 		rval = ENOTSUP;
1030 		break;
1031 	}
1032 
1033 	return (rval);
1034 }
1035 
1036 static int
1037 simnet_m_start(void *arg)
1038 {
1039 	simnet_dev_t *sdev = arg;
1040 
1041 	sdev->sd_flags |= SDF_STARTED;
1042 	return (0);
1043 }
1044 
1045 static void
1046 simnet_m_stop(void *arg)
1047 {
1048 	simnet_dev_t *sdev = arg;
1049 
1050 	sdev->sd_flags &= ~SDF_STARTED;
1051 }
1052 
1053 static int
1054 simnet_m_promisc(void *arg, boolean_t on)
1055 {
1056 	simnet_dev_t *sdev = arg;
1057 
1058 	sdev->sd_promisc = on;
1059 	return (0);
1060 }
1061 
1062 /*
1063  * Returns matching multicast address enabled on the simnet instance.
1064  * Assumes simnet instance mutex lock is held.
1065  */
1066 static const struct ether_addr *
1067 mcastaddr_lookup(const simnet_dev_t *sdev, const uint8_t *addrp)
1068 {
1069 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1070 	for (uint_t i = 0; i < sdev->sd_mcastaddr_count; i++) {
1071 		const struct ether_addr *maddrp = &sdev->sd_mcastaddrs[i];
1072 
1073 		if (bcmp(maddrp->ether_addr_octet, addrp,
1074 		    sizeof (maddrp->ether_addr_octet)) == 0) {
1075 			return (maddrp);
1076 		}
1077 	}
1078 
1079 	return (NULL);
1080 }
1081 
1082 static int
1083 simnet_multicst_add(simnet_dev_t *sdev, const struct ether_addr *eap)
1084 {
1085 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1086 
1087 	if ((eap->ether_addr_octet[0] & 01) == 0) {
1088 		return (EINVAL);
1089 	}
1090 
1091 	if (sdev->sd_mcastaddr_count == SM_MAX_NUM_MCAST_ADDRS) {
1092 		return (ENOSPC);
1093 	}
1094 
1095 	bcopy(eap, &sdev->sd_mcastaddrs[sdev->sd_mcastaddr_count],
1096 	    sizeof (*eap));
1097 	sdev->sd_mcastaddr_count++;
1098 	return (0);
1099 }
1100 
1101 static int
1102 simnet_multicst_rm(simnet_dev_t *sdev, const struct ether_addr *eap)
1103 {
1104 	ASSERT(MUTEX_HELD(&sdev->sd_instlock));
1105 
1106 	for (uint_t i = 0; i < sdev->sd_mcastaddr_count; i++) {
1107 		if (bcmp(eap, &sdev->sd_mcastaddrs[i], sizeof (*eap)) == 0) {
1108 			for (i++; i < sdev->sd_mcastaddr_count; i++) {
1109 				sdev->sd_mcastaddrs[i - 1] =
1110 				    sdev->sd_mcastaddrs[i];
1111 			}
1112 
1113 			/* Zero-out the last entry as it is no longer valid. */
1114 			bzero(&sdev->sd_mcastaddrs[i - 1],
1115 			    sizeof (sdev->sd_mcastaddrs[0]));
1116 
1117 			sdev->sd_mcastaddr_count--;
1118 			return (0);
1119 		}
1120 	}
1121 
1122 	return (EINVAL);
1123 }
1124 
1125 /* Add or remove Multicast addresses on simnet instance */
1126 static int
1127 simnet_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
1128 {
1129 	simnet_dev_t *sdev = arg;
1130 	struct ether_addr ea;
1131 	int ret;
1132 
1133 	bcopy(addrp, ea.ether_addr_octet, sizeof (ea.ether_addr_octet));
1134 	mutex_enter(&sdev->sd_instlock);
1135 
1136 	if (add) {
1137 		ret = simnet_multicst_add(sdev, &ea);
1138 	} else {
1139 		ret = simnet_multicst_rm(sdev, &ea);
1140 	}
1141 
1142 	ASSERT3U(sdev->sd_mcastaddr_count, <=, SM_MAX_NUM_MCAST_ADDRS);
1143 	mutex_exit(&sdev->sd_instlock);
1144 	return (ret);
1145 }
1146 
1147 static int
1148 simnet_m_unicst(void *arg, const uint8_t *macaddr)
1149 {
1150 	simnet_dev_t *sdev = arg;
1151 
1152 	(void) memcpy(sdev->sd_mac_addr, macaddr, ETHERADDRL);
1153 	return (0);
1154 }
1155 
1156 /* Parse WiFi scan list entry arguments and return the arg count */
1157 static int
1158 parse_esslist_args(const void *pr_val, uint_t pr_valsize,
1159     char args[][MAX_ESSLIST_ARGLEN])
1160 {
1161 	char *sep;
1162 	ptrdiff_t len = pr_valsize;
1163 	const char *piece = pr_val;
1164 	const char *end = (const char *)pr_val + pr_valsize - 1;
1165 	int arg = 0;
1166 
1167 	while (piece < end && (arg < MAX_ESSLIST_ARGS)) {
1168 		sep = strchr(piece, ',');
1169 		if (sep == NULL)
1170 			sep = (char *)end;
1171 		/* LINTED E_PTRDIFF_OVERFLOW */
1172 		len = sep - piece;
1173 		/* If first arg is zero then return none to delete all */
1174 		if (arg == 0 && strnlen(piece, len) == 1 && piece[0] == '0')
1175 			return (0);
1176 		if (len > MAX_ESSLIST_ARGLEN)
1177 			len = MAX_ESSLIST_ARGLEN - 1;
1178 		(void) memcpy(&args[arg][0], piece, len);
1179 		args[arg][len] = '\0';
1180 		piece = sep + 1;
1181 		arg++;
1182 	}
1183 
1184 	return (arg);
1185 }
1186 
1187 /* Set WiFi scan list entry from private property _wl_esslist */
1188 static int
1189 set_wl_esslist_priv_prop(simnet_wifidev_t *wdev, uint_t pr_valsize,
1190     const void *pr_val)
1191 {
1192 	char essargs[MAX_ESSLIST_ARGS][MAX_ESSLIST_ARGLEN];
1193 	wl_ess_conf_t *wls;
1194 	long result;
1195 	int i;
1196 
1197 	bzero(essargs, sizeof (essargs));
1198 	if (parse_esslist_args(pr_val, pr_valsize, essargs) == 0) {
1199 		for (i = 0; i < wdev->swd_esslist_num; i++) {
1200 			kmem_free(wdev->swd_esslist[i], sizeof (wl_ess_conf_t));
1201 			wdev->swd_esslist[i] = NULL;
1202 		}
1203 		wdev->swd_esslist_num = 0;
1204 		return (0);
1205 	}
1206 
1207 	for (i = 0; i < wdev->swd_esslist_num; i++) {
1208 		wls = wdev->swd_esslist[i];
1209 		if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
1210 		    essargs[0]) == 0)
1211 			return (EEXIST);
1212 	}
1213 
1214 	if (wdev->swd_esslist_num >= MAX_SIMNET_ESSCONF)
1215 		return (EINVAL);
1216 
1217 	wls = kmem_zalloc(sizeof (wl_ess_conf_t), KM_SLEEP);
1218 	(void) strlcpy(wls->wl_ess_conf_essid.wl_essid_essid,
1219 	    essargs[0], sizeof (wls->wl_ess_conf_essid.wl_essid_essid));
1220 	wls->wl_ess_conf_essid.wl_essid_length =
1221 	    strlen(wls->wl_ess_conf_essid.wl_essid_essid);
1222 	(void) random_get_pseudo_bytes((uint8_t *)
1223 	    &wls->wl_ess_conf_bssid, sizeof (wl_bssid_t));
1224 	(void) ddi_strtol(essargs[1], (char **)NULL, 0, &result);
1225 	wls->wl_ess_conf_sl = (wl_rssi_t)
1226 	    ((result > MAX_RSSI || result < 0) ? 0:result);
1227 	wdev->swd_esslist[wdev->swd_esslist_num] = wls;
1228 	wdev->swd_esslist_num++;
1229 
1230 	return (0);
1231 }
1232 
1233 static int
1234 simnet_set_priv_prop_wifi(simnet_dev_t *sdev, const char *name,
1235     const uint_t len, const void *val)
1236 {
1237 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1238 	long result;
1239 
1240 	if (strcmp(name, "_wl_esslist") == 0) {
1241 		if (val == NULL)
1242 			return (EINVAL);
1243 		return (set_wl_esslist_priv_prop(wdev, len, val));
1244 	} else if (strcmp(name, "_wl_connected") == 0) {
1245 		if (val == NULL)
1246 			return (EINVAL);
1247 		(void) ddi_strtol(val, (char **)NULL, 0, &result);
1248 		wdev->swd_linkstatus = ((result == 1) ?
1249 		    WL_CONNECTED:WL_NOTCONNECTED);
1250 		return (0);
1251 	}
1252 
1253 	return (EINVAL);
1254 }
1255 
1256 /* ARGSUSED */
1257 static int
1258 simnet_set_priv_prop_ether(simnet_dev_t *sdev, const char *name,
1259     const uint_t len, const void *val)
1260 {
1261 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) {
1262 		if (val == NULL)
1263 			return (EINVAL);
1264 
1265 		if (strcmp(val, "off") == 0) {
1266 			sdev->sd_rx_cksum &= ~HCKSUM_IPHDRCKSUM;
1267 		} else if (strcmp(val, "on") == 0) {
1268 			sdev->sd_rx_cksum |= HCKSUM_IPHDRCKSUM;
1269 		} else {
1270 			return (EINVAL);
1271 		}
1272 
1273 		return (0);
1274 	} else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1275 		if (val == NULL)
1276 			return (EINVAL);
1277 
1278 		/*
1279 		 * Remember, full and partial checksum are mutually
1280 		 * exclusive.
1281 		 */
1282 		if (strcmp(val, "none") == 0) {
1283 			sdev->sd_tx_cksum &= ~HCKSUM_INET_FULL_V4;
1284 		} else if (strcmp(val, "fullv4") == 0) {
1285 			sdev->sd_tx_cksum &= ~HCKSUM_INET_PARTIAL;
1286 			sdev->sd_tx_cksum |= HCKSUM_INET_FULL_V4;
1287 		} else if (strcmp(val, "partial") == 0) {
1288 			sdev->sd_tx_cksum &= HCKSUM_INET_FULL_V4;
1289 			sdev->sd_tx_cksum |= HCKSUM_INET_PARTIAL;
1290 		} else {
1291 			return (EINVAL);
1292 		}
1293 
1294 		return (0);
1295 	} else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
1296 		if (val == NULL)
1297 			return (EINVAL);
1298 
1299 		if (strcmp(val, "off") == 0) {
1300 			sdev->sd_tx_cksum &= ~HCKSUM_IPHDRCKSUM;
1301 		} else if (strcmp(val, "on") == 0) {
1302 			sdev->sd_tx_cksum |= HCKSUM_IPHDRCKSUM;
1303 		} else {
1304 			return (EINVAL);
1305 		}
1306 
1307 		return (0);
1308 	} else if (strcmp(name, SD_PROP_LSO) == 0) {
1309 		if (val == NULL)
1310 			return (EINVAL);
1311 
1312 		if (strcmp(val, "off") == 0) {
1313 			sdev->sd_lso = B_FALSE;
1314 		} else if (strcmp(val, "on") == 0) {
1315 			sdev->sd_lso = B_TRUE;
1316 		} else {
1317 			return (EINVAL);
1318 		}
1319 
1320 		return (0);
1321 	} else if (strcmp(name, SD_PROP_LINKSTATE) == 0) {
1322 		if (val == NULL)
1323 			return (EINVAL);
1324 
1325 		if (strcmp(val, "up") == 0) {
1326 			sdev->sd_ls = LINK_STATE_UP;
1327 		} else if (strcmp(val, "down") == 0) {
1328 			sdev->sd_ls = LINK_STATE_DOWN;
1329 		} else if (strcmp(val, "unknown") == 0) {
1330 			sdev->sd_ls = LINK_STATE_UNKNOWN;
1331 		} else {
1332 			return (EINVAL);
1333 		}
1334 		mac_link_update(sdev->sd_mh, sdev->sd_ls);
1335 
1336 		return (0);
1337 	}
1338 
1339 	return (ENOTSUP);
1340 }
1341 
1342 static int
1343 simnet_setprop_wifi(simnet_dev_t *sdev, const char *name,
1344     const mac_prop_id_t num, const uint_t len, const void *val)
1345 {
1346 	int err = 0;
1347 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1348 
1349 	switch (num) {
1350 	case MAC_PROP_WL_ESSID: {
1351 		int i;
1352 		wl_ess_conf_t *wls;
1353 
1354 		(void) memcpy(&wdev->swd_essid, val, sizeof (wl_essid_t));
1355 		wdev->swd_linkstatus = WL_CONNECTED;
1356 
1357 		/* Lookup the signal strength of the connected ESSID */
1358 		for (i = 0; i < wdev->swd_esslist_num; i++) {
1359 			wls = wdev->swd_esslist[i];
1360 			if (strcasecmp(wls->wl_ess_conf_essid.wl_essid_essid,
1361 			    wdev->swd_essid.wl_essid_essid) == 0) {
1362 				wdev->swd_rssi = wls->wl_ess_conf_sl;
1363 				break;
1364 			}
1365 		}
1366 		break;
1367 	}
1368 	case MAC_PROP_WL_BSSID: {
1369 		(void) memcpy(&wdev->swd_bssid, val, sizeof (wl_bssid_t));
1370 		break;
1371 	}
1372 	case MAC_PROP_WL_PHY_CONFIG:
1373 	case MAC_PROP_WL_KEY_TAB:
1374 	case MAC_PROP_WL_AUTH_MODE:
1375 	case MAC_PROP_WL_ENCRYPTION:
1376 	case MAC_PROP_WL_BSSTYPE:
1377 	case MAC_PROP_WL_DESIRED_RATES:
1378 		break;
1379 	case MAC_PROP_PRIVATE:
1380 		err = simnet_set_priv_prop_wifi(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_setprop_ether(simnet_dev_t *sdev, const char *name,
1392     const mac_prop_id_t num, const uint_t len, const void *val)
1393 {
1394 	int err = 0;
1395 
1396 	switch (num) {
1397 	case MAC_PROP_PRIVATE:
1398 		err = simnet_set_priv_prop_ether(sdev, name, len, val);
1399 		break;
1400 	default:
1401 		err = EINVAL;
1402 		break;
1403 	}
1404 
1405 	return (err);
1406 }
1407 
1408 static int
1409 simnet_m_setprop(void *arg, const char *name, mac_prop_id_t num,
1410     const uint_t len, const void *val)
1411 {
1412 	simnet_dev_t *sdev = arg;
1413 	int err = 0;
1414 	uint32_t mtu;
1415 
1416 	switch (num) {
1417 	case MAC_PROP_MTU:
1418 		(void) memcpy(&mtu, val, sizeof (mtu));
1419 		if (mtu > ETHERMIN && mtu < SIMNET_MAX_MTU)
1420 			return (mac_maxsdu_update(sdev->sd_mh, mtu));
1421 		else
1422 			return (EINVAL);
1423 	default:
1424 		break;
1425 	}
1426 
1427 	switch (sdev->sd_type) {
1428 	case DL_ETHER:
1429 		err = simnet_setprop_ether(sdev, name, num, len, val);
1430 		break;
1431 	case DL_WIFI:
1432 		err = simnet_setprop_wifi(sdev, name, num, len, val);
1433 		break;
1434 	default:
1435 		err = EINVAL;
1436 		break;
1437 	}
1438 
1439 	/*
1440 	 * We may have modified the configuration of hardware
1441 	 * offloads. Make sure to renegotiate capabilities with the
1442 	 * upstream clients.
1443 	 */
1444 	mac_capab_update(sdev->sd_mh);
1445 	return (err);
1446 }
1447 
1448 static int
1449 simnet_get_priv_prop_wifi(const simnet_dev_t *sdev, const char *name,
1450     const uint_t len, void *val)
1451 {
1452 	simnet_wifidev_t *wdev = sdev->sd_wifidev;
1453 	int ret, value;
1454 
1455 	if (strcmp(name, "_wl_esslist") == 0) {
1456 		/* Returns num of _wl_ess_conf_t that have been set */
1457 		value = wdev->swd_esslist_num;
1458 	} else if (strcmp(name, "_wl_connected") == 0) {
1459 		value = ((wdev->swd_linkstatus == WL_CONNECTED) ? 1:0);
1460 	} else {
1461 		return (ENOTSUP);
1462 	}
1463 
1464 	ret = snprintf(val, len, "%d", value);
1465 
1466 	if (ret < 0 || ret >= len)
1467 		return (EOVERFLOW);
1468 
1469 	return (0);
1470 }
1471 
1472 static int
1473 simnet_get_priv_prop_ether(const simnet_dev_t *sdev, const char *name,
1474     const uint_t len, void *val)
1475 {
1476 	int ret;
1477 	char *value;
1478 
1479 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0) {
1480 		if ((sdev->sd_rx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
1481 			value = "on";
1482 		} else {
1483 			value = "off";
1484 		}
1485 	} else if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1486 		if ((sdev->sd_tx_cksum & HCKSUM_INET_FULL_V4) != 0) {
1487 			value = "fullv4";
1488 		} else if ((sdev->sd_tx_cksum & HCKSUM_INET_PARTIAL) != 0) {
1489 			value = "partial";
1490 		} else {
1491 			value = "none";
1492 		}
1493 	} else if (strcmp(name, SD_PROP_TX_IP_CKSUM) == 0) {
1494 		if ((sdev->sd_tx_cksum & HCKSUM_IPHDRCKSUM) != 0) {
1495 			value = "on";
1496 		} else {
1497 			value = "off";
1498 		}
1499 	} else if (strcmp(name, SD_PROP_LSO) == 0) {
1500 		value = sdev->sd_lso ? "on" : "off";
1501 	} else if (strcmp(name, SD_PROP_LINKSTATE) == 0) {
1502 		if (sdev->sd_ls == LINK_STATE_UP) {
1503 			value = "up";
1504 		} else if (sdev->sd_ls == LINK_STATE_DOWN) {
1505 			value = "down";
1506 		} else {
1507 			value = "unknown";
1508 		}
1509 	} else {
1510 		return (ENOTSUP);
1511 	}
1512 
1513 	ret = snprintf(val, len, "%s", value);
1514 
1515 	if (ret < 0 || ret >= len) {
1516 		return (EOVERFLOW);
1517 	}
1518 
1519 	return (0);
1520 }
1521 
1522 static int
1523 simnet_getprop_wifi(const simnet_dev_t *sdev, const char *name,
1524     const mac_prop_id_t num, const uint_t len, void *val)
1525 {
1526 	const simnet_wifidev_t *wdev = sdev->sd_wifidev;
1527 	int err = 0;
1528 
1529 	switch (num) {
1530 	case MAC_PROP_WL_ESSID:
1531 		(void) memcpy(val, &wdev->swd_essid, sizeof (wl_essid_t));
1532 		break;
1533 	case MAC_PROP_WL_BSSID:
1534 		(void) memcpy(val, &wdev->swd_bssid, sizeof (wl_bssid_t));
1535 		break;
1536 	case MAC_PROP_WL_PHY_CONFIG:
1537 	case MAC_PROP_WL_AUTH_MODE:
1538 	case MAC_PROP_WL_ENCRYPTION:
1539 		break;
1540 	case MAC_PROP_WL_LINKSTATUS:
1541 		(void) memcpy(val, &wdev->swd_linkstatus,
1542 		    sizeof (wdev->swd_linkstatus));
1543 		break;
1544 	case MAC_PROP_WL_ESS_LIST: {
1545 		wl_ess_conf_t *w_ess_conf;
1546 
1547 		((wl_ess_list_t *)val)->wl_ess_list_num = wdev->swd_esslist_num;
1548 		/* LINTED E_BAD_PTR_CAST_ALIGN */
1549 		w_ess_conf = (wl_ess_conf_t *)((char *)val +
1550 		    offsetof(wl_ess_list_t, wl_ess_list_ess));
1551 		for (uint_t i = 0; i < wdev->swd_esslist_num; i++) {
1552 			(void) memcpy(w_ess_conf, wdev->swd_esslist[i],
1553 			    sizeof (wl_ess_conf_t));
1554 			w_ess_conf++;
1555 		}
1556 		break;
1557 	}
1558 	case MAC_PROP_WL_RSSI:
1559 		*(wl_rssi_t *)val = wdev->swd_rssi;
1560 		break;
1561 	case MAC_PROP_WL_RADIO:
1562 		*(wl_radio_t *)val = B_TRUE;
1563 		break;
1564 	case MAC_PROP_WL_POWER_MODE:
1565 		break;
1566 	case MAC_PROP_WL_DESIRED_RATES:
1567 		break;
1568 	case MAC_PROP_PRIVATE:
1569 		err = simnet_get_priv_prop_wifi(sdev, name, len, val);
1570 		break;
1571 	default:
1572 		err = ENOTSUP;
1573 		break;
1574 	}
1575 
1576 	return (err);
1577 }
1578 
1579 static int
1580 simnet_getprop_ether(const simnet_dev_t *sdev, const char *name,
1581     const mac_prop_id_t num, const uint_t len, void *val)
1582 {
1583 	int err = 0;
1584 
1585 	switch (num) {
1586 	case MAC_PROP_PRIVATE:
1587 		err = simnet_get_priv_prop_ether(sdev, name, len, val);
1588 		break;
1589 	default:
1590 		err = ENOTSUP;
1591 		break;
1592 	}
1593 
1594 	return (err);
1595 }
1596 
1597 static int
1598 simnet_m_getprop(void *arg, const char *name, const mac_prop_id_t num,
1599     const uint_t len, void *val)
1600 {
1601 	const simnet_dev_t *sdev = arg;
1602 	int err = 0;
1603 
1604 	switch (sdev->sd_type) {
1605 	case DL_ETHER:
1606 		err = simnet_getprop_ether(sdev, name, num, len, val);
1607 		break;
1608 	case DL_WIFI:
1609 		err = simnet_getprop_wifi(sdev, name, num, len, val);
1610 		break;
1611 	default:
1612 		err = EINVAL;
1613 		break;
1614 	}
1615 
1616 	return (err);
1617 }
1618 
1619 static void
1620 simnet_priv_propinfo_wifi(const char *name, mac_prop_info_handle_t prh)
1621 {
1622 	char valstr[MAXNAMELEN];
1623 
1624 	bzero(valstr, sizeof (valstr));
1625 
1626 	if (strcmp(name, "_wl_esslist") == 0) {
1627 		(void) snprintf(valstr, sizeof (valstr), "%d", 0);
1628 	}
1629 
1630 	if (strlen(valstr) > 0)
1631 		mac_prop_info_set_default_str(prh, valstr);
1632 }
1633 
1634 static void
1635 simnet_propinfo_wifi(const char *name, const mac_prop_id_t num,
1636     mac_prop_info_handle_t prh)
1637 {
1638 	switch (num) {
1639 	case MAC_PROP_WL_BSSTYPE:
1640 	case MAC_PROP_WL_ESS_LIST:
1641 	case MAC_PROP_WL_SUPPORTED_RATES:
1642 	case MAC_PROP_WL_RSSI:
1643 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1644 		break;
1645 	case MAC_PROP_PRIVATE:
1646 		simnet_priv_propinfo_wifi(name, prh);
1647 		break;
1648 	}
1649 }
1650 
1651 static void
1652 simnet_priv_propinfo_ether(const char *name, mac_prop_info_handle_t prh)
1653 {
1654 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 ||
1655 	    strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0 ||
1656 	    strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
1657 	    strcmp(name, SD_PROP_LSO) == 0) {
1658 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1659 	}
1660 
1661 	if (strcmp(name, SD_PROP_TX_ULP_CKSUM) == 0) {
1662 		mac_prop_info_set_default_str(prh, "none");
1663 	}
1664 
1665 	if (strcmp(name, SD_PROP_RX_IP_CKSUM) == 0 ||
1666 	    strcmp(name, SD_PROP_TX_IP_CKSUM) == 0 ||
1667 	    strcmp(name, SD_PROP_LSO) == 0) {
1668 		mac_prop_info_set_default_str(prh, "off");
1669 	}
1670 	if (strcmp(name, SD_PROP_LINKSTATE) == 0) {
1671 		mac_prop_info_set_default_str(prh, "unknown");
1672 	}
1673 }
1674 
1675 static void
1676 simnet_propinfo_ether(const char *name, const mac_prop_id_t num,
1677     mac_prop_info_handle_t prh)
1678 {
1679 	switch (num) {
1680 	case MAC_PROP_PRIVATE:
1681 		simnet_priv_propinfo_ether(name, prh);
1682 		break;
1683 	}
1684 }
1685 
1686 static void
1687 simnet_m_propinfo(void *arg, const char *name, const mac_prop_id_t num,
1688     const mac_prop_info_handle_t prh)
1689 {
1690 	simnet_dev_t *sdev = arg;
1691 
1692 	switch (sdev->sd_type) {
1693 	case DL_ETHER:
1694 		simnet_propinfo_ether(name, num, prh);
1695 		break;
1696 	case DL_WIFI:
1697 		simnet_propinfo_wifi(name, num, prh);
1698 		break;
1699 	}
1700 }
1701