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