2 * Copyright (c) 2015-2019 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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
38 * Modification History:
40 * September 9, 2015 Dieter Siegmund (dieter@apple.com)
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/malloc.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>
58 #include <net/ethernet.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>
70 #include <net/kpi_interface.h>
71 #include <net/kpi_protocol.h>
73 #include <kern/locks.h>
74 #include <kern/zalloc.h>
77 #include <netinet/in.h>
78 #include <netinet/if_ether.h>
81 #include <net/if_media.h>
82 #include <net/ether_if_module.h>
85 is_power_of_two(unsigned int val
)
87 return (val
& (val
- 1)) == 0;
90 #define FAKE_ETHER_NAME "feth"
92 SYSCTL_DECL(_net_link
);
93 SYSCTL_NODE(_net_link
, OID_AUTO
, fake
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0,
96 static int if_fake_txstart
= 1;
97 SYSCTL_INT(_net_link_fake
, OID_AUTO
, txstart
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
98 &if_fake_txstart
, 0, "Fake interface TXSTART mode");
100 static int if_fake_hwcsum
= 0;
101 SYSCTL_INT(_net_link_fake
, OID_AUTO
, hwcsum
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
102 &if_fake_hwcsum
, 0, "Fake interface simulate hardware checksum");
104 static int if_fake_nxattach
= 0;
105 SYSCTL_INT(_net_link_fake
, OID_AUTO
, nxattach
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
106 &if_fake_nxattach
, 0, "Fake interface auto-attach nexus");
108 static int if_fake_bsd_mode
= 1;
109 SYSCTL_INT(_net_link_fake
, OID_AUTO
, bsd_mode
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
110 &if_fake_bsd_mode
, 0, "Fake interface attach as BSD interface");
112 static int if_fake_debug
= 0;
113 SYSCTL_INT(_net_link_fake
, OID_AUTO
, debug
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
114 &if_fake_debug
, 0, "Fake interface debug logs");
116 static int if_fake_wmm_mode
= 0;
117 SYSCTL_INT(_net_link_fake
, OID_AUTO
, wmm_mode
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
118 &if_fake_wmm_mode
, 0, "Fake interface in 802.11 WMM mode");
120 static int if_fake_multibuflet
= 0;
121 SYSCTL_INT(_net_link_fake
, OID_AUTO
, multibuflet
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
122 &if_fake_multibuflet
, 0, "Fake interface using multi-buflet packets");
124 static int if_fake_copypkt_mode
= 0;
125 SYSCTL_INT(_net_link_fake
, OID_AUTO
, copypkt_mode
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
126 &if_fake_copypkt_mode
, 0, "Fake interface copying packet to peer");
128 /* sysctl net.link.fake.tx_headroom */
129 #define FETH_TX_HEADROOM_MAX 32
130 static unsigned int if_fake_tx_headroom
= 0;
133 feth_tx_headroom_sysctl SYSCTL_HANDLER_ARGS
135 #pragma unused(oidp, arg1, arg2)
136 unsigned int new_value
;
140 error
= sysctl_io_number(req
, if_fake_tx_headroom
,
141 sizeof(if_fake_tx_headroom
), &new_value
, &changed
);
142 if (error
== 0 && changed
!= 0) {
143 if (new_value
> FETH_TX_HEADROOM_MAX
||
144 (new_value
% 8) != 0) {
147 if_fake_tx_headroom
= new_value
;
152 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, tx_headroom
,
153 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
154 0, 0, feth_tx_headroom_sysctl
, "IU", "Fake ethernet Tx headroom");
157 /* sysctl net.link.fake.max_mtu */
158 #define FETH_MAX_MTU_DEFAULT 2048
159 #define FETH_MAX_MTU_MAX ((16 * 1024) - ETHER_HDR_LEN)
161 static unsigned int if_fake_max_mtu
= FETH_MAX_MTU_DEFAULT
;
163 /* sysctl net.link.fake.buflet_size */
164 #define FETH_BUFLET_SIZE_MIN 512
165 #define FETH_BUFLET_SIZE_MAX 2048
167 static unsigned int if_fake_buflet_size
= FETH_BUFLET_SIZE_MIN
;
170 feth_max_mtu_sysctl SYSCTL_HANDLER_ARGS
172 #pragma unused(oidp, arg1, arg2)
173 unsigned int new_value
;
177 error
= sysctl_io_number(req
, if_fake_max_mtu
,
178 sizeof(if_fake_max_mtu
), &new_value
, &changed
);
179 if (error
== 0 && changed
!= 0) {
180 if (new_value
> FETH_MAX_MTU_MAX
||
181 new_value
< ETHERMTU
||
182 new_value
<= if_fake_buflet_size
) {
185 if_fake_max_mtu
= new_value
;
190 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, max_mtu
,
191 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
192 0, 0, feth_max_mtu_sysctl
, "IU", "Fake interface maximum MTU");
195 feth_buflet_size_sysctl SYSCTL_HANDLER_ARGS
197 #pragma unused(oidp, arg1, arg2)
198 unsigned int new_value
;
202 error
= sysctl_io_number(req
, if_fake_buflet_size
,
203 sizeof(if_fake_buflet_size
), &new_value
, &changed
);
204 if (error
== 0 && changed
!= 0) {
205 /* must be a power of 2 between min and max */
206 if (new_value
> FETH_BUFLET_SIZE_MAX
||
207 new_value
< FETH_BUFLET_SIZE_MIN
||
208 !is_power_of_two(new_value
) ||
209 new_value
>= if_fake_max_mtu
) {
212 if_fake_buflet_size
= new_value
;
217 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, buflet_size
,
218 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
219 0, 0, feth_buflet_size_sysctl
, "IU", "Fake interface buflet size");
221 static unsigned int if_fake_user_access
= 0;
224 feth_user_access_sysctl SYSCTL_HANDLER_ARGS
226 #pragma unused(oidp, arg1, arg2)
227 unsigned int new_value
;
231 error
= sysctl_io_number(req
, if_fake_user_access
,
232 sizeof(if_fake_user_access
), &new_value
, &changed
);
233 if (error
== 0 && changed
!= 0) {
234 if (new_value
!= 0) {
235 if (new_value
!= 1) {
239 * copypkt mode requires a kernel only buffer pool so
240 * it is incompatible with user access mode.
242 if (if_fake_copypkt_mode
!= 0) {
246 if_fake_user_access
= new_value
;
251 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, user_access
,
252 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
253 0, 0, feth_user_access_sysctl
, "IU", "Fake interface user access");
255 /* sysctl net.link.fake.if_adv_intvl (unit: millisecond) */
256 #define FETH_IF_ADV_INTVL_MIN 10
257 #define FETH_IF_ADV_INTVL_MAX INT_MAX
259 static int if_fake_if_adv_interval
= 0; /* no interface advisory */
261 feth_if_adv_interval_sysctl SYSCTL_HANDLER_ARGS
263 #pragma unused(oidp, arg1, arg2)
264 unsigned int new_value
;
268 error
= sysctl_io_number(req
, if_fake_if_adv_interval
,
269 sizeof(if_fake_if_adv_interval
), &new_value
, &changed
);
270 if (error
== 0 && changed
!= 0) {
271 if ((new_value
!= 0) && (new_value
> FETH_IF_ADV_INTVL_MAX
||
272 new_value
< FETH_IF_ADV_INTVL_MIN
)) {
275 if_fake_if_adv_interval
= new_value
;
280 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, if_adv_intvl
,
281 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0,
282 feth_if_adv_interval_sysctl
, "IU",
283 "Fake interface will generate interface advisories reports at the specified interval in ms");
285 /* sysctl net.link.fake.tx_drops */
287 * Fake ethernet will drop packet on the transmit path at the specified
288 * rate, i.e drop one in every if_fake_tx_drops number of packets.
290 #define FETH_TX_DROPS_MIN 0
291 #define FETH_TX_DROPS_MAX INT_MAX
292 static int if_fake_tx_drops
= 0; /* no packets are dropped */
294 feth_fake_tx_drops_sysctl SYSCTL_HANDLER_ARGS
296 #pragma unused(oidp, arg1, arg2)
297 unsigned int new_value
;
301 error
= sysctl_io_number(req
, if_fake_tx_drops
,
302 sizeof(if_fake_tx_drops
), &new_value
, &changed
);
303 if (error
== 0 && changed
!= 0) {
304 if (new_value
> FETH_TX_DROPS_MAX
||
305 new_value
< FETH_TX_DROPS_MIN
) {
308 if_fake_tx_drops
= new_value
;
313 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, tx_drops
,
314 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0,
315 feth_fake_tx_drops_sysctl
, "IU",
316 "Fake interface will intermittently drop packets on Tx path");
319 ** virtual ethernet structures, types
322 #define IFF_NUM_TX_RINGS_WMM_MODE 4
323 #define IFF_NUM_RX_RINGS_WMM_MODE 1
324 #define IFF_MAX_TX_RINGS IFF_NUM_TX_RINGS_WMM_MODE
325 #define IFF_MAX_RX_RINGS IFF_NUM_RX_RINGS_WMM_MODE
327 typedef uint16_t iff_flags_t
;
328 #define IFF_FLAGS_HWCSUM 0x0001
329 #define IFF_FLAGS_BSD_MODE 0x0002
330 #define IFF_FLAGS_DETACHING 0x0004
331 #define IFF_FLAGS_WMM_MODE 0x0008
332 #define IFF_FLAGS_MULTIBUFLETS 0x0010
333 #define IFF_FLAGS_COPYPKT_MODE 0x0020
337 char iff_name
[IFNAMSIZ
]; /* our unique id */
339 iff_flags_t iff_flags
;
340 uint32_t iff_retain_count
;
341 ifnet_t iff_peer
; /* the other end */
342 int iff_media_current
;
343 int iff_media_active
;
344 uint32_t iff_media_count
;
345 int iff_media_list
[IF_FAKE_MEDIA_LIST_MAX
];
346 struct mbuf
* iff_pending_tx_packet
;
347 boolean_t iff_start_busy
;
348 unsigned int iff_max_mtu
;
351 typedef struct if_fake
* if_fake_ref
;
354 ifnet_get_if_fake(ifnet_t ifp
);
356 #define FETH_DPRINTF(fmt, ...) \
357 { if (if_fake_debug != 0) printf("%s " fmt, __func__, ## __VA_ARGS__); }
359 static inline boolean_t
360 feth_in_bsd_mode(if_fake_ref fakeif
)
362 return (fakeif
->iff_flags
& IFF_FLAGS_BSD_MODE
) != 0;
366 feth_set_detaching(if_fake_ref fakeif
)
368 fakeif
->iff_flags
|= IFF_FLAGS_DETACHING
;
371 static inline boolean_t
372 feth_is_detaching(if_fake_ref fakeif
)
374 return (fakeif
->iff_flags
& IFF_FLAGS_DETACHING
) != 0;
378 feth_enable_dequeue_stall(ifnet_t ifp
, uint32_t enable
)
383 error
= ifnet_disable_output(ifp
);
385 error
= ifnet_enable_output(ifp
);
392 #define FETH_MAXUNIT IF_MAXUNIT
393 #define FETH_ZONE_MAX_ELEM MIN(IFNETS_MAX, FETH_MAXUNIT)
394 #define M_FAKE M_DEVBUF
396 static int feth_clone_create(struct if_clone
*, u_int32_t
, void *);
397 static int feth_clone_destroy(ifnet_t
);
398 static int feth_output(ifnet_t ifp
, struct mbuf
*m
);
399 static void feth_start(ifnet_t ifp
);
400 static int feth_ioctl(ifnet_t ifp
, u_long cmd
, void * addr
);
401 static int feth_config(ifnet_t ifp
, ifnet_t peer
);
402 static void feth_if_free(ifnet_t ifp
);
403 static void feth_ifnet_set_attrs(if_fake_ref fakeif
, ifnet_t ifp
);
404 static void feth_free(if_fake_ref fakeif
);
406 static struct if_clone
407 feth_cloner
= IF_CLONE_INITIALIZER(FAKE_ETHER_NAME
,
413 sizeof(struct if_fake
));
414 static void interface_link_event(ifnet_t ifp
, u_int32_t event_code
);
416 /* some media words to pretend to be ethernet */
417 static int default_media_words
[] = {
418 IFM_MAKEWORD(IFM_ETHER
, 0, 0, 0),
419 IFM_MAKEWORD(IFM_ETHER
, IFM_10G_T
, IFM_FDX
, 0),
420 IFM_MAKEWORD(IFM_ETHER
, IFM_2500_T
, IFM_FDX
, 0),
421 IFM_MAKEWORD(IFM_ETHER
, IFM_5000_T
, IFM_FDX
, 0),
423 IFM_MAKEWORD(IFM_ETHER
, IFM_10G_KX4
, IFM_FDX
, 0),
424 IFM_MAKEWORD(IFM_ETHER
, IFM_20G_KR2
, IFM_FDX
, 0),
425 IFM_MAKEWORD(IFM_ETHER
, IFM_2500_SX
, IFM_FDX
, 0),
426 IFM_MAKEWORD(IFM_ETHER
, IFM_25G_KR
, IFM_FDX
, 0),
427 IFM_MAKEWORD(IFM_ETHER
, IFM_40G_SR4
, IFM_FDX
, 0),
428 IFM_MAKEWORD(IFM_ETHER
, IFM_50G_CR2
, IFM_FDX
, 0),
429 IFM_MAKEWORD(IFM_ETHER
, IFM_56G_R4
, IFM_FDX
, 0),
430 IFM_MAKEWORD(IFM_ETHER
, IFM_100G_CR4
, IFM_FDX
, 0),
431 IFM_MAKEWORD(IFM_ETHER
, IFM_400G_AUI8
, IFM_FDX
, 0),
433 #define default_media_words_count (sizeof(default_media_words) \
434 / sizeof (default_media_words[0]))
439 static inline lck_grp_t
*
440 my_lck_grp_alloc_init(const char * grp_name
)
443 lck_grp_attr_t
* grp_attrs
;
445 grp_attrs
= lck_grp_attr_alloc_init();
446 grp
= lck_grp_alloc_init(grp_name
, grp_attrs
);
447 lck_grp_attr_free(grp_attrs
);
451 static inline lck_mtx_t
*
452 my_lck_mtx_alloc_init(lck_grp_t
* lck_grp
)
454 lck_attr_t
* lck_attrs
;
457 lck_attrs
= lck_attr_alloc_init();
458 lck_mtx
= lck_mtx_alloc_init(lck_grp
, lck_attrs
);
459 lck_attr_free(lck_attrs
);
463 static lck_mtx_t
* feth_lck_mtx
;
468 lck_grp_t
* feth_lck_grp
;
470 feth_lck_grp
= my_lck_grp_alloc_init("fake");
471 feth_lck_mtx
= my_lck_mtx_alloc_init(feth_lck_grp
);
476 feth_assert_lock_not_held(void)
478 LCK_MTX_ASSERT(feth_lck_mtx
, LCK_MTX_ASSERT_NOTOWNED
);
486 lck_mtx_lock(feth_lck_mtx
);
493 lck_mtx_unlock(feth_lck_mtx
);
498 get_max_mtu(int bsd_mode
, unsigned int max_mtu
)
503 mtu
= (njcl
> 0) ? (M16KCLBYTES
- ETHER_HDR_LEN
)
504 : MBIGCLBYTES
- ETHER_HDR_LEN
;
514 static inline unsigned int
515 feth_max_mtu(ifnet_t ifp
)
518 unsigned int max_mtu
= ETHERMTU
;
521 fakeif
= ifnet_get_if_fake(ifp
);
522 if (fakeif
!= NULL
) {
523 max_mtu
= fakeif
->iff_max_mtu
;
530 feth_free(if_fake_ref fakeif
)
532 assert(fakeif
->iff_retain_count
== 0);
533 if (feth_in_bsd_mode(fakeif
)) {
534 if (fakeif
->iff_pending_tx_packet
) {
535 m_freem(fakeif
->iff_pending_tx_packet
);
539 FETH_DPRINTF("%s\n", fakeif
->iff_name
);
540 if_clone_softc_deallocate(&feth_cloner
, fakeif
);
544 feth_release(if_fake_ref fakeif
)
546 u_int32_t old_retain_count
;
548 old_retain_count
= OSDecrementAtomic(&fakeif
->iff_retain_count
);
549 switch (old_retain_count
) {
551 assert(old_retain_count
!= 0);
564 ** feth interface routines
567 feth_ifnet_set_attrs(if_fake_ref fakeif
, ifnet_t ifp
)
569 (void)ifnet_set_capabilities_enabled(ifp
, 0, -1);
570 ifnet_set_addrlen(ifp
, ETHER_ADDR_LEN
);
571 ifnet_set_baudrate(ifp
, 0);
572 ifnet_set_mtu(ifp
, ETHERMTU
);
574 IFF_BROADCAST
| IFF_MULTICAST
| IFF_SIMPLEX
,
576 ifnet_set_hdrlen(ifp
, sizeof(struct ether_header
));
577 if ((fakeif
->iff_flags
& IFF_FLAGS_HWCSUM
) != 0) {
578 ifnet_set_offload(ifp
,
579 IFNET_CSUM_IP
| IFNET_CSUM_TCP
| IFNET_CSUM_UDP
|
580 IFNET_CSUM_TCPIPV6
| IFNET_CSUM_UDPIPV6
);
582 ifnet_set_offload(ifp
, 0);
587 interface_link_event(ifnet_t ifp
, u_int32_t event_code
)
590 struct kern_event_msg header
;
592 char if_name
[IFNAMSIZ
];
595 bzero(&event
, sizeof(event
));
596 event
.header
.total_size
= sizeof(event
);
597 event
.header
.vendor_code
= KEV_VENDOR_APPLE
;
598 event
.header
.kev_class
= KEV_NETWORK_CLASS
;
599 event
.header
.kev_subclass
= KEV_DL_SUBCLASS
;
600 event
.header
.event_code
= event_code
;
601 event
.header
.event_data
[0] = ifnet_family(ifp
);
602 event
.unit
= (u_int32_t
) ifnet_unit(ifp
);
603 strlcpy(event
.if_name
, ifnet_name(ifp
), IFNAMSIZ
);
604 ifnet_event(ifp
, &event
.header
);
609 ifnet_get_if_fake(ifnet_t ifp
)
611 return (if_fake_ref
)ifnet_softc(ifp
);
615 feth_clone_create(struct if_clone
*ifc
, u_int32_t unit
, __unused
void *params
)
619 struct ifnet_init_eparams feth_init
;
621 uint8_t mac_address
[ETHER_ADDR_LEN
];
623 fakeif
= if_clone_softc_allocate(&feth_cloner
);
624 if (fakeif
== NULL
) {
627 fakeif
->iff_retain_count
= 1;
628 #define FAKE_ETHER_NAME_LEN (sizeof(FAKE_ETHER_NAME) - 1)
629 _CASSERT(FAKE_ETHER_NAME_LEN
== 4);
630 bcopy(FAKE_ETHER_NAME
, mac_address
, FAKE_ETHER_NAME_LEN
);
631 mac_address
[ETHER_ADDR_LEN
- 2] = (unit
& 0xff00) >> 8;
632 mac_address
[ETHER_ADDR_LEN
- 1] = unit
& 0xff;
633 if (if_fake_bsd_mode
!= 0) {
634 fakeif
->iff_flags
|= IFF_FLAGS_BSD_MODE
;
636 if (if_fake_hwcsum
!= 0) {
637 fakeif
->iff_flags
|= IFF_FLAGS_HWCSUM
;
639 fakeif
->iff_max_mtu
= get_max_mtu(if_fake_bsd_mode
, if_fake_max_mtu
);
641 /* use the interface name as the unique id for ifp recycle */
643 snprintf(fakeif
->iff_name
, sizeof(fakeif
->iff_name
), "%s%d",
644 ifc
->ifc_name
, unit
) >= sizeof(fakeif
->iff_name
)) {
645 feth_release(fakeif
);
648 bzero(&feth_init
, sizeof(feth_init
));
649 feth_init
.ver
= IFNET_INIT_CURRENT_VERSION
;
650 feth_init
.len
= sizeof(feth_init
);
651 if (feth_in_bsd_mode(fakeif
)) {
652 if (if_fake_txstart
!= 0) {
653 feth_init
.start
= feth_start
;
655 feth_init
.flags
|= IFNET_INIT_LEGACY
;
656 feth_init
.output
= feth_output
;
659 if (if_fake_nxattach
== 0) {
660 feth_init
.flags
|= IFNET_INIT_NX_NOAUTO
;
662 feth_init
.uniqueid
= fakeif
->iff_name
;
663 feth_init
.uniqueid_len
= strlen(fakeif
->iff_name
);
664 feth_init
.name
= ifc
->ifc_name
;
665 feth_init
.unit
= unit
;
666 feth_init
.family
= IFNET_FAMILY_ETHERNET
;
667 feth_init
.type
= IFT_ETHER
;
668 feth_init
.demux
= ether_demux
;
669 feth_init
.add_proto
= ether_add_proto
;
670 feth_init
.del_proto
= ether_del_proto
;
671 feth_init
.check_multi
= ether_check_multi
;
672 feth_init
.framer_extended
= ether_frameout_extended
;
673 feth_init
.softc
= fakeif
;
674 feth_init
.ioctl
= feth_ioctl
;
675 feth_init
.set_bpf_tap
= NULL
;
676 feth_init
.detach
= feth_if_free
;
677 feth_init
.broadcast_addr
= etherbroadcastaddr
;
678 feth_init
.broadcast_len
= ETHER_ADDR_LEN
;
679 if (feth_in_bsd_mode(fakeif
)) {
680 error
= ifnet_allocate_extended(&feth_init
, &ifp
);
682 feth_release(fakeif
);
685 feth_ifnet_set_attrs(fakeif
, ifp
);
687 fakeif
->iff_media_count
= MIN(default_media_words_count
, IF_FAKE_MEDIA_LIST_MAX
);
688 bcopy(default_media_words
, fakeif
->iff_media_list
,
689 fakeif
->iff_media_count
* sizeof(fakeif
->iff_media_list
[0]));
690 if (feth_in_bsd_mode(fakeif
)) {
691 error
= ifnet_attach(ifp
, NULL
);
694 feth_release(fakeif
);
697 fakeif
->iff_ifp
= ifp
;
700 ifnet_set_lladdr(ifp
, mac_address
, sizeof(mac_address
));
702 /* attach as ethernet */
703 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
708 feth_clone_destroy(ifnet_t ifp
)
713 fakeif
= ifnet_get_if_fake(ifp
);
714 if (fakeif
== NULL
|| feth_is_detaching(fakeif
)) {
718 feth_set_detaching(fakeif
);
721 feth_config(ifp
, NULL
);
727 feth_enqueue_input(ifnet_t ifp
, struct mbuf
* m
)
729 struct ifnet_stat_increment_param stats
= {};
731 stats
.packets_in
= 1;
732 stats
.bytes_in
= (uint32_t)mbuf_pkthdr_len(m
) + ETHER_HDR_LEN
;
733 ifnet_input(ifp
, m
, &stats
);
737 copy_mbuf(struct mbuf
*m
)
739 struct mbuf
* copy_m
;
743 if ((m
->m_flags
& M_PKTHDR
) == 0) {
746 pkt_len
= m
->m_pkthdr
.len
;
747 MGETHDR(copy_m
, M_DONTWAIT
, MT_DATA
);
748 if (copy_m
== NULL
) {
751 if (pkt_len
> MHLEN
) {
752 if (pkt_len
<= MCLBYTES
) {
753 MCLGET(copy_m
, M_DONTWAIT
);
754 } else if (pkt_len
<= MBIGCLBYTES
) {
755 copy_m
= m_mbigget(copy_m
, M_DONTWAIT
);
756 } else if (pkt_len
<= M16KCLBYTES
&& njcl
> 0) {
757 copy_m
= m_m16kget(copy_m
, M_DONTWAIT
);
759 printf("if_fake: copy_mbuf(): packet too large %d\n",
763 if (copy_m
== NULL
|| (copy_m
->m_flags
& M_EXT
) == 0) {
767 mbuf_setlen(copy_m
, pkt_len
);
768 copy_m
->m_pkthdr
.len
= pkt_len
;
769 copy_m
->m_pkthdr
.pkt_svc
= m
->m_pkthdr
.pkt_svc
;
771 while (m
!= NULL
&& offset
< pkt_len
) {
775 if (frag_len
> (pkt_len
- offset
)) {
776 printf("if_fake_: Large mbuf fragment %d > %d\n",
777 frag_len
, (pkt_len
- offset
));
780 m_copydata(m
, 0, frag_len
, mtod(copy_m
, void *) + offset
);
787 if (copy_m
!= NULL
) {
794 feth_output_common(ifnet_t ifp
, struct mbuf
* m
, ifnet_t peer
,
799 frame_header
= mbuf_data(m
);
800 if ((flags
& IFF_FLAGS_HWCSUM
) != 0) {
801 m
->m_pkthdr
.csum_data
= 0xffff;
802 m
->m_pkthdr
.csum_flags
=
803 CSUM_DATA_VALID
| CSUM_PSEUDO_HDR
|
804 CSUM_IP_CHECKED
| CSUM_IP_VALID
;
807 (void)ifnet_stat_increment_out(ifp
, 1, m
->m_pkthdr
.len
, 0);
808 bpf_tap_out(ifp
, DLT_EN10MB
, m
, NULL
, 0);
810 (void)mbuf_pkthdr_setrcvif(m
, peer
);
811 mbuf_pkthdr_setheader(m
, frame_header
);
812 mbuf_pkthdr_adjustlen(m
, -ETHER_HDR_LEN
);
813 (void)mbuf_setdata(m
, (char *)mbuf_data(m
) + ETHER_HDR_LEN
,
814 mbuf_len(m
) - ETHER_HDR_LEN
);
815 bpf_tap_in(peer
, DLT_EN10MB
, m
, frame_header
,
816 sizeof(struct ether_header
));
817 feth_enqueue_input(peer
, m
);
821 feth_start(ifnet_t ifp
)
823 struct mbuf
* copy_m
= NULL
;
825 iff_flags_t flags
= 0;
828 struct mbuf
* save_m
;
831 fakeif
= ifnet_get_if_fake(ifp
);
832 if (fakeif
== NULL
) {
837 if (fakeif
->iff_start_busy
) {
839 printf("if_fake: start is busy\n");
843 peer
= fakeif
->iff_peer
;
844 flags
= fakeif
->iff_flags
;
846 /* check for pending TX */
847 m
= fakeif
->iff_pending_tx_packet
;
850 copy_m
= copy_mbuf(m
);
851 if (copy_m
== NULL
) {
856 fakeif
->iff_pending_tx_packet
= NULL
;
860 fakeif
->iff_start_busy
= TRUE
;
864 if (copy_m
!= NULL
) {
865 assert(peer
!= NULL
);
866 feth_output_common(ifp
, copy_m
, peer
, flags
);
869 if (ifnet_dequeue(ifp
, &m
) != 0) {
875 copy_m
= copy_mbuf(m
);
876 if (copy_m
== NULL
) {
885 fakeif
= ifnet_get_if_fake(ifp
);
886 if (fakeif
!= NULL
) {
887 fakeif
->iff_start_busy
= FALSE
;
888 if (save_m
!= NULL
&& fakeif
->iff_peer
!= NULL
) {
889 /* save it for next time */
890 fakeif
->iff_pending_tx_packet
= save_m
;
895 if (save_m
!= NULL
) {
896 /* didn't save packet, so free it */
902 feth_output(ifnet_t ifp
, struct mbuf
* m
)
904 struct mbuf
* copy_m
;
912 copy_m
= copy_mbuf(m
);
915 if (copy_m
== NULL
) {
916 /* count this as an output error */
917 ifnet_stat_increment_out(ifp
, 0, 0, 1);
921 fakeif
= ifnet_get_if_fake(ifp
);
922 if (fakeif
!= NULL
) {
923 peer
= fakeif
->iff_peer
;
924 flags
= fakeif
->iff_flags
;
929 ifnet_stat_increment_out(ifp
, 0, 0, 1);
932 feth_output_common(ifp
, copy_m
, peer
, flags
);
937 feth_config(ifnet_t ifp
, ifnet_t peer
)
939 int connected
= FALSE
;
940 int disconnected
= FALSE
;
942 if_fake_ref fakeif
= NULL
;
945 fakeif
= ifnet_get_if_fake(ifp
);
946 if (fakeif
== NULL
) {
951 /* connect to peer */
952 if_fake_ref peer_fakeif
;
954 peer_fakeif
= ifnet_get_if_fake(peer
);
955 if (peer_fakeif
== NULL
) {
959 if (feth_is_detaching(fakeif
) ||
960 feth_is_detaching(peer_fakeif
) ||
961 peer_fakeif
->iff_peer
!= NULL
||
962 fakeif
->iff_peer
!= NULL
) {
966 fakeif
->iff_peer
= peer
;
967 peer_fakeif
->iff_peer
= ifp
;
969 } else if (fakeif
->iff_peer
!= NULL
) {
970 /* disconnect from peer */
971 if_fake_ref peer_fakeif
;
973 peer
= fakeif
->iff_peer
;
974 peer_fakeif
= ifnet_get_if_fake(peer
);
975 if (peer_fakeif
== NULL
) {
976 /* should not happen */
980 fakeif
->iff_peer
= NULL
;
981 peer_fakeif
->iff_peer
= NULL
;
988 /* generate link status event if we connect or disconnect */
990 interface_link_event(ifp
, KEV_DL_LINK_ON
);
991 interface_link_event(peer
, KEV_DL_LINK_ON
);
992 } else if (disconnected
) {
993 interface_link_event(ifp
, KEV_DL_LINK_OFF
);
994 interface_link_event(peer
, KEV_DL_LINK_OFF
);
1000 feth_set_media(ifnet_t ifp
, struct if_fake_request
* iffr
)
1005 if (iffr
->iffr_media
.iffm_count
> IF_FAKE_MEDIA_LIST_MAX
) {
1006 /* list is too long */
1010 fakeif
= ifnet_get_if_fake(ifp
);
1011 if (fakeif
== NULL
) {
1015 fakeif
->iff_media_count
= iffr
->iffr_media
.iffm_count
;
1016 bcopy(iffr
->iffr_media
.iffm_list
, fakeif
->iff_media_list
,
1017 iffr
->iffr_media
.iffm_count
* sizeof(fakeif
->iff_media_list
[0]));
1019 /* XXX: "auto-negotiate" active with peer? */
1020 /* generate link status event? */
1021 fakeif
->iff_media_current
= iffr
->iffr_media
.iffm_current
;
1030 if_fake_request_copyin(user_addr_t user_addr
,
1031 struct if_fake_request
*iffr
, u_int32_t len
)
1035 if (user_addr
== USER_ADDR_NULL
|| len
< sizeof(*iffr
)) {
1039 error
= copyin(user_addr
, iffr
, sizeof(*iffr
));
1043 if (iffr
->iffr_reserved
[0] != 0 || iffr
->iffr_reserved
[1] != 0 ||
1044 iffr
->iffr_reserved
[2] != 0 || iffr
->iffr_reserved
[3] != 0) {
1053 feth_set_drvspec(ifnet_t ifp
, uint32_t cmd
, u_int32_t len
,
1054 user_addr_t user_addr
)
1057 struct if_fake_request iffr
;
1061 case IF_FAKE_S_CMD_SET_PEER
:
1062 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1066 if (iffr
.iffr_peer_name
[0] == '\0') {
1067 error
= feth_config(ifp
, NULL
);
1071 /* ensure nul termination */
1072 iffr
.iffr_peer_name
[IFNAMSIZ
- 1] = '\0';
1073 peer
= ifunit(iffr
.iffr_peer_name
);
1078 if (ifnet_type(peer
) != IFT_ETHER
) {
1082 if (strcmp(ifnet_name(peer
), FAKE_ETHER_NAME
) != 0) {
1086 error
= feth_config(ifp
, peer
);
1088 case IF_FAKE_S_CMD_SET_MEDIA
:
1089 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1093 error
= feth_set_media(ifp
, &iffr
);
1095 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL
:
1096 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1100 error
= feth_enable_dequeue_stall(ifp
,
1101 iffr
.iffr_dequeue_stall
);
1111 feth_get_drvspec(ifnet_t ifp
, u_int32_t cmd
, u_int32_t len
,
1112 user_addr_t user_addr
)
1114 int error
= EOPNOTSUPP
;
1116 struct if_fake_request iffr
;
1120 case IF_FAKE_G_CMD_GET_PEER
:
1121 if (len
< sizeof(iffr
)) {
1126 fakeif
= ifnet_get_if_fake(ifp
);
1127 if (fakeif
== NULL
) {
1132 peer
= fakeif
->iff_peer
;
1134 bzero(&iffr
, sizeof(iffr
));
1136 strlcpy(iffr
.iffr_peer_name
,
1138 sizeof(iffr
.iffr_peer_name
));
1140 error
= copyout(&iffr
, user_addr
, sizeof(iffr
));
1149 struct ifdrv32
*ifdrvu_32
;
1150 struct ifdrv64
*ifdrvu_64
;
1155 feth_ioctl(ifnet_t ifp
, u_long cmd
, void * data
)
1158 struct ifdevmtu
* devmtu_p
;
1162 boolean_t drv_set_command
= FALSE
;
1164 struct ifmediareq
* ifmr
;
1168 user_addr_t user_addr
;
1170 ifr
= (struct ifreq
*)data
;
1173 ifnet_set_flags(ifp
, IFF_UP
, IFF_UP
);
1176 case SIOCGIFMEDIA32
:
1177 case SIOCGIFMEDIA64
:
1179 fakeif
= ifnet_get_if_fake(ifp
);
1180 if (fakeif
== NULL
) {
1184 status
= (fakeif
->iff_peer
!= NULL
)
1185 ? (IFM_AVALID
| IFM_ACTIVE
) : IFM_AVALID
;
1186 ifmr
= (struct ifmediareq
*)data
;
1187 user_addr
= (cmd
== SIOCGIFMEDIA64
) ?
1188 ((struct ifmediareq64
*)ifmr
)->ifmu_ulist
:
1189 CAST_USER_ADDR_T(((struct ifmediareq32
*)ifmr
)->ifmu_ulist
);
1190 count
= ifmr
->ifm_count
;
1191 ifmr
->ifm_active
= IFM_ETHER
;
1192 ifmr
->ifm_current
= IFM_ETHER
;
1194 ifmr
->ifm_status
= status
;
1195 if (user_addr
== USER_ADDR_NULL
) {
1196 ifmr
->ifm_count
= fakeif
->iff_media_count
;
1197 } else if (count
> 0) {
1198 if (count
> fakeif
->iff_media_count
) {
1199 count
= fakeif
->iff_media_count
;
1201 ifmr
->ifm_count
= count
;
1202 error
= copyout(&fakeif
->iff_media_list
, user_addr
,
1203 count
* sizeof(int));
1209 devmtu_p
= &ifr
->ifr_devmtu
;
1210 devmtu_p
->ifdm_current
= ifnet_mtu(ifp
);
1211 devmtu_p
->ifdm_max
= feth_max_mtu(ifp
);
1212 devmtu_p
->ifdm_min
= IF_MINMTU
;
1216 if ((unsigned int)ifr
->ifr_mtu
> feth_max_mtu(ifp
) ||
1217 ifr
->ifr_mtu
< IF_MINMTU
) {
1220 error
= ifnet_set_mtu(ifp
, ifr
->ifr_mtu
);
1224 case SIOCSDRVSPEC32
:
1225 case SIOCSDRVSPEC64
:
1226 error
= proc_suser(current_proc());
1230 drv_set_command
= TRUE
;
1232 case SIOCGDRVSPEC32
:
1233 case SIOCGDRVSPEC64
:
1234 drv
.ifdrvu_p
= data
;
1235 if (cmd
== SIOCGDRVSPEC32
|| cmd
== SIOCSDRVSPEC32
) {
1236 drv_cmd
= drv
.ifdrvu_32
->ifd_cmd
;
1237 drv_len
= drv
.ifdrvu_32
->ifd_len
;
1238 user_addr
= CAST_USER_ADDR_T(drv
.ifdrvu_32
->ifd_data
);
1240 drv_cmd
= drv
.ifdrvu_64
->ifd_cmd
;
1241 drv_len
= drv
.ifdrvu_64
->ifd_len
;
1242 user_addr
= drv
.ifdrvu_64
->ifd_data
;
1244 if (drv_set_command
) {
1245 error
= feth_set_drvspec(ifp
, drv_cmd
, drv_len
,
1248 error
= feth_get_drvspec(ifp
, drv_cmd
, drv_len
,
1254 error
= ifnet_set_lladdr(ifp
, ifr
->ifr_addr
.sa_data
,
1255 ifr
->ifr_addr
.sa_len
);
1259 if ((ifp
->if_flags
& IFF_UP
) != 0) {
1260 /* marked up, set running if not already set */
1261 if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
1263 error
= ifnet_set_flags(ifp
, IFF_RUNNING
,
1266 } else if ((ifp
->if_flags
& IFF_RUNNING
) != 0) {
1267 /* marked down, clear running */
1268 error
= ifnet_set_flags(ifp
, 0, IFF_RUNNING
);
1284 feth_if_free(ifnet_t ifp
)
1292 fakeif
= ifnet_get_if_fake(ifp
);
1293 if (fakeif
== NULL
) {
1297 ifp
->if_softc
= NULL
;
1299 feth_release(fakeif
);
1304 __private_extern__
void
1310 error
= if_clone_attach(&feth_cloner
);