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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)175 _info(struct modinfo *modinfop)
176 {
177 return (mod_info(&modlinkage, modinfop));
178 }
179
180 static boolean_t
simnet_init(void)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
simnet_fini(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
simnet_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
simnet_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
simnet_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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 *
simnet_dev_lookup(datalink_id_t link_id)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
simnet_wifidev_free(simnet_dev_t * sdev)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
simnet_dev_unref(simnet_dev_t * sdev)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
simnet_init_wifi(simnet_dev_t * sdev,mac_register_t * mac)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
simnet_init_ether(simnet_dev_t * sdev,mac_register_t * mac)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
simnet_init_mac(simnet_dev_t * sdev)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
simnet_ioc_create(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
simnet_remove_peer(simnet_dev_t * sdev)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
simnet_ioc_modify(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
simnet_ioc_delete(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
simnet_ioc_info(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)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
simnet_thread_ref(simnet_dev_t * sdev)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
simnet_thread_unref(simnet_dev_t * sdev)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
simnet_rx(void * arg)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 *
simnet_m_tx(void * arg,mblk_t * mp_chain)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
simnet_wifi_ioctl(simnet_dev_t * sdev,mblk_t * mp)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
simnet_m_ioctl(void * arg,queue_t * q,mblk_t * mp)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
simnet_m_getcapab(void * arg,mac_capab_t cap,void * cap_data)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
simnet_m_stat(void * arg,uint_t stat,uint64_t * val)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
simnet_m_start(void * arg)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
simnet_m_stop(void * arg)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
simnet_m_promisc(void * arg,boolean_t on)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 *
mcastaddr_lookup(const simnet_dev_t * sdev,const uint8_t * addrp)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
simnet_multicst_add(simnet_dev_t * sdev,const struct ether_addr * eap)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
simnet_multicst_rm(simnet_dev_t * sdev,const struct ether_addr * eap)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
simnet_m_multicst(void * arg,boolean_t add,const uint8_t * addrp)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
simnet_m_unicst(void * arg,const uint8_t * macaddr)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
parse_esslist_args(const void * pr_val,uint_t pr_valsize,char args[][MAX_ESSLIST_ARGLEN])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
set_wl_esslist_priv_prop(simnet_wifidev_t * wdev,uint_t pr_valsize,const void * pr_val)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
simnet_set_priv_prop_wifi(simnet_dev_t * sdev,const char * name,const uint_t len,const void * val)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
simnet_set_priv_prop_ether(simnet_dev_t * sdev,const char * name,const uint_t len,const void * val)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
simnet_setprop_wifi(simnet_dev_t * sdev,const char * name,const mac_prop_id_t num,const uint_t len,const void * val)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
simnet_setprop_ether(simnet_dev_t * sdev,const char * name,const mac_prop_id_t num,const uint_t len,const void * val)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
simnet_m_setprop(void * arg,const char * name,mac_prop_id_t num,const uint_t len,const void * val)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
simnet_get_priv_prop_wifi(const simnet_dev_t * sdev,const char * name,const uint_t len,void * val)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
simnet_get_priv_prop_ether(const simnet_dev_t * sdev,const char * name,const uint_t len,void * val)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
simnet_getprop_wifi(const simnet_dev_t * sdev,const char * name,const mac_prop_id_t num,const uint_t len,void * val)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
simnet_getprop_ether(const simnet_dev_t * sdev,const char * name,const mac_prop_id_t num,const uint_t len,void * val)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
simnet_m_getprop(void * arg,const char * name,const mac_prop_id_t num,const uint_t len,void * val)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
simnet_priv_propinfo_wifi(const char * name,mac_prop_info_handle_t prh)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
simnet_propinfo_wifi(const char * name,const mac_prop_id_t num,mac_prop_info_handle_t prh)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
simnet_priv_propinfo_ether(const char * name,mac_prop_info_handle_t prh)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
simnet_propinfo_ether(const char * name,const mac_prop_id_t num,mac_prop_info_handle_t prh)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
simnet_m_propinfo(void * arg,const char * name,const mac_prop_id_t num,const mac_prop_info_handle_t prh)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