1/*
2 * Copyright (c) 2015-2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * if_fake.c
31 * - fake network interface used for testing
32 * - "feth" (e.g. "feth0", "feth1") is a virtual ethernet interface that allows
33 * two instances to have their output/input paths "crossed-over" so that
34 * output on one is input on the other
35 */
36
37/*
38 * Modification History:
39 *
40 * September 9, 2015 Dieter Siegmund (dieter@apple.com)
41 * - created
42 */
43
44#include <sys/param.h>
45#include <sys/kernel.h>
46#include <sys/malloc.h>
47#include <sys/mbuf.h>
48#include <sys/queue.h>
49#include <sys/socket.h>
50#include <sys/sockio.h>
51#include <sys/sysctl.h>
52#include <sys/systm.h>
53#include <sys/kern_event.h>
54#include <sys/mcache.h>
55#include <sys/syslog.h>
56
57#include <net/bpf.h>
58#include <net/ethernet.h>
59#include <net/if.h>
60#include <net/if_vlan_var.h>
61#include <net/if_fake_var.h>
62#include <net/if_arp.h>
63#include <net/if_dl.h>
64#include <net/if_ether.h>
65#include <net/if_types.h>
66#include <libkern/OSAtomic.h>
67
68#include <net/dlil.h>
69
70#include <net/kpi_interface.h>
71#include <net/kpi_protocol.h>
72
73#include <kern/locks.h>
74#include <kern/zalloc.h>
75
76#ifdef INET
77#include <netinet/in.h>
78#include <netinet/if_ether.h>
79#endif
80
81#include <net/if_media.h>
82#include <net/ether_if_module.h>
83
84#define FAKE_ETHER_NAME "feth"
85
86SYSCTL_DECL(_net_link);
87SYSCTL_NODE(_net_link, OID_AUTO, fake, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
88 "Fake interface");
89
90static int if_fake_txstart = 1;
91SYSCTL_INT(_net_link_fake, OID_AUTO, txstart, CTLFLAG_RW | CTLFLAG_LOCKED,
92 &if_fake_txstart, 0, "Fake interface TXSTART mode");
93
94static int if_fake_hwcsum = 0;
95SYSCTL_INT(_net_link_fake, OID_AUTO, hwcsum, CTLFLAG_RW | CTLFLAG_LOCKED,
96 &if_fake_hwcsum, 0, "Fake interface simulate hardware checksum");
97
98static int if_fake_nxattach = 0;
99SYSCTL_INT(_net_link_fake, OID_AUTO, nxattach, CTLFLAG_RW | CTLFLAG_LOCKED,
100 &if_fake_nxattach, 0, "Fake interface auto-attach nexus");
101
102static int if_fake_bsd_mode = 1;
103SYSCTL_INT(_net_link_fake, OID_AUTO, bsd_mode, CTLFLAG_RW | CTLFLAG_LOCKED,
104 &if_fake_bsd_mode, 0, "Fake interface attach as BSD interface");
105
106static int if_fake_debug = 0;
107SYSCTL_INT(_net_link_fake, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
108 &if_fake_debug, 0, "Fake interface debug logs");
109
110static int if_fake_wmm_mode = 0;
111SYSCTL_INT(_net_link_fake, OID_AUTO, wmm_mode, CTLFLAG_RW | CTLFLAG_LOCKED,
112 &if_fake_wmm_mode, 0, "Fake interface in 802.11 WMM mode");
113
114/**
115 ** virtual ethernet structures, types
116 **/
117
118#define IFF_NUM_TX_RINGS_WMM_MODE 4
119#define IFF_NUM_RX_RINGS_WMM_MODE 1
120#define IFF_MAX_TX_RINGS IFF_NUM_TX_RINGS_WMM_MODE
121#define IFF_MAX_RX_RINGS IFF_NUM_RX_RINGS_WMM_MODE
122
123typedef uint16_t iff_flags_t;
124#define IFF_FLAGS_HWCSUM 0x0001
125#define IFF_FLAGS_BSD_MODE 0x0002
126#define IFF_FLAGS_DETACHING 0x0004
127#define IFF_FLAGS_WMM_MODE 0x0008
128
129
130struct if_fake {
131 char iff_name[IFNAMSIZ]; /* our unique id */
132 ifnet_t iff_ifp;
133 iff_flags_t iff_flags;
134 uint32_t iff_retain_count;
135 ifnet_t iff_peer; /* the other end */
136 int iff_media_current;
137 int iff_media_active;
138 uint32_t iff_media_count;
139 int iff_media_list[IF_FAKE_MEDIA_LIST_MAX];
140 struct mbuf * iff_pending_tx_packet;
141 boolean_t iff_start_busy;
142};
143
144typedef struct if_fake * if_fake_ref;
145
146static if_fake_ref
147ifnet_get_if_fake(ifnet_t ifp);
148
149#define FETH_DPRINTF(fmt, ...) \
150 { if (if_fake_debug != 0) printf("%s " fmt, __func__, ## __VA_ARGS__); }
151
152static inline boolean_t
153feth_in_bsd_mode(if_fake_ref fakeif)
154{
155 return ((fakeif->iff_flags & IFF_FLAGS_BSD_MODE) != 0);
156}
157
158static inline void
159feth_set_detaching(if_fake_ref fakeif)
160{
161 fakeif->iff_flags |= IFF_FLAGS_DETACHING;
162}
163
164static inline boolean_t
165feth_is_detaching(if_fake_ref fakeif)
166{
167 return ((fakeif->iff_flags & IFF_FLAGS_DETACHING) != 0);
168}
169
170static int
171feth_enable_dequeue_stall(ifnet_t ifp, uint32_t enable)
172{
173 int error;
174
175 if (enable != 0)
176 error = ifnet_disable_output(ifp);
177 else
178 error = ifnet_enable_output(ifp);
179
180 return (error);
181}
182
183
184#define FETH_MAXUNIT IF_MAXUNIT
185#define FETH_ZONE_MAX_ELEM MIN(IFNETS_MAX, FETH_MAXUNIT)
186#define M_FAKE M_DEVBUF
187
188static int feth_clone_create(struct if_clone *, u_int32_t, void *);
189static int feth_clone_destroy(ifnet_t);
190static int feth_output(ifnet_t ifp, struct mbuf *m);
191static void feth_start(ifnet_t ifp);
192static int feth_ioctl(ifnet_t ifp, u_long cmd, void * addr);
193static int feth_config(ifnet_t ifp, ifnet_t peer);
194static void feth_if_free(ifnet_t ifp);
195static void feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp);
196static void feth_free(if_fake_ref fakeif);
197
198static struct if_clone
199feth_cloner = IF_CLONE_INITIALIZER(FAKE_ETHER_NAME,
200 feth_clone_create,
201 feth_clone_destroy,
202 0,
203 FETH_MAXUNIT,
204 FETH_ZONE_MAX_ELEM,
205 sizeof(struct if_fake));
206static void interface_link_event(ifnet_t ifp, u_int32_t event_code);
207
208/* some media words to pretend to be ethernet */
209static int default_media_words[] = {
210 IFM_MAKEWORD(IFM_ETHER, 0, 0, 0),
211 IFM_MAKEWORD(IFM_ETHER, IFM_10G_T, IFM_FDX, 0),
212 IFM_MAKEWORD(IFM_ETHER, IFM_2500_T, IFM_FDX, 0),
213 IFM_MAKEWORD(IFM_ETHER, IFM_5000_T, IFM_FDX, 0),
214};
215#define default_media_words_count (sizeof(default_media_words) \
216 / sizeof (default_media_words[0]))
217
218/**
219 ** veth locks
220 **/
221static inline lck_grp_t *
222my_lck_grp_alloc_init(const char * grp_name)
223{
224 lck_grp_t * grp;
225 lck_grp_attr_t * grp_attrs;
226
227 grp_attrs = lck_grp_attr_alloc_init();
228 grp = lck_grp_alloc_init(grp_name, grp_attrs);
229 lck_grp_attr_free(grp_attrs);
230 return (grp);
231}
232
233static inline lck_mtx_t *
234my_lck_mtx_alloc_init(lck_grp_t * lck_grp)
235{
236 lck_attr_t * lck_attrs;
237 lck_mtx_t * lck_mtx;
238
239 lck_attrs = lck_attr_alloc_init();
240 lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs);
241 lck_attr_free(lck_attrs);
242 return (lck_mtx);
243}
244
245static lck_mtx_t * feth_lck_mtx;
246
247static inline void
248feth_lock_init(void)
249{
250 lck_grp_t * feth_lck_grp;
251
252 feth_lck_grp = my_lck_grp_alloc_init("fake");
253 feth_lck_mtx = my_lck_mtx_alloc_init(feth_lck_grp);
254}
255
256#if 0
257static inline void
258feth_assert_lock_not_held(void)
259{
260 LCK_MTX_ASSERT(feth_lck_mtx, LCK_MTX_ASSERT_NOTOWNED);
261 return;
262}
263#endif
264
265static inline void
266feth_lock(void)
267{
268 lck_mtx_lock(feth_lck_mtx);
269 return;
270}
271
272static inline void
273feth_unlock(void)
274{
275 lck_mtx_unlock(feth_lck_mtx);
276 return;
277}
278
279static inline int
280feth_max_mtu(void)
281{
282 if (njcl > 0) {
283 return (M16KCLBYTES - ETHER_HDR_LEN);
284 }
285 return (MBIGCLBYTES - ETHER_HDR_LEN);
286}
287
288static void
289feth_free(if_fake_ref fakeif)
290{
291 assert(fakeif->iff_retain_count == 0);
292 if (feth_in_bsd_mode(fakeif)) {
293 if (fakeif->iff_pending_tx_packet) {
294 m_freem(fakeif->iff_pending_tx_packet);
295 }
296 }
297
298 FETH_DPRINTF("%s\n", fakeif->iff_name);
299 if_clone_softc_deallocate(&feth_cloner, fakeif);
300}
301
302static void
303feth_release(if_fake_ref fakeif)
304{
305 u_int32_t old_retain_count;
306
307 old_retain_count = OSDecrementAtomic(&fakeif->iff_retain_count);
308 switch (old_retain_count) {
309 case 0:
310 assert(old_retain_count != 0);
311 break;
312 case 1:
313 feth_free(fakeif);
314 break;
315 default:
316 break;
317 }
318 return;
319}
320
321
322/**
323 ** feth interface routines
324 **/
325static void
326feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp)
327{
328 (void)ifnet_set_capabilities_enabled(ifp, 0, -1);
329 ifnet_set_addrlen(ifp, ETHER_ADDR_LEN);
330 ifnet_set_baudrate(ifp, 0);
331 ifnet_set_mtu(ifp, ETHERMTU);
332 ifnet_set_flags(ifp,
333 IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX,
334 0xffff);
335 ifnet_set_hdrlen(ifp, sizeof(struct ether_header));
336 if ((fakeif->iff_flags & IFF_FLAGS_HWCSUM) != 0) {
337 ifnet_set_offload(ifp,
338 IFNET_CSUM_IP | IFNET_CSUM_TCP | IFNET_CSUM_UDP |
339 IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6);
340 } else {
341 ifnet_set_offload(ifp, 0);
342 }
343}
344
345static void
346interface_link_event(ifnet_t ifp, u_int32_t event_code)
347{
348 struct {
349 struct kern_event_msg header;
350 u_int32_t unit;
351 char if_name[IFNAMSIZ];
352 } event;
353
354 bzero(&event, sizeof(event));
355 event.header.total_size = sizeof(event);
356 event.header.vendor_code = KEV_VENDOR_APPLE;
357 event.header.kev_class = KEV_NETWORK_CLASS;
358 event.header.kev_subclass = KEV_DL_SUBCLASS;
359 event.header.event_code = event_code;
360 event.header.event_data[0] = ifnet_family(ifp);
361 event.unit = (u_int32_t) ifnet_unit(ifp);
362 strlcpy(event.if_name, ifnet_name(ifp), IFNAMSIZ);
363 ifnet_event(ifp, &event.header);
364 return;
365}
366
367static if_fake_ref
368ifnet_get_if_fake(ifnet_t ifp)
369{
370 return ((if_fake_ref)ifnet_softc(ifp));
371}
372
373static int
374feth_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
375{
376 int error;
377 if_fake_ref fakeif;
378 struct ifnet_init_eparams feth_init;
379 ifnet_t ifp;
380 uint8_t mac_address[ETHER_ADDR_LEN];
381
382 fakeif = if_clone_softc_allocate(&feth_cloner);
383 if (fakeif == NULL) {
384 return ENOBUFS;
385 }
386 fakeif->iff_retain_count = 1;
387#define FAKE_ETHER_NAME_LEN (sizeof(FAKE_ETHER_NAME) - 1)
388 _CASSERT(FAKE_ETHER_NAME_LEN == 4);
389 bcopy(FAKE_ETHER_NAME, mac_address, FAKE_ETHER_NAME_LEN);
390 mac_address[ETHER_ADDR_LEN - 2] = (unit & 0xff00) >> 8;
391 mac_address[ETHER_ADDR_LEN - 1] = unit & 0xff;
392 if (if_fake_bsd_mode != 0) {
393 fakeif->iff_flags |= IFF_FLAGS_BSD_MODE;
394 }
395 if (if_fake_hwcsum != 0) {
396 fakeif->iff_flags |= IFF_FLAGS_HWCSUM;
397 }
398
399 /* use the interface name as the unique id for ifp recycle */
400 if ((unsigned int)
401 snprintf(fakeif->iff_name, sizeof(fakeif->iff_name), "%s%d",
402 ifc->ifc_name, unit) >= sizeof(fakeif->iff_name)) {
403 feth_release(fakeif);
404 return (EINVAL);
405 }
406 bzero(&feth_init, sizeof(feth_init));
407 feth_init.ver = IFNET_INIT_CURRENT_VERSION;
408 feth_init.len = sizeof (feth_init);
409 if (feth_in_bsd_mode(fakeif)) {
410 if (if_fake_txstart != 0) {
411 feth_init.start = feth_start;
412 } else {
413 feth_init.flags |= IFNET_INIT_LEGACY;
414 feth_init.output = feth_output;
415 }
416 }
417 if (if_fake_nxattach == 0) {
418 feth_init.flags |= IFNET_INIT_NX_NOAUTO;
419 }
420 feth_init.uniqueid = fakeif->iff_name;
421 feth_init.uniqueid_len = strlen(fakeif->iff_name);
422 feth_init.name = ifc->ifc_name;
423 feth_init.unit = unit;
424 feth_init.family = IFNET_FAMILY_ETHERNET;
425 feth_init.type = IFT_ETHER;
426 feth_init.demux = ether_demux;
427 feth_init.add_proto = ether_add_proto;
428 feth_init.del_proto = ether_del_proto;
429 feth_init.check_multi = ether_check_multi;
430 feth_init.framer_extended = ether_frameout_extended;
431 feth_init.softc = fakeif;
432 feth_init.ioctl = feth_ioctl;
433 feth_init.set_bpf_tap = NULL;
434 feth_init.detach = feth_if_free;
435 feth_init.broadcast_addr = etherbroadcastaddr;
436 feth_init.broadcast_len = ETHER_ADDR_LEN;
437 if (feth_in_bsd_mode(fakeif)) {
438 error = ifnet_allocate_extended(&feth_init, &ifp);
439 if (error) {
440 feth_release(fakeif);
441 return (error);
442 }
443 feth_ifnet_set_attrs(fakeif, ifp);
444 }
445 fakeif->iff_media_count = default_media_words_count;
446 bcopy(default_media_words, fakeif->iff_media_list,
447 sizeof(default_media_words));
448 if (feth_in_bsd_mode(fakeif)) {
449 error = ifnet_attach(ifp, NULL);
450 if (error) {
451 ifnet_release(ifp);
452 feth_release(fakeif);
453 return (error);
454 }
455 fakeif->iff_ifp = ifp;
456 }
457
458 ifnet_set_lladdr(ifp, mac_address, sizeof(mac_address));
459
460 /* attach as ethernet */
461 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
462 return (0);
463}
464
465static int
466feth_clone_destroy(ifnet_t ifp)
467{
468 if_fake_ref fakeif;
469
470 feth_lock();
471 fakeif = ifnet_get_if_fake(ifp);
472 if (fakeif == NULL || feth_is_detaching(fakeif)) {
473 feth_unlock();
474 return (0);
475 }
476 feth_set_detaching(fakeif);
477 feth_unlock();
478
479 feth_config(ifp, NULL);
480 ifnet_detach(ifp);
481 return 0;
482}
483
484static void
485feth_enqueue_input(ifnet_t ifp, struct mbuf * m)
486{
487 struct ifnet_stat_increment_param stats = {};
488
489 stats.packets_in = 1;
490 stats.bytes_in = (uint32_t)mbuf_pkthdr_len(m) + ETHER_HDR_LEN;
491 ifnet_input(ifp, m, &stats);
492}
493
494static struct mbuf *
495copy_mbuf(struct mbuf *m)
496{
497 struct mbuf * copy_m;
498 uint32_t pkt_len;
499 uint32_t offset;
500
501 if ((m->m_flags & M_PKTHDR) == 0) {
502 return (NULL);
503 }
504 pkt_len = m->m_pkthdr.len;
505 MGETHDR(copy_m, M_DONTWAIT, MT_DATA);
506 if (copy_m == NULL) {
507 goto failed;
508 }
509 if (pkt_len > MHLEN) {
510 if (pkt_len <= MCLBYTES) {
511 MCLGET(copy_m, M_DONTWAIT);
512 } else if (pkt_len <= MBIGCLBYTES) {
513 copy_m = m_mbigget(copy_m, M_DONTWAIT);
514 } else if (pkt_len <= M16KCLBYTES && njcl > 0) {
515 copy_m = m_m16kget(copy_m, M_DONTWAIT);
516 } else {
517 printf("if_fake: copy_mbuf(): packet too large %d\n",
518 pkt_len);
519 goto failed;
520 }
521 if (copy_m == NULL || (copy_m->m_flags & M_EXT) == 0) {
522 goto failed;
523 }
524 }
525 mbuf_setlen(copy_m, pkt_len);
526 copy_m->m_pkthdr.len = pkt_len;
527 copy_m->m_pkthdr.pkt_svc = m->m_pkthdr.pkt_svc;
528 offset = 0;
529 while (m != NULL && offset < pkt_len) {
530 uint32_t frag_len;
531
532 frag_len = m->m_len;
533 if (frag_len > (pkt_len - offset)) {
534 printf("if_fake_: Large mbuf fragment %d > %d\n",
535 frag_len, (pkt_len - offset));
536 goto failed;
537 }
538 m_copydata(m, 0, frag_len, mtod(copy_m, void *) + offset);
539 offset += frag_len;
540 m = m->m_next;
541 }
542 return (copy_m);
543
544 failed:
545 if (copy_m != NULL) {
546 m_freem(copy_m);
547 }
548 return (NULL);
549}
550
551static void
552feth_output_common(ifnet_t ifp, struct mbuf * m, ifnet_t peer,
553 iff_flags_t flags)
554{
555 void * frame_header;
556
557 frame_header = mbuf_data(m);
558 if ((flags & IFF_FLAGS_HWCSUM) != 0) {
559 m->m_pkthdr.csum_data = 0xffff;
560 m->m_pkthdr.csum_flags =
561 CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
562 CSUM_IP_CHECKED | CSUM_IP_VALID;
563 }
564
565 (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
566 bpf_tap_out(ifp, DLT_EN10MB, m, NULL, 0);
567
568 (void)mbuf_pkthdr_setrcvif(m, peer);
569 mbuf_pkthdr_setheader(m, frame_header);
570 mbuf_pkthdr_adjustlen(m, - ETHER_HDR_LEN);
571 (void)mbuf_setdata(m, (char *)mbuf_data(m) + ETHER_HDR_LEN,
572 mbuf_len(m) - ETHER_HDR_LEN);
573 bpf_tap_in(peer, DLT_EN10MB, m, frame_header,
574 sizeof(struct ether_header));
575 feth_enqueue_input(peer, m);
576}
577
578static void
579feth_start(ifnet_t ifp)
580{
581 struct mbuf * copy_m = NULL;
582 if_fake_ref fakeif;
583 iff_flags_t flags = 0;
584 ifnet_t peer = NULL;
585 struct mbuf * m;
586 struct mbuf * save_m;
587
588 feth_lock();
589 fakeif = ifnet_get_if_fake(ifp);
590 if (fakeif->iff_start_busy) {
591 feth_unlock();
592 printf("if_fake: start is busy\n");
593 return;
594 }
595 if (fakeif != NULL) {
596 peer = fakeif->iff_peer;
597 flags = fakeif->iff_flags;
598 }
599
600 /* check for pending TX */
601 m = fakeif->iff_pending_tx_packet;
602 if (m != NULL) {
603 if (peer != NULL) {
604 copy_m = copy_mbuf(m);
605 if (copy_m == NULL) {
606 feth_unlock();
607 return;
608 }
609 }
610 fakeif->iff_pending_tx_packet = NULL;
611 m_freem(m);
612 m = NULL;
613 }
614 fakeif->iff_start_busy = TRUE;
615 feth_unlock();
616 save_m = NULL;
617 for (;;) {
618 if (copy_m != NULL) {
619 assert(peer != NULL);
620 feth_output_common(ifp, copy_m, peer, flags);
621 copy_m = NULL;
622 }
623 if (ifnet_dequeue(ifp, &m) != 0) {
624 break;
625 }
626 if (peer == NULL) {
627 m_freem(m);
628 } else {
629 copy_m = copy_mbuf(m);
630 if (copy_m == NULL) {
631 save_m = m;
632 break;
633 }
634 m_freem(m);
635 }
636 }
637 peer = NULL;
638 feth_lock();
639 fakeif = ifnet_get_if_fake(ifp);
640 if (fakeif != NULL) {
641 fakeif->iff_start_busy = FALSE;
642 if (save_m != NULL && fakeif->iff_peer != NULL) {
643 /* save it for next time */
644 fakeif->iff_pending_tx_packet = save_m;
645 save_m = NULL;
646 }
647 }
648 feth_unlock();
649 if (save_m != NULL) {
650 /* didn't save packet, so free it */
651 m_freem(save_m);
652 }
653}
654
655static int
656feth_output(ifnet_t ifp, struct mbuf * m)
657{
658 struct mbuf * copy_m;
659 if_fake_ref fakeif;
660 iff_flags_t flags;
661 ifnet_t peer = NULL;
662
663 if (m == NULL) {
664 return (0);
665 }
666 copy_m = copy_mbuf(m);
667 m_freem(m);
668 m = NULL;
669 if (copy_m == NULL) {
670 /* count this as an output error */
671 ifnet_stat_increment_out(ifp, 0, 0, 1);
672 return (0);
673 }
674 feth_lock();
675 fakeif = ifnet_get_if_fake(ifp);
676 if (fakeif != NULL) {
677 peer = fakeif->iff_peer;
678 flags = fakeif->iff_flags;
679 }
680 feth_unlock();
681 if (peer == NULL) {
682 m_freem(copy_m);
683 ifnet_stat_increment_out(ifp, 0, 0, 1);
684 return (0);
685 }
686 feth_output_common(ifp, copy_m, peer, flags);
687 return (0);
688}
689
690static int
691feth_config(ifnet_t ifp, ifnet_t peer)
692{
693 int connected = FALSE;
694 int disconnected = FALSE;
695 int error = 0;
696 if_fake_ref fakeif = NULL;
697
698 feth_lock();
699 fakeif = ifnet_get_if_fake(ifp);
700 if (fakeif == NULL) {
701 error = EINVAL;
702 goto done;
703 }
704 if (peer != NULL) {
705 /* connect to peer */
706 if_fake_ref peer_fakeif;
707
708 peer_fakeif = ifnet_get_if_fake(peer);
709 if (peer_fakeif == NULL) {
710 error = EINVAL;
711 goto done;
712 }
713 if (feth_is_detaching(fakeif) ||
714 feth_is_detaching(peer_fakeif) ||
715 peer_fakeif->iff_peer != NULL ||
716 fakeif->iff_peer != NULL) {
717 error = EBUSY;
718 goto done;
719 }
720 fakeif->iff_peer = peer;
721 peer_fakeif->iff_peer = ifp;
722 connected = TRUE;
723 }
724 else if (fakeif->iff_peer != NULL) {
725 /* disconnect from peer */
726 if_fake_ref peer_fakeif;
727
728 peer = fakeif->iff_peer;
729 peer_fakeif = ifnet_get_if_fake(peer);
730 if (peer_fakeif == NULL) {
731 /* should not happen */
732 error = EINVAL;
733 goto done;
734 }
735 fakeif->iff_peer = NULL;
736 peer_fakeif->iff_peer = NULL;
737 disconnected = TRUE;
738 }
739
740 done:
741 feth_unlock();
742
743 /* generate link status event if we connect or disconnect */
744 if (connected) {
745 interface_link_event(ifp, KEV_DL_LINK_ON);
746 interface_link_event(peer, KEV_DL_LINK_ON);
747 }
748 else if (disconnected) {
749 interface_link_event(ifp, KEV_DL_LINK_OFF);
750 interface_link_event(peer, KEV_DL_LINK_OFF);
751 }
752 return (error);
753}
754
755static int
756feth_set_media(ifnet_t ifp, struct if_fake_request * iffr)
757{
758 if_fake_ref fakeif;
759 int error;
760
761 if (iffr->iffr_media.iffm_count > IF_FAKE_MEDIA_LIST_MAX) {
762 /* list is too long */
763 return (EINVAL);
764 }
765 feth_lock();
766 fakeif = ifnet_get_if_fake(ifp);
767 if (fakeif == NULL) {
768 error = EINVAL;
769 goto done;
770 }
771 fakeif->iff_media_count = iffr->iffr_media.iffm_count;
772 bcopy(iffr->iffr_media.iffm_list, fakeif->iff_media_list,
773 iffr->iffr_media.iffm_count * sizeof(fakeif->iff_media_list[0]));
774#if 0
775 /* XXX: "auto-negotiate" active with peer? */
776 /* generate link status event? */
777 fakeif->iff_media_current = iffr->iffr_media.iffm_current;
778#endif
779 error = 0;
780 done:
781 feth_unlock();
782 return (error);
783}
784
785static int
786if_fake_request_copyin(user_addr_t user_addr,
787 struct if_fake_request *iffr, u_int32_t len)
788{
789 int error;
790
791 if (user_addr == USER_ADDR_NULL || len < sizeof(*iffr)) {
792 error = EINVAL;
793 goto done;
794 }
795 error = copyin(user_addr, iffr, sizeof(*iffr));
796 if (error != 0) {
797 goto done;
798 }
799 if (iffr->iffr_reserved[0] != 0 || iffr->iffr_reserved[1] != 0 ||
800 iffr->iffr_reserved[2] != 0 || iffr->iffr_reserved[3] != 0) {
801 error = EINVAL;
802 goto done;
803 }
804 done:
805 return (error);
806}
807
808static int
809feth_set_drvspec(ifnet_t ifp, uint32_t cmd, u_int32_t len,
810 user_addr_t user_addr)
811{
812 int error;
813 struct if_fake_request iffr;
814 ifnet_t peer;
815
816 switch (cmd) {
817 case IF_FAKE_S_CMD_SET_PEER:
818 error = if_fake_request_copyin(user_addr, &iffr, len);
819 if (error != 0) {
820 break;
821 }
822 if (iffr.iffr_peer_name[0] == '\0') {
823 error = feth_config(ifp, NULL);
824 break;
825 }
826
827 /* ensure nul termination */
828 iffr.iffr_peer_name[IFNAMSIZ - 1] = '\0';
829 peer = ifunit(iffr.iffr_peer_name);
830 if (peer == NULL) {
831 error = ENXIO;
832 break;
833 }
834 if (ifnet_type(peer) != IFT_ETHER) {
835 error = EINVAL;
836 break;
837 }
838 if (strcmp(ifnet_name(peer), FAKE_ETHER_NAME) != 0) {
839 error = EINVAL;
840 break;
841 }
842 error = feth_config(ifp, peer);
843 break;
844 case IF_FAKE_S_CMD_SET_MEDIA:
845 error = if_fake_request_copyin(user_addr, &iffr, len);
846 if (error != 0) {
847 break;
848 }
849 error = feth_set_media(ifp, &iffr);
850 break;
851 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL:
852 error = if_fake_request_copyin(user_addr, &iffr, len);
853 if (error != 0) {
854 break;
855 }
856 error = feth_enable_dequeue_stall(ifp,
857 iffr.iffr_dequeue_stall);
858 break;
859 default:
860 error = EOPNOTSUPP;
861 break;
862 }
863 return (error);
864}
865
866static int
867feth_get_drvspec(ifnet_t ifp, u_int32_t cmd, u_int32_t len,
868 user_addr_t user_addr)
869{
870 int error = EOPNOTSUPP;
871 if_fake_ref fakeif;
872 struct if_fake_request iffr;
873 ifnet_t peer;
874
875 switch (cmd) {
876 case IF_FAKE_G_CMD_GET_PEER:
877 if (len < sizeof(iffr)) {
878 error = EINVAL;
879 break;
880 }
881 feth_lock();
882 fakeif = (if_fake_ref)ifnet_softc(ifp);
883 if (fakeif == NULL) {
884 feth_unlock();
885 error = EOPNOTSUPP;
886 break;
887 }
888 peer = fakeif->iff_peer;
889 feth_unlock();
890 bzero(&iffr, sizeof(iffr));
891 if (peer != NULL) {
892 strlcpy(iffr.iffr_peer_name,
893 if_name(peer),
894 sizeof(iffr.iffr_peer_name));
895 }
896 error = copyout(&iffr, user_addr, sizeof(iffr));
897 break;
898 default:
899 break;
900 }
901 return (error);
902}
903
904union ifdrvu {
905 struct ifdrv32 *ifdrvu_32;
906 struct ifdrv64 *ifdrvu_64;
907 void *ifdrvu_p;
908};
909
910static int
911feth_ioctl(ifnet_t ifp, u_long cmd, void * data)
912{
913 unsigned int count;
914 struct ifdevmtu * devmtu_p;
915 union ifdrvu drv;
916 uint32_t drv_cmd;
917 uint32_t drv_len;
918 boolean_t drv_set_command = FALSE;
919 int error = 0;
920 struct ifmediareq * ifmr;
921 struct ifreq * ifr;
922 if_fake_ref fakeif;
923 int status;
924 user_addr_t user_addr;
925
926 ifr = (struct ifreq *)data;
927 switch (cmd) {
928 case SIOCSIFADDR:
929 ifnet_set_flags(ifp, IFF_UP, IFF_UP);
930 break;
931
932 case SIOCGIFMEDIA32:
933 case SIOCGIFMEDIA64:
934 feth_lock();
935 fakeif = (if_fake_ref)ifnet_softc(ifp);
936 if (fakeif == NULL) {
937 feth_unlock();
938 return (EOPNOTSUPP);
939 }
940 status = (fakeif->iff_peer != NULL)
941 ? (IFM_AVALID | IFM_ACTIVE) : IFM_AVALID;
942 ifmr = (struct ifmediareq *)data;
943 user_addr = (cmd == SIOCGIFMEDIA64) ?
944 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
945 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
946 count = ifmr->ifm_count;
947 ifmr->ifm_active = IFM_ETHER;
948 ifmr->ifm_current = IFM_ETHER;
949 ifmr->ifm_mask = 0;
950 ifmr->ifm_status = status;
951 if (user_addr == USER_ADDR_NULL) {
952 ifmr->ifm_count = fakeif->iff_media_count;
953 }
954 else if (count > 0) {
955 if (count > fakeif->iff_media_count) {
956 count = fakeif->iff_media_count;
957 }
958 ifmr->ifm_count = count;
959 error = copyout(&fakeif->iff_media_list, user_addr,
960 count * sizeof(int));
961 }
962 feth_unlock();
963 break;
964
965 case SIOCGIFDEVMTU:
966 devmtu_p = &ifr->ifr_devmtu;
967 devmtu_p->ifdm_current = ifnet_mtu(ifp);
968 devmtu_p->ifdm_max = feth_max_mtu();
969 devmtu_p->ifdm_min = IF_MINMTU;
970 break;
971
972 case SIOCSIFMTU:
973 if (ifr->ifr_mtu > feth_max_mtu() || ifr->ifr_mtu < IF_MINMTU) {
974 error = EINVAL;
975 } else {
976 error = ifnet_set_mtu(ifp, ifr->ifr_mtu);
977 }
978 break;
979
980 case SIOCSDRVSPEC32:
981 case SIOCSDRVSPEC64:
982 error = proc_suser(current_proc());
983 if (error != 0) {
984 break;
985 }
986 drv_set_command = TRUE;
987 /* FALL THROUGH */
988 case SIOCGDRVSPEC32:
989 case SIOCGDRVSPEC64:
990 drv.ifdrvu_p = data;
991 if (cmd == SIOCGDRVSPEC32 || cmd == SIOCSDRVSPEC32) {
992 drv_cmd = drv.ifdrvu_32->ifd_cmd;
993 drv_len = drv.ifdrvu_32->ifd_len;
994 user_addr = CAST_USER_ADDR_T(drv.ifdrvu_32->ifd_data);
995
996 } else {
997 drv_cmd = drv.ifdrvu_64->ifd_cmd;
998 drv_len = drv.ifdrvu_64->ifd_len;
999 user_addr = drv.ifdrvu_64->ifd_data;
1000 }
1001 if (drv_set_command) {
1002 error = feth_set_drvspec(ifp, drv_cmd, drv_len,
1003 user_addr);
1004 } else {
1005 error = feth_get_drvspec(ifp, drv_cmd, drv_len,
1006 user_addr);
1007 }
1008 break;
1009
1010 case SIOCSIFLLADDR:
1011 error = ifnet_set_lladdr(ifp, ifr->ifr_addr.sa_data,
1012 ifr->ifr_addr.sa_len);
1013 break;
1014
1015 case SIOCSIFFLAGS:
1016 if ((ifp->if_flags & IFF_UP) != 0) {
1017 /* marked up, set running if not already set */
1018 if ((ifp->if_flags & IFF_RUNNING) == 0) {
1019 /* set running */
1020 error = ifnet_set_flags(ifp, IFF_RUNNING,
1021 IFF_RUNNING);
1022 }
1023 } else if ((ifp->if_flags & IFF_RUNNING) != 0) {
1024 /* marked down, clear running */
1025 error = ifnet_set_flags(ifp, 0, IFF_RUNNING);
1026 }
1027 break;
1028
1029 case SIOCADDMULTI:
1030 case SIOCDELMULTI:
1031 error = 0;
1032 break;
1033 default:
1034 error = EOPNOTSUPP;
1035 break;
1036 }
1037 return error;
1038}
1039
1040static void
1041feth_if_free(ifnet_t ifp)
1042{
1043 if_fake_ref fakeif;
1044
1045 if (ifp == NULL) {
1046 return;
1047 }
1048 feth_lock();
1049 fakeif = ifnet_get_if_fake(ifp);
1050 if (fakeif == NULL) {
1051 feth_unlock();
1052 return;
1053 }
1054 ifp->if_softc = NULL;
1055 feth_unlock();
1056 feth_release(fakeif);
1057 ifnet_release(ifp);
1058 return;
1059}
1060
1061__private_extern__ void
1062if_fake_init(void)
1063{
1064 int error;
1065
1066 feth_lock_init();
1067 error = if_clone_attach(&feth_cloner);
1068 if (error != 0) {
1069 return;
1070 }
1071 return;
1072}
1073