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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Simulated network device (simnet) driver: simulates a pseudo GLDv3 network
28 * device. Can simulate an Ethernet or WiFi network device. In addition, another
29 * simnet instance can be attached as a peer to create a point-to-point link on
33 #include <sys/policy.h>
35 #include <sys/modctl.h>
36 #include <sys/priv_names.h>
38 #include <net/simnet.h>
39 #include <sys/ethernet.h>
42 #include <sys/mac_ether.h>
43 #include <sys/mac_provider.h>
44 #include <sys/mac_client_priv.h>
46 #include <sys/random.h>
47 #include <sys/sysmacros.h>
49 #include <sys/strsubr.h>
50 #include <sys/strsun.h>
51 #include <sys/atomic.h>
52 #include <sys/mac_wifi.h>
53 #include <sys/mac_impl.h>
54 #include <inet/wifi_ioctl.h>
55 #include <sys/thread.h>
56 #include <sys/synch.h>
57 #include <sys/sunddi.h>
59 #include "simnet_impl.h"
61 #define SIMNETINFO "Simulated Network Driver"
63 static dev_info_t
*simnet_dip
;
64 static ddi_taskq_t
*simnet_rxq
;
66 static int simnet_getinfo(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
67 static int simnet_attach(dev_info_t
*, ddi_attach_cmd_t
);
68 static int simnet_detach(dev_info_t
*, ddi_detach_cmd_t
);
69 static int simnet_ioc_create(void *, intptr_t, int, cred_t
*, int *);
70 static int simnet_ioc_delete(void *, intptr_t, int, cred_t
*, int *);
71 static int simnet_ioc_info(void *, intptr_t, int, cred_t
*, int *);
72 static int simnet_ioc_modify(void *, intptr_t, int, cred_t
*, int *);
73 static uint8_t *mcastaddr_lookup(simnet_dev_t
*, const uint8_t *);
75 static dld_ioc_info_t simnet_ioc_list
[] = {
76 {SIMNET_IOC_CREATE
, DLDCOPYINOUT
, sizeof (simnet_ioc_create_t
),
77 simnet_ioc_create
, secpolicy_dl_config
},
78 {SIMNET_IOC_DELETE
, DLDCOPYIN
, sizeof (simnet_ioc_delete_t
),
79 simnet_ioc_delete
, secpolicy_dl_config
},
80 {SIMNET_IOC_INFO
, DLDCOPYINOUT
, sizeof (simnet_ioc_info_t
),
81 simnet_ioc_info
, NULL
},
82 {SIMNET_IOC_MODIFY
, DLDCOPYIN
, sizeof (simnet_ioc_modify_t
),
83 simnet_ioc_modify
, secpolicy_dl_config
}
86 DDI_DEFINE_STREAM_OPS(simnet_dev_ops
, nulldev
, nulldev
, simnet_attach
,
87 simnet_detach
, nodev
, simnet_getinfo
, D_MP
, NULL
,
88 ddi_quiesce_not_supported
);
90 static struct modldrv simnet_modldrv
= {
91 &mod_driverops
, /* Type of module. This one is a driver */
92 SIMNETINFO
, /* short description */
93 &simnet_dev_ops
/* driver specific ops */
96 static struct modlinkage modlinkage
= {
97 MODREV_1
, &simnet_modldrv
, NULL
100 /* MAC callback function declarations */
101 static int simnet_m_start(void *);
102 static void simnet_m_stop(void *);
103 static int simnet_m_promisc(void *, boolean_t
);
104 static int simnet_m_multicst(void *, boolean_t
, const uint8_t *);
105 static int simnet_m_unicst(void *, const uint8_t *);
106 static int simnet_m_stat(void *, uint_t
, uint64_t *);
107 static void simnet_m_ioctl(void *, queue_t
*, mblk_t
*);
108 static mblk_t
*simnet_m_tx(void *, mblk_t
*);
109 static int simnet_m_setprop(void *, const char *, mac_prop_id_t
,
110 uint_t
, const void *);
111 static int simnet_m_getprop(void *, const char *, mac_prop_id_t
,
113 static void simnet_m_propinfo(void *, const char *, mac_prop_id_t
,
114 mac_prop_info_handle_t
);
116 static mac_callbacks_t simnet_m_callbacks
= {
117 (MC_IOCTL
| MC_SETPROP
| MC_GETPROP
| MC_PROPINFO
),
136 * simnet_dev_lock protects the simnet device list.
137 * sd_instlock in each simnet_dev_t protects access to
138 * a single simnet_dev_t.
140 static krwlock_t simnet_dev_lock
;
141 static list_t simnet_dev_list
;
142 static int simnet_count
; /* Num of simnet instances */
149 mac_init_ops(&simnet_dev_ops
, "simnet");
150 status
= mod_install(&modlinkage
);
151 if (status
!= DDI_SUCCESS
)
152 mac_fini_ops(&simnet_dev_ops
);
162 status
= mod_remove(&modlinkage
);
163 if (status
== DDI_SUCCESS
)
164 mac_fini_ops(&simnet_dev_ops
);
170 _info(struct modinfo
*modinfop
)
172 return (mod_info(&modlinkage
, modinfop
));
178 if ((simnet_rxq
= ddi_taskq_create(simnet_dip
, "simnet", 1,
179 TASKQ_DEFAULTPRI
, 0)) == NULL
)
181 rw_init(&simnet_dev_lock
, NULL
, RW_DEFAULT
, NULL
);
182 list_create(&simnet_dev_list
, sizeof (simnet_dev_t
),
183 offsetof(simnet_dev_t
, sd_listnode
));
190 ASSERT(simnet_count
== 0);
191 rw_destroy(&simnet_dev_lock
);
192 list_destroy(&simnet_dev_list
);
193 ddi_taskq_destroy(simnet_rxq
);
198 simnet_getinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
202 case DDI_INFO_DEVT2DEVINFO
:
203 *result
= simnet_dip
;
204 return (DDI_SUCCESS
);
205 case DDI_INFO_DEVT2INSTANCE
:
207 return (DDI_SUCCESS
);
209 return (DDI_FAILURE
);
213 simnet_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
217 if (ddi_get_instance(dip
) != 0) {
218 /* we only allow instance 0 to attach */
219 return (DDI_FAILURE
);
222 if (dld_ioc_register(SIMNET_IOC
, simnet_ioc_list
,
223 DLDIOCCNT(simnet_ioc_list
)) != 0)
224 return (DDI_FAILURE
);
228 return (DDI_FAILURE
);
229 return (DDI_SUCCESS
);
232 return (DDI_SUCCESS
);
235 return (DDI_FAILURE
);
241 simnet_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
246 * Allow the simnet instance to be detached only if there
247 * are no simnets configured.
249 if (simnet_count
> 0)
250 return (DDI_FAILURE
);
252 dld_ioc_unregister(SIMNET_IOC
);
255 return (DDI_SUCCESS
);
258 return (DDI_SUCCESS
);
261 return (DDI_FAILURE
);
265 /* Caller must hold simnet_dev_lock */
266 static simnet_dev_t
*
267 simnet_dev_lookup(datalink_id_t link_id
)
271 ASSERT(RW_LOCK_HELD(&simnet_dev_lock
));
272 for (sdev
= list_head(&simnet_dev_list
); sdev
!= NULL
;
273 sdev
= list_next(&simnet_dev_list
, sdev
)) {
274 if (!(sdev
->sd_flags
& SDF_SHUTDOWN
) &&
275 (sdev
->sd_link_id
== link_id
)) {
276 atomic_inc_32(&sdev
->sd_refcount
);
285 simnet_wifidev_free(simnet_dev_t
*sdev
)
287 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
290 for (i
= 0; i
< wdev
->swd_esslist_num
; i
++) {
291 kmem_free(wdev
->swd_esslist
[i
],
292 sizeof (wl_ess_conf_t
));
294 kmem_free(wdev
, sizeof (simnet_wifidev_t
));
298 simnet_dev_unref(simnet_dev_t
*sdev
)
301 ASSERT(sdev
->sd_refcount
> 0);
302 if (atomic_dec_32_nv(&sdev
->sd_refcount
) != 0)
305 if (sdev
->sd_mh
!= NULL
)
306 (void) mac_unregister(sdev
->sd_mh
);
308 if (sdev
->sd_wifidev
!= NULL
) {
309 ASSERT(sdev
->sd_type
== DL_WIFI
);
310 simnet_wifidev_free(sdev
);
313 mutex_destroy(&sdev
->sd_instlock
);
314 cv_destroy(&sdev
->sd_threadwait
);
315 kmem_free(sdev
->sd_mcastaddrs
, ETHERADDRL
* sdev
->sd_mcastaddr_count
);
316 kmem_free(sdev
, sizeof (*sdev
));
321 simnet_init_wifi(simnet_dev_t
*sdev
, mac_register_t
*mac
)
323 wifi_data_t wd
= { 0 };
326 sdev
->sd_wifidev
= kmem_zalloc(sizeof (simnet_wifidev_t
), KM_NOSLEEP
);
327 if (sdev
->sd_wifidev
== NULL
)
330 sdev
->sd_wifidev
->swd_sdev
= sdev
;
331 sdev
->sd_wifidev
->swd_linkstatus
= WL_NOTCONNECTED
;
332 wd
.wd_secalloc
= WIFI_SEC_NONE
;
333 wd
.wd_opmode
= IEEE80211_M_STA
;
334 mac
->m_type_ident
= MAC_PLUGIN_IDENT_WIFI
;
335 mac
->m_max_sdu
= IEEE80211_MTU
;
337 mac
->m_pdata_size
= sizeof (wd
);
338 err
= mac_register(mac
, &sdev
->sd_mh
);
343 simnet_init_ether(simnet_dev_t
*sdev
, mac_register_t
*mac
)
347 mac
->m_type_ident
= MAC_PLUGIN_IDENT_ETHER
;
348 mac
->m_max_sdu
= SIMNET_MAX_MTU
;
349 mac
->m_margin
= VLAN_TAGSZ
;
350 err
= mac_register(mac
, &sdev
->sd_mh
);
355 simnet_init_mac(simnet_dev_t
*sdev
)
360 if ((mac
= mac_alloc(MAC_VERSION
)) == NULL
)
363 mac
->m_driver
= sdev
;
364 mac
->m_dip
= simnet_dip
;
365 mac
->m_instance
= (uint_t
)-1;
366 mac
->m_src_addr
= sdev
->sd_mac_addr
;
367 mac
->m_callbacks
= &simnet_m_callbacks
;
370 if (sdev
->sd_type
== DL_ETHER
)
371 err
= simnet_init_ether(sdev
, mac
);
372 else if (sdev
->sd_type
== DL_WIFI
)
373 err
= simnet_init_wifi(sdev
, mac
);
383 simnet_ioc_create(void *karg
, intptr_t arg
, int mode
, cred_t
*cred
, int *rvalp
)
385 simnet_ioc_create_t
*create_arg
= karg
;
387 simnet_dev_t
*sdev_tmp
;
390 sdev
= kmem_zalloc(sizeof (*sdev
), KM_NOSLEEP
);
394 rw_enter(&simnet_dev_lock
, RW_WRITER
);
395 if ((sdev_tmp
= simnet_dev_lookup(create_arg
->sic_link_id
)) != NULL
) {
396 simnet_dev_unref(sdev_tmp
);
397 rw_exit(&simnet_dev_lock
);
398 kmem_free(sdev
, sizeof (*sdev
));
402 sdev
->sd_type
= create_arg
->sic_type
;
403 sdev
->sd_link_id
= create_arg
->sic_link_id
;
404 sdev
->sd_zoneid
= crgetzoneid(cred
);
406 mutex_init(&sdev
->sd_instlock
, NULL
, MUTEX_DRIVER
, NULL
);
407 cv_init(&sdev
->sd_threadwait
, NULL
, CV_DRIVER
, NULL
);
410 /* Simnets created from configuration on boot pass saved MAC address */
411 if (create_arg
->sic_mac_len
== 0) {
412 /* Generate random MAC address */
413 (void) random_get_pseudo_bytes(sdev
->sd_mac_addr
, ETHERADDRL
);
414 /* Ensure MAC address is not multicast and is local */
415 sdev
->sd_mac_addr
[0] = (sdev
->sd_mac_addr
[0] & ~1) | 2;
416 sdev
->sd_mac_len
= ETHERADDRL
;
418 (void) memcpy(sdev
->sd_mac_addr
, create_arg
->sic_mac_addr
,
419 create_arg
->sic_mac_len
);
420 sdev
->sd_mac_len
= create_arg
->sic_mac_len
;
423 if ((err
= simnet_init_mac(sdev
)) != 0) {
424 simnet_dev_unref(sdev
);
428 if ((err
= dls_devnet_create(sdev
->sd_mh
, sdev
->sd_link_id
,
429 crgetzoneid(cred
))) != 0) {
430 simnet_dev_unref(sdev
);
434 mac_link_update(sdev
->sd_mh
, LINK_STATE_UP
);
435 mac_tx_update(sdev
->sd_mh
);
436 list_insert_tail(&simnet_dev_list
, sdev
);
438 /* Always return MAC address back to caller */
439 (void) memcpy(create_arg
->sic_mac_addr
, sdev
->sd_mac_addr
,
441 create_arg
->sic_mac_len
= sdev
->sd_mac_len
;
443 rw_exit(&simnet_dev_lock
);
447 /* Caller must hold writer simnet_dev_lock */
449 simnet_remove_peer(simnet_dev_t
*sdev
)
451 simnet_dev_t
*sdev_peer
;
452 datalink_id_t peer_link_id
= DATALINK_INVALID_LINKID
;
454 ASSERT(RW_WRITE_HELD(&simnet_dev_lock
));
455 if ((sdev_peer
= sdev
->sd_peer_dev
) != NULL
) {
456 ASSERT(sdev
== sdev_peer
->sd_peer_dev
);
457 sdev_peer
->sd_peer_dev
= NULL
;
458 sdev
->sd_peer_dev
= NULL
;
459 peer_link_id
= sdev_peer
->sd_link_id
;
460 /* Release previous references held on both simnets */
461 simnet_dev_unref(sdev_peer
);
462 simnet_dev_unref(sdev
);
465 return (peer_link_id
);
470 simnet_ioc_modify(void *karg
, intptr_t arg
, int mode
, cred_t
*cred
, int *rvalp
)
472 simnet_ioc_modify_t
*modify_arg
= karg
;
474 simnet_dev_t
*sdev_peer
= NULL
;
476 rw_enter(&simnet_dev_lock
, RW_WRITER
);
477 if ((sdev
= simnet_dev_lookup(modify_arg
->sim_link_id
)) == NULL
) {
478 rw_exit(&simnet_dev_lock
);
482 if (sdev
->sd_zoneid
!= crgetzoneid(cred
)) {
483 rw_exit(&simnet_dev_lock
);
484 simnet_dev_unref(sdev
);
488 if (sdev
->sd_link_id
== modify_arg
->sim_peer_link_id
) {
489 /* Cannot peer with self */
490 rw_exit(&simnet_dev_lock
);
491 simnet_dev_unref(sdev
);
495 if (sdev
->sd_peer_dev
!= NULL
&& sdev
->sd_peer_dev
->sd_link_id
==
496 modify_arg
->sim_peer_link_id
) {
497 /* Nothing to modify */
498 rw_exit(&simnet_dev_lock
);
499 simnet_dev_unref(sdev
);
503 if (modify_arg
->sim_peer_link_id
!= DATALINK_INVALID_LINKID
) {
504 sdev_peer
= simnet_dev_lookup(modify_arg
->sim_peer_link_id
);
505 if (sdev_peer
== NULL
) {
506 /* Peer simnet device not available */
507 rw_exit(&simnet_dev_lock
);
508 simnet_dev_unref(sdev
);
511 if (sdev_peer
->sd_zoneid
!= sdev
->sd_zoneid
) {
512 /* The two peers must be in the same zone (for now). */
513 rw_exit(&simnet_dev_lock
);
514 simnet_dev_unref(sdev
);
515 simnet_dev_unref(sdev_peer
);
520 /* First remove any previous peer */
521 (void) simnet_remove_peer(sdev
);
523 if (sdev_peer
!= NULL
) {
524 /* Remove any previous peer of sdev_peer */
525 (void) simnet_remove_peer(sdev_peer
);
526 /* Update both devices with the new peer */
527 sdev_peer
->sd_peer_dev
= sdev
;
528 sdev
->sd_peer_dev
= sdev_peer
;
529 /* Hold references on both devices */
531 /* Release sdev lookup reference */
532 simnet_dev_unref(sdev
);
535 rw_exit(&simnet_dev_lock
);
541 simnet_ioc_delete(void *karg
, intptr_t arg
, int mode
, cred_t
*cred
, int *rvalp
)
545 simnet_dev_t
*sdev_peer
;
546 simnet_ioc_delete_t
*delete_arg
= karg
;
548 datalink_id_t peerid
;
550 rw_enter(&simnet_dev_lock
, RW_WRITER
);
551 if ((sdev
= simnet_dev_lookup(delete_arg
->sid_link_id
)) == NULL
) {
552 rw_exit(&simnet_dev_lock
);
556 if (sdev
->sd_zoneid
!= crgetzoneid(cred
)) {
557 rw_exit(&simnet_dev_lock
);
558 simnet_dev_unref(sdev
);
562 if ((err
= dls_devnet_destroy(sdev
->sd_mh
, &tmpid
, B_TRUE
)) != 0) {
563 rw_exit(&simnet_dev_lock
);
564 simnet_dev_unref(sdev
);
568 ASSERT(sdev
->sd_link_id
== tmpid
);
569 /* Remove any attached peer link */
570 peerid
= simnet_remove_peer(sdev
);
572 /* Prevent new threads from using the instance */
573 mutex_enter(&sdev
->sd_instlock
);
574 sdev
->sd_flags
|= SDF_SHUTDOWN
;
575 /* Wait until all active threads using the instance exit */
576 while (sdev
->sd_threadcount
> 0) {
577 if (cv_wait_sig(&sdev
->sd_threadwait
,
578 &sdev
->sd_instlock
) == 0) {
580 mutex_exit(&sdev
->sd_instlock
);
585 mutex_exit(&sdev
->sd_instlock
);
587 /* Try disabling the MAC */
588 if ((err
= mac_disable(sdev
->sd_mh
)) != 0)
591 list_remove(&simnet_dev_list
, sdev
);
592 rw_exit(&simnet_dev_lock
);
593 simnet_dev_unref(sdev
); /* Release lookup ref */
594 /* Releasing the last ref performs sdev/mem free */
595 simnet_dev_unref(sdev
);
598 /* Re-create simnet instance and add any previous peer */
599 (void) dls_devnet_create(sdev
->sd_mh
, sdev
->sd_link_id
,
601 sdev
->sd_flags
&= ~SDF_SHUTDOWN
;
603 ASSERT(sdev
->sd_peer_dev
== NULL
);
604 if (peerid
!= DATALINK_INVALID_LINKID
&&
605 ((sdev_peer
= simnet_dev_lookup(peerid
)) != NULL
)) {
606 /* Attach peer device back */
607 ASSERT(sdev_peer
->sd_peer_dev
== NULL
);
608 sdev_peer
->sd_peer_dev
= sdev
;
609 sdev
->sd_peer_dev
= sdev_peer
;
610 /* Hold reference on both devices */
613 * No previous peer or previous peer no longer
614 * available so release lookup reference.
616 simnet_dev_unref(sdev
);
619 rw_exit(&simnet_dev_lock
);
625 simnet_ioc_info(void *karg
, intptr_t arg
, int mode
, cred_t
*cred
, int *rvalp
)
627 simnet_ioc_info_t
*info_arg
= karg
;
630 /* Make sure that the simnet link is visible from the caller's zone. */
631 if (!dls_devnet_islinkvisible(info_arg
->sii_link_id
, crgetzoneid(cred
)))
634 rw_enter(&simnet_dev_lock
, RW_READER
);
635 if ((sdev
= simnet_dev_lookup(info_arg
->sii_link_id
)) == NULL
) {
636 rw_exit(&simnet_dev_lock
);
640 (void) memcpy(info_arg
->sii_mac_addr
, sdev
->sd_mac_addr
,
642 info_arg
->sii_mac_len
= sdev
->sd_mac_len
;
643 info_arg
->sii_type
= sdev
->sd_type
;
644 if (sdev
->sd_peer_dev
!= NULL
)
645 info_arg
->sii_peer_link_id
= sdev
->sd_peer_dev
->sd_link_id
;
646 rw_exit(&simnet_dev_lock
);
647 simnet_dev_unref(sdev
);
652 simnet_thread_ref(simnet_dev_t
*sdev
)
654 mutex_enter(&sdev
->sd_instlock
);
655 if (sdev
->sd_flags
& SDF_SHUTDOWN
||
656 !(sdev
->sd_flags
& SDF_STARTED
)) {
657 mutex_exit(&sdev
->sd_instlock
);
660 sdev
->sd_threadcount
++;
661 mutex_exit(&sdev
->sd_instlock
);
666 simnet_thread_unref(simnet_dev_t
*sdev
)
668 mutex_enter(&sdev
->sd_instlock
);
669 if (--sdev
->sd_threadcount
== 0)
670 cv_broadcast(&sdev
->sd_threadwait
);
671 mutex_exit(&sdev
->sd_instlock
);
678 mac_header_info_t hdr_info
;
681 sdev
= (simnet_dev_t
*)mp
->b_next
;
684 /* Check for valid packet header */
685 if (mac_header_info(sdev
->sd_mh
, mp
, &hdr_info
) != 0) {
687 sdev
->sd_stats
.recv_errors
++;
692 * When we are NOT in promiscuous mode we only receive
693 * unicast packets addressed to us and multicast packets that
694 * MAC clients have requested.
696 if (!sdev
->sd_promisc
&&
697 hdr_info
.mhi_dsttype
!= MAC_ADDRTYPE_BROADCAST
) {
698 if (hdr_info
.mhi_dsttype
== MAC_ADDRTYPE_UNICAST
&&
699 bcmp(hdr_info
.mhi_daddr
, sdev
->sd_mac_addr
,
703 } else if (hdr_info
.mhi_dsttype
== MAC_ADDRTYPE_MULTICAST
) {
704 mutex_enter(&sdev
->sd_instlock
);
705 if (mcastaddr_lookup(sdev
, hdr_info
.mhi_daddr
) ==
707 mutex_exit(&sdev
->sd_instlock
);
711 mutex_exit(&sdev
->sd_instlock
);
715 sdev
->sd_stats
.recv_count
++;
716 sdev
->sd_stats
.rbytes
+= msgdsize(mp
);
717 mac_rx(sdev
->sd_mh
, NULL
, mp
);
719 simnet_thread_unref(sdev
);
723 simnet_m_tx(void *arg
, mblk_t
*mp_chain
)
725 simnet_dev_t
*sdev
= arg
;
726 simnet_dev_t
*sdev_rx
;
727 mblk_t
*mpnext
= mp_chain
;
730 rw_enter(&simnet_dev_lock
, RW_READER
);
731 if ((sdev_rx
= sdev
->sd_peer_dev
) == NULL
) {
732 /* Discard packets when no peer exists */
733 rw_exit(&simnet_dev_lock
);
734 freemsgchain(mp_chain
);
739 * Discard packets when either device is shutting down or not ready.
740 * Though MAC layer ensures a reference is held on the MAC while we
741 * process the packet chain, there is no guarantee the peer MAC will
742 * remain enabled. So we increment per-instance threadcount to ensure
743 * either MAC instance is not disabled while we handle the chain of
744 * packets. It is okay if the peer device is disconnected while we are
745 * here since we lookup the peer device while holding simnet_dev_lock
746 * (reader lock) and increment the threadcount of the peer, the peer
747 * MAC cannot be disabled in simnet_ioc_delete.
749 if (!simnet_thread_ref(sdev_rx
)) {
750 rw_exit(&simnet_dev_lock
);
751 freemsgchain(mp_chain
);
754 rw_exit(&simnet_dev_lock
);
756 if (!simnet_thread_ref(sdev
)) {
757 simnet_thread_unref(sdev_rx
);
758 freemsgchain(mp_chain
);
762 while ((mp
= mpnext
) != NULL
) {
772 /* Pad packet to minimum Ethernet frame size */
773 if (len
< ETHERMIN
) {
774 size
= ETHERMIN
- len
;
775 mp_new
= allocb(size
, BPRI_HI
);
776 if (mp_new
== NULL
) {
777 sdev
->sd_stats
.xmit_errors
++;
781 bzero(mp_new
->b_wptr
, size
);
782 mp_new
->b_wptr
+= size
;
785 while (mp_tmp
->b_cont
!= NULL
)
786 mp_tmp
= mp_tmp
->b_cont
;
787 mp_tmp
->b_cont
= mp_new
;
791 /* Pullup packet into a single mblk */
792 if (!pullupmsg(mp
, -1)) {
793 sdev
->sd_stats
.xmit_errors
++;
798 /* Fix mblk checksum as the pkt dest is local */
799 if ((mp
= mac_fix_cksum(mp
)) == NULL
) {
800 sdev
->sd_stats
.xmit_errors
++;
804 /* Hold reference for taskq receive processing per-pkt */
805 if (!simnet_thread_ref(sdev_rx
)) {
807 freemsgchain(mpnext
);
811 /* Use taskq for pkt receive to avoid kernel stack explosion */
812 mp
->b_next
= (mblk_t
*)sdev_rx
;
813 if (ddi_taskq_dispatch(simnet_rxq
, simnet_rx
, mp
,
814 DDI_NOSLEEP
) == DDI_SUCCESS
) {
815 sdev
->sd_stats
.xmit_count
++;
816 sdev
->sd_stats
.obytes
+= len
;
818 simnet_thread_unref(sdev_rx
);
821 sdev_rx
->sd_stats
.recv_errors
++;
825 simnet_thread_unref(sdev
);
826 simnet_thread_unref(sdev_rx
);
831 simnet_wifi_ioctl(simnet_dev_t
*sdev
, mblk_t
*mp
)
834 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
836 /* LINTED E_BAD_PTR_CAST_ALIGN */
837 switch (((wldp_t
*)mp
->b_rptr
)->wldp_id
) {
838 case WL_DISASSOCIATE
:
839 wdev
->swd_linkstatus
= WL_NOTCONNECTED
;
848 simnet_m_ioctl(void *arg
, queue_t
*q
, mblk_t
*mp
)
850 simnet_dev_t
*sdev
= arg
;
856 if (sdev
->sd_type
!= DL_WIFI
) {
857 miocnak(q
, mp
, 0, ENOTSUP
);
861 /* LINTED E_BAD_PTR_CAST_ALIGN */
862 iocp
= (struct iocblk
*)mp
->b_rptr
;
863 if (iocp
->ioc_count
== 0) {
864 miocnak(q
, mp
, 0, EINVAL
);
868 /* We only claim support for WiFi operation commands */
872 miocnak(q
, mp
, 0, EINVAL
);
881 freemsg(mp1
->b_cont
);
883 /* overwrite everything */
884 mp1
->b_wptr
= mp1
->b_rptr
;
885 rc
= simnet_wifi_ioctl(sdev
, mp1
);
886 miocack(q
, mp
, msgdsize(mp1
), rc
);
890 simnet_m_stat(void *arg
, uint_t stat
, uint64_t *val
)
893 simnet_dev_t
*sdev
= arg
;
895 ASSERT(sdev
->sd_mh
!= NULL
);
898 case MAC_STAT_IFSPEED
:
899 *val
= 100 * 1000000ull; /* 100 Mbps */
901 case MAC_STAT_LINK_STATE
:
902 *val
= LINK_DUPLEX_FULL
;
904 case MAC_STAT_LINK_UP
:
905 if (sdev
->sd_flags
& SDF_STARTED
)
906 *val
= LINK_STATE_UP
;
908 *val
= LINK_STATE_DOWN
;
910 case MAC_STAT_PROMISC
:
911 case MAC_STAT_MULTIRCV
:
912 case MAC_STAT_MULTIXMT
:
913 case MAC_STAT_BRDCSTRCV
:
914 case MAC_STAT_BRDCSTXMT
:
917 case MAC_STAT_OPACKETS
:
918 *val
= sdev
->sd_stats
.xmit_count
;
920 case MAC_STAT_OBYTES
:
921 *val
= sdev
->sd_stats
.obytes
;
923 case MAC_STAT_IERRORS
:
924 *val
= sdev
->sd_stats
.recv_errors
;
926 case MAC_STAT_OERRORS
:
927 *val
= sdev
->sd_stats
.xmit_errors
;
929 case MAC_STAT_RBYTES
:
930 *val
= sdev
->sd_stats
.rbytes
;
932 case MAC_STAT_IPACKETS
:
933 *val
= sdev
->sd_stats
.recv_count
;
935 case WIFI_STAT_FCS_ERRORS
:
936 case WIFI_STAT_WEP_ERRORS
:
937 case WIFI_STAT_TX_FRAGS
:
938 case WIFI_STAT_MCAST_TX
:
939 case WIFI_STAT_RTS_SUCCESS
:
940 case WIFI_STAT_RTS_FAILURE
:
941 case WIFI_STAT_ACK_FAILURE
:
942 case WIFI_STAT_RX_FRAGS
:
943 case WIFI_STAT_MCAST_RX
:
944 case WIFI_STAT_RX_DUPS
:
956 simnet_m_start(void *arg
)
958 simnet_dev_t
*sdev
= arg
;
960 sdev
->sd_flags
|= SDF_STARTED
;
965 simnet_m_stop(void *arg
)
967 simnet_dev_t
*sdev
= arg
;
969 sdev
->sd_flags
&= ~SDF_STARTED
;
973 simnet_m_promisc(void *arg
, boolean_t on
)
975 simnet_dev_t
*sdev
= arg
;
977 sdev
->sd_promisc
= on
;
982 * Returns matching multicast address enabled on the simnet instance.
983 * Assumes simnet instance mutex lock is held.
986 mcastaddr_lookup(simnet_dev_t
*sdev
, const uint8_t *addrp
)
991 ASSERT(MUTEX_HELD(&sdev
->sd_instlock
));
992 maddrptr
= sdev
->sd_mcastaddrs
;
993 for (idx
= 0; idx
< sdev
->sd_mcastaddr_count
; idx
++) {
994 if (bcmp(maddrptr
, addrp
, ETHERADDRL
) == 0)
996 maddrptr
+= ETHERADDRL
;
1002 /* Add or remove Multicast addresses on simnet instance */
1004 simnet_m_multicst(void *arg
, boolean_t add
, const uint8_t *addrp
)
1006 simnet_dev_t
*sdev
= arg
;
1015 prevsize
= sdev
->sd_mcastaddr_count
* ETHERADDRL
;
1016 newsize
= prevsize
+ (add
? ETHERADDRL
:-ETHERADDRL
);
1017 newbuf
= kmem_alloc(newsize
, KM_SLEEP
);
1019 mutex_enter(&sdev
->sd_instlock
);
1020 if (prevsize
!= (sdev
->sd_mcastaddr_count
* ETHERADDRL
)) {
1021 mutex_exit(&sdev
->sd_instlock
);
1022 kmem_free(newbuf
, newsize
);
1026 maddrptr
= mcastaddr_lookup(sdev
, addrp
);
1027 if (!add
&& maddrptr
!= NULL
) {
1028 /* Removing a Multicast address */
1029 if (newbuf
!= NULL
) {
1030 /* LINTED: E_PTRDIFF_OVERFLOW */
1031 len
= maddrptr
- sdev
->sd_mcastaddrs
;
1032 (void) memcpy(newbuf
, sdev
->sd_mcastaddrs
, len
);
1033 len2
= prevsize
- len
- ETHERADDRL
;
1034 (void) memcpy(newbuf
+ len
,
1035 maddrptr
+ ETHERADDRL
, len2
);
1037 sdev
->sd_mcastaddr_count
--;
1038 } else if (add
&& maddrptr
== NULL
) {
1039 /* Adding a new Multicast address */
1040 (void) memcpy(newbuf
, sdev
->sd_mcastaddrs
, prevsize
);
1041 (void) memcpy(newbuf
+ prevsize
, addrp
, ETHERADDRL
);
1042 sdev
->sd_mcastaddr_count
++;
1044 /* Error: removing a non-existing Multicast address */
1045 mutex_exit(&sdev
->sd_instlock
);
1046 kmem_free(newbuf
, newsize
);
1047 cmn_err(CE_WARN
, "simnet: MAC call to remove a "
1048 "Multicast address failed");
1052 kmem_free(sdev
->sd_mcastaddrs
, prevsize
);
1053 sdev
->sd_mcastaddrs
= newbuf
;
1054 mutex_exit(&sdev
->sd_instlock
);
1059 simnet_m_unicst(void *arg
, const uint8_t *macaddr
)
1061 simnet_dev_t
*sdev
= arg
;
1063 (void) memcpy(sdev
->sd_mac_addr
, macaddr
, ETHERADDRL
);
1067 /* Parse WiFi scan list entry arguments and return the arg count */
1069 parse_esslist_args(const void *pr_val
, uint_t pr_valsize
,
1070 char args
[][MAX_ESSLIST_ARGLEN
])
1073 ptrdiff_t len
= pr_valsize
;
1074 const char *piece
= pr_val
;
1075 const char *end
= (const char *)pr_val
+ pr_valsize
- 1;
1078 while (piece
< end
&& (arg
< MAX_ESSLIST_ARGS
)) {
1079 sep
= strchr(piece
, ',');
1082 /* LINTED E_PTRDIFF_OVERFLOW */
1084 /* If first arg is zero then return none to delete all */
1085 if (arg
== 0 && strnlen(piece
, len
) == 1 && piece
[0] == '0')
1087 if (len
> MAX_ESSLIST_ARGLEN
)
1088 len
= MAX_ESSLIST_ARGLEN
- 1;
1089 (void) memcpy(&args
[arg
][0], piece
, len
);
1090 args
[arg
][len
] = '\0';
1098 /* Set WiFi scan list entry from private property _wl_esslist */
1100 set_wl_esslist_priv_prop(simnet_wifidev_t
*wdev
, uint_t pr_valsize
,
1103 char essargs
[MAX_ESSLIST_ARGS
][MAX_ESSLIST_ARGLEN
];
1108 bzero(essargs
, sizeof (essargs
));
1109 if (parse_esslist_args(pr_val
, pr_valsize
, essargs
) == 0) {
1110 for (i
= 0; i
< wdev
->swd_esslist_num
; i
++) {
1111 kmem_free(wdev
->swd_esslist
[i
], sizeof (wl_ess_conf_t
));
1112 wdev
->swd_esslist
[i
] = NULL
;
1114 wdev
->swd_esslist_num
= 0;
1118 for (i
= 0; i
< wdev
->swd_esslist_num
; i
++) {
1119 wls
= wdev
->swd_esslist
[i
];
1120 if (strcasecmp(wls
->wl_ess_conf_essid
.wl_essid_essid
,
1125 if (wdev
->swd_esslist_num
>= MAX_SIMNET_ESSCONF
)
1128 wls
= kmem_zalloc(sizeof (wl_ess_conf_t
), KM_SLEEP
);
1129 (void) strlcpy(wls
->wl_ess_conf_essid
.wl_essid_essid
,
1130 essargs
[0], sizeof (wls
->wl_ess_conf_essid
.wl_essid_essid
));
1131 wls
->wl_ess_conf_essid
.wl_essid_length
=
1132 strlen(wls
->wl_ess_conf_essid
.wl_essid_essid
);
1133 (void) random_get_pseudo_bytes((uint8_t *)
1134 &wls
->wl_ess_conf_bssid
, sizeof (wl_bssid_t
));
1135 (void) ddi_strtol(essargs
[1], (char **)NULL
, 0, &result
);
1136 wls
->wl_ess_conf_sl
= (wl_rssi_t
)
1137 ((result
> MAX_RSSI
|| result
< 0) ? 0:result
);
1138 wdev
->swd_esslist
[wdev
->swd_esslist_num
] = wls
;
1139 wdev
->swd_esslist_num
++;
1145 simnet_set_priv_prop(simnet_dev_t
*sdev
, const char *pr_name
,
1146 uint_t pr_valsize
, const void *pr_val
)
1148 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
1151 if (strcmp(pr_name
, "_wl_esslist") == 0) {
1154 return (set_wl_esslist_priv_prop(wdev
, pr_valsize
, pr_val
));
1155 } else if (strcmp(pr_name
, "_wl_connected") == 0) {
1158 (void) ddi_strtol(pr_val
, (char **)NULL
, 0, &result
);
1159 wdev
->swd_linkstatus
= ((result
== 1) ?
1160 WL_CONNECTED
:WL_NOTCONNECTED
);
1168 simnet_m_setprop(void *arg
, const char *pr_name
, mac_prop_id_t wldp_pr_num
,
1169 uint_t wldp_length
, const void *wldp_buf
)
1171 simnet_dev_t
*sdev
= arg
;
1172 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
1176 switch (wldp_pr_num
) {
1178 (void) memcpy(&mtu
, wldp_buf
, sizeof (mtu
));
1179 if (mtu
> ETHERMIN
&& mtu
< SIMNET_MAX_MTU
)
1180 return (mac_maxsdu_update(sdev
->sd_mh
, mtu
));
1187 if (sdev
->sd_type
== DL_ETHER
)
1191 switch (wldp_pr_num
) {
1192 case MAC_PROP_WL_ESSID
: {
1196 (void) memcpy(&wdev
->swd_essid
, wldp_buf
,
1197 sizeof (wl_essid_t
));
1198 wdev
->swd_linkstatus
= WL_CONNECTED
;
1200 /* Lookup the signal strength of the connected ESSID */
1201 for (i
= 0; i
< wdev
->swd_esslist_num
; i
++) {
1202 wls
= wdev
->swd_esslist
[i
];
1203 if (strcasecmp(wls
->wl_ess_conf_essid
.wl_essid_essid
,
1204 wdev
->swd_essid
.wl_essid_essid
) == 0) {
1205 wdev
->swd_rssi
= wls
->wl_ess_conf_sl
;
1211 case MAC_PROP_WL_BSSID
: {
1212 (void) memcpy(&wdev
->swd_bssid
, wldp_buf
,
1213 sizeof (wl_bssid_t
));
1216 case MAC_PROP_WL_PHY_CONFIG
:
1217 case MAC_PROP_WL_KEY_TAB
:
1218 case MAC_PROP_WL_AUTH_MODE
:
1219 case MAC_PROP_WL_ENCRYPTION
:
1220 case MAC_PROP_WL_BSSTYPE
:
1221 case MAC_PROP_WL_DESIRED_RATES
:
1223 case MAC_PROP_PRIVATE
:
1224 err
= simnet_set_priv_prop(sdev
, pr_name
,
1225 wldp_length
, wldp_buf
);
1235 simnet_get_priv_prop(simnet_dev_t
*sdev
, const char *pr_name
,
1236 uint_t pr_valsize
, void *pr_val
)
1238 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
1242 if (strcmp(pr_name
, "_wl_esslist") == 0) {
1243 /* Returns num of _wl_ess_conf_t that have been set */
1244 value
= wdev
->swd_esslist_num
;
1245 } else if (strcmp(pr_name
, "_wl_connected") == 0) {
1246 value
= ((wdev
->swd_linkstatus
== WL_CONNECTED
) ? 1:0);
1252 (void) snprintf(pr_val
, pr_valsize
, "%d", value
);
1257 simnet_m_getprop(void *arg
, const char *pr_name
, mac_prop_id_t wldp_pr_num
,
1258 uint_t wldp_length
, void *wldp_buf
)
1260 simnet_dev_t
*sdev
= arg
;
1261 simnet_wifidev_t
*wdev
= sdev
->sd_wifidev
;
1265 if (sdev
->sd_type
== DL_ETHER
)
1269 switch (wldp_pr_num
) {
1270 case MAC_PROP_WL_ESSID
:
1271 (void) memcpy(wldp_buf
, &wdev
->swd_essid
,
1272 sizeof (wl_essid_t
));
1274 case MAC_PROP_WL_BSSID
:
1275 (void) memcpy(wldp_buf
, &wdev
->swd_bssid
,
1276 sizeof (wl_bssid_t
));
1278 case MAC_PROP_WL_PHY_CONFIG
:
1279 case MAC_PROP_WL_AUTH_MODE
:
1280 case MAC_PROP_WL_ENCRYPTION
:
1282 case MAC_PROP_WL_LINKSTATUS
:
1283 (void) memcpy(wldp_buf
, &wdev
->swd_linkstatus
,
1284 sizeof (wdev
->swd_linkstatus
));
1286 case MAC_PROP_WL_ESS_LIST
: {
1287 wl_ess_conf_t
*w_ess_conf
;
1289 ((wl_ess_list_t
*)wldp_buf
)->wl_ess_list_num
=
1290 wdev
->swd_esslist_num
;
1291 /* LINTED E_BAD_PTR_CAST_ALIGN */
1292 w_ess_conf
= (wl_ess_conf_t
*)((char *)wldp_buf
+
1293 offsetof(wl_ess_list_t
, wl_ess_list_ess
));
1294 for (i
= 0; i
< wdev
->swd_esslist_num
; i
++) {
1295 (void) memcpy(w_ess_conf
, wdev
->swd_esslist
[i
],
1296 sizeof (wl_ess_conf_t
));
1301 case MAC_PROP_WL_RSSI
:
1302 *(wl_rssi_t
*)wldp_buf
= wdev
->swd_rssi
;
1304 case MAC_PROP_WL_RADIO
:
1305 *(wl_radio_t
*)wldp_buf
= B_TRUE
;
1307 case MAC_PROP_WL_POWER_MODE
:
1309 case MAC_PROP_WL_DESIRED_RATES
:
1311 case MAC_PROP_PRIVATE
:
1312 err
= simnet_get_priv_prop(sdev
, pr_name
, wldp_length
,
1324 simnet_priv_propinfo(const char *pr_name
, mac_prop_info_handle_t prh
)
1326 char valstr
[MAXNAMELEN
];
1328 bzero(valstr
, sizeof (valstr
));
1330 if (strcmp(pr_name
, "_wl_esslist") == 0) {
1331 (void) snprintf(valstr
, sizeof (valstr
), "%d", 0);
1334 if (strlen(valstr
) > 0)
1335 mac_prop_info_set_default_str(prh
, valstr
);
1339 simnet_m_propinfo(void *arg
, const char *pr_name
, mac_prop_id_t wldp_pr_num
,
1340 mac_prop_info_handle_t prh
)
1342 simnet_dev_t
*sdev
= arg
;
1344 if (sdev
->sd_type
== DL_ETHER
)
1347 switch (wldp_pr_num
) {
1348 case MAC_PROP_WL_BSSTYPE
:
1349 case MAC_PROP_WL_ESS_LIST
:
1350 case MAC_PROP_WL_SUPPORTED_RATES
:
1351 case MAC_PROP_WL_RSSI
:
1352 mac_prop_info_set_perm(prh
, MAC_PROP_PERM_READ
);
1354 case MAC_PROP_PRIVATE
:
1355 simnet_priv_propinfo(pr_name
, prh
);