2 * Copyright (c) 2015-2020 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");
125 IFF_PP_MODE_GLOBAL
= 0, /* share a global pool */
126 IFF_PP_MODE_PRIVATE
= 1, /* creates its own rx/tx pool */
127 IFF_PP_MODE_PRIVATE_SPLIT
= 2, /* creates its own split rx & tx pool */
128 } iff_pktpool_mode_t
;
129 static iff_pktpool_mode_t if_fake_pktpool_mode
= 0;
130 SYSCTL_INT(_net_link_fake
, OID_AUTO
, pktpool_mode
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
131 &if_fake_pktpool_mode
, 0,
132 "Fake interface packet pool mode (0 global, 1 private, 2 private split");
134 #define FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX 32
135 static int if_fake_link_layer_aggregation_factor
=
136 FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX
;
138 feth_link_layer_aggregation_factor_sysctl SYSCTL_HANDLER_ARGS
140 #pragma unused(oidp, arg1, arg2)
141 unsigned int new_value
;
145 error
= sysctl_io_number(req
, if_fake_link_layer_aggregation_factor
,
146 sizeof(if_fake_link_layer_aggregation_factor
), &new_value
,
148 if (error
== 0 && changed
!= 0) {
149 if (new_value
<= 0 ||
150 new_value
> FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX
) {
153 if_fake_link_layer_aggregation_factor
= new_value
;
158 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, link_layer_aggregation_factor
,
159 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
160 0, 0, feth_link_layer_aggregation_factor_sysctl
, "IU",
161 "Fake interface link layer aggregation factor");
163 #define FETH_TX_HEADROOM_MAX 32
164 static unsigned int if_fake_tx_headroom
= FETH_TX_HEADROOM_MAX
;
166 feth_tx_headroom_sysctl SYSCTL_HANDLER_ARGS
168 #pragma unused(oidp, arg1, arg2)
169 unsigned int new_value
;
173 error
= sysctl_io_number(req
, if_fake_tx_headroom
,
174 sizeof(if_fake_tx_headroom
), &new_value
, &changed
);
175 if (error
== 0 && changed
!= 0) {
176 if (new_value
> FETH_TX_HEADROOM_MAX
||
177 (new_value
% 8) != 0) {
180 if_fake_tx_headroom
= new_value
;
185 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, tx_headroom
,
186 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
187 0, 0, feth_tx_headroom_sysctl
, "IU", "Fake ethernet Tx headroom");
189 static int if_fake_fcs
= 0;
190 SYSCTL_INT(_net_link_fake
, OID_AUTO
, fcs
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
191 &if_fake_fcs
, 0, "Fake interface using frame check sequence");
193 #define FETH_TRAILER_LENGTH_MAX 28
194 char feth_trailer
[FETH_TRAILER_LENGTH_MAX
+ 1] = "trailertrailertrailertrailer";
195 static unsigned int if_fake_trailer_length
= 0;
197 feth_trailer_length_sysctl SYSCTL_HANDLER_ARGS
199 #pragma unused(oidp, arg1, arg2)
200 unsigned int new_value
;
204 error
= sysctl_io_number(req
, if_fake_trailer_length
,
205 sizeof(if_fake_trailer_length
), &new_value
, &changed
);
206 if (error
== 0 && changed
!= 0) {
207 if (new_value
> FETH_TRAILER_LENGTH_MAX
) {
210 if_fake_trailer_length
= new_value
;
215 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, trailer_length
,
216 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0,
217 feth_trailer_length_sysctl
, "IU", "Fake interface frame trailer length");
219 /* sysctl net.link.fake.max_mtu */
220 #define FETH_MAX_MTU_DEFAULT 2048
221 #define FETH_MAX_MTU_MAX ((16 * 1024) - ETHER_HDR_LEN)
223 static unsigned int if_fake_max_mtu
= FETH_MAX_MTU_DEFAULT
;
225 /* sysctl net.link.fake.buflet_size */
226 #define FETH_BUFLET_SIZE_MIN 512
227 #define FETH_BUFLET_SIZE_MAX 2048
229 static unsigned int if_fake_buflet_size
= FETH_BUFLET_SIZE_MIN
;
232 feth_max_mtu_sysctl SYSCTL_HANDLER_ARGS
234 #pragma unused(oidp, arg1, arg2)
235 unsigned int new_value
;
239 error
= sysctl_io_number(req
, if_fake_max_mtu
,
240 sizeof(if_fake_max_mtu
), &new_value
, &changed
);
241 if (error
== 0 && changed
!= 0) {
242 if (new_value
> FETH_MAX_MTU_MAX
||
243 new_value
< ETHERMTU
||
244 new_value
<= if_fake_buflet_size
) {
247 if_fake_max_mtu
= new_value
;
252 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, max_mtu
,
253 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
254 0, 0, feth_max_mtu_sysctl
, "IU", "Fake interface maximum MTU");
257 feth_buflet_size_sysctl SYSCTL_HANDLER_ARGS
259 #pragma unused(oidp, arg1, arg2)
260 unsigned int new_value
;
264 error
= sysctl_io_number(req
, if_fake_buflet_size
,
265 sizeof(if_fake_buflet_size
), &new_value
, &changed
);
266 if (error
== 0 && changed
!= 0) {
267 /* must be a power of 2 between min and max */
268 if (new_value
> FETH_BUFLET_SIZE_MAX
||
269 new_value
< FETH_BUFLET_SIZE_MIN
||
270 !is_power_of_two(new_value
) ||
271 new_value
>= if_fake_max_mtu
) {
274 if_fake_buflet_size
= new_value
;
279 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, buflet_size
,
280 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
281 0, 0, feth_buflet_size_sysctl
, "IU", "Fake interface buflet size");
283 static unsigned int if_fake_user_access
= 0;
286 feth_user_access_sysctl SYSCTL_HANDLER_ARGS
288 #pragma unused(oidp, arg1, arg2)
289 unsigned int new_value
;
293 error
= sysctl_io_number(req
, if_fake_user_access
,
294 sizeof(if_fake_user_access
), &new_value
, &changed
);
295 if (error
== 0 && changed
!= 0) {
296 if (new_value
!= 0) {
297 if (new_value
!= 1) {
301 if_fake_user_access
= new_value
;
306 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, user_access
,
307 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
,
308 0, 0, feth_user_access_sysctl
, "IU", "Fake interface user access");
310 /* sysctl net.link.fake.if_adv_intvl (unit: millisecond) */
311 #define FETH_IF_ADV_INTVL_MIN 10
312 #define FETH_IF_ADV_INTVL_MAX INT_MAX
314 static int if_fake_if_adv_interval
= 0; /* no interface advisory */
316 feth_if_adv_interval_sysctl SYSCTL_HANDLER_ARGS
318 #pragma unused(oidp, arg1, arg2)
319 unsigned int new_value
;
323 error
= sysctl_io_number(req
, if_fake_if_adv_interval
,
324 sizeof(if_fake_if_adv_interval
), &new_value
, &changed
);
325 if (error
== 0 && changed
!= 0) {
326 if ((new_value
!= 0) && (new_value
> FETH_IF_ADV_INTVL_MAX
||
327 new_value
< FETH_IF_ADV_INTVL_MIN
)) {
330 if_fake_if_adv_interval
= new_value
;
335 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, if_adv_intvl
,
336 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0,
337 feth_if_adv_interval_sysctl
, "IU",
338 "Fake interface will generate interface advisories reports at the specified interval in ms");
340 /* sysctl net.link.fake.tx_drops */
342 * Fake ethernet will drop packet on the transmit path at the specified
343 * rate, i.e drop one in every if_fake_tx_drops number of packets.
345 #define FETH_TX_DROPS_MIN 0
346 #define FETH_TX_DROPS_MAX INT_MAX
347 static int if_fake_tx_drops
= 0; /* no packets are dropped */
349 feth_fake_tx_drops_sysctl SYSCTL_HANDLER_ARGS
351 #pragma unused(oidp, arg1, arg2)
352 unsigned int new_value
;
356 error
= sysctl_io_number(req
, if_fake_tx_drops
,
357 sizeof(if_fake_tx_drops
), &new_value
, &changed
);
358 if (error
== 0 && changed
!= 0) {
359 if (new_value
> FETH_TX_DROPS_MAX
||
360 new_value
< FETH_TX_DROPS_MIN
) {
363 if_fake_tx_drops
= new_value
;
368 SYSCTL_PROC(_net_link_fake
, OID_AUTO
, tx_drops
,
369 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0,
370 feth_fake_tx_drops_sysctl
, "IU",
371 "Fake interface will intermittently drop packets on Tx path");
374 ** virtual ethernet structures, types
377 #define IFF_NUM_TX_RINGS_WMM_MODE 4
378 #define IFF_NUM_RX_RINGS_WMM_MODE 1
379 #define IFF_MAX_TX_RINGS IFF_NUM_TX_RINGS_WMM_MODE
380 #define IFF_MAX_RX_RINGS IFF_NUM_RX_RINGS_WMM_MODE
382 #define IFF_MAX_BATCH_SIZE 32
384 typedef uint16_t iff_flags_t
;
385 #define IFF_FLAGS_HWCSUM 0x0001
386 #define IFF_FLAGS_BSD_MODE 0x0002
387 #define IFF_FLAGS_DETACHING 0x0004
388 #define IFF_FLAGS_WMM_MODE 0x0008
389 #define IFF_FLAGS_MULTIBUFLETS 0x0010
393 char iff_name
[IFNAMSIZ
]; /* our unique id */
395 iff_flags_t iff_flags
;
396 uint32_t iff_retain_count
;
397 ifnet_t iff_peer
; /* the other end */
398 int iff_media_current
;
399 int iff_media_active
;
400 uint32_t iff_media_count
;
401 int iff_media_list
[IF_FAKE_MEDIA_LIST_MAX
];
402 struct mbuf
* iff_pending_tx_packet
;
403 boolean_t iff_start_busy
;
404 unsigned int iff_max_mtu
;
406 uint32_t iff_trailer_length
;
409 typedef struct if_fake
* if_fake_ref
;
412 ifnet_get_if_fake(ifnet_t ifp
);
414 #define FETH_DPRINTF(fmt, ...) \
415 { if (if_fake_debug != 0) printf("%s " fmt, __func__, ## __VA_ARGS__); }
417 static inline boolean_t
418 feth_in_bsd_mode(if_fake_ref fakeif
)
420 return (fakeif
->iff_flags
& IFF_FLAGS_BSD_MODE
) != 0;
424 feth_set_detaching(if_fake_ref fakeif
)
426 fakeif
->iff_flags
|= IFF_FLAGS_DETACHING
;
429 static inline boolean_t
430 feth_is_detaching(if_fake_ref fakeif
)
432 return (fakeif
->iff_flags
& IFF_FLAGS_DETACHING
) != 0;
436 feth_enable_dequeue_stall(ifnet_t ifp
, uint32_t enable
)
441 error
= ifnet_disable_output(ifp
);
443 error
= ifnet_enable_output(ifp
);
450 #define FETH_MAXUNIT IF_MAXUNIT
451 #define FETH_ZONE_MAX_ELEM MIN(IFNETS_MAX, FETH_MAXUNIT)
452 #define M_FAKE M_DEVBUF
454 static int feth_clone_create(struct if_clone
*, u_int32_t
, void *);
455 static int feth_clone_destroy(ifnet_t
);
456 static int feth_output(ifnet_t ifp
, struct mbuf
*m
);
457 static void feth_start(ifnet_t ifp
);
458 static int feth_ioctl(ifnet_t ifp
, u_long cmd
, void * addr
);
459 static int feth_config(ifnet_t ifp
, ifnet_t peer
);
460 static void feth_if_free(ifnet_t ifp
);
461 static void feth_ifnet_set_attrs(if_fake_ref fakeif
, ifnet_t ifp
);
462 static void feth_free(if_fake_ref fakeif
);
464 static struct if_clone
465 feth_cloner
= IF_CLONE_INITIALIZER(FAKE_ETHER_NAME
,
471 sizeof(struct if_fake
));
472 static void interface_link_event(ifnet_t ifp
, u_int32_t event_code
);
474 /* some media words to pretend to be ethernet */
475 static int default_media_words
[] = {
476 IFM_MAKEWORD(IFM_ETHER
, 0, 0, 0),
477 IFM_MAKEWORD(IFM_ETHER
, IFM_10G_T
, IFM_FDX
, 0),
478 IFM_MAKEWORD(IFM_ETHER
, IFM_2500_T
, IFM_FDX
, 0),
479 IFM_MAKEWORD(IFM_ETHER
, IFM_5000_T
, IFM_FDX
, 0),
481 IFM_MAKEWORD(IFM_ETHER
, IFM_10G_KX4
, IFM_FDX
, 0),
482 IFM_MAKEWORD(IFM_ETHER
, IFM_20G_KR2
, IFM_FDX
, 0),
483 IFM_MAKEWORD(IFM_ETHER
, IFM_2500_SX
, IFM_FDX
, 0),
484 IFM_MAKEWORD(IFM_ETHER
, IFM_25G_KR
, IFM_FDX
, 0),
485 IFM_MAKEWORD(IFM_ETHER
, IFM_40G_SR4
, IFM_FDX
, 0),
486 IFM_MAKEWORD(IFM_ETHER
, IFM_50G_CR2
, IFM_FDX
, 0),
487 IFM_MAKEWORD(IFM_ETHER
, IFM_56G_R4
, IFM_FDX
, 0),
488 IFM_MAKEWORD(IFM_ETHER
, IFM_100G_CR4
, IFM_FDX
, 0),
489 IFM_MAKEWORD(IFM_ETHER
, IFM_400G_AUI8
, IFM_FDX
, 0),
491 #define default_media_words_count (sizeof(default_media_words) \
492 / sizeof (default_media_words[0]))
497 static inline lck_grp_t
*
498 my_lck_grp_alloc_init(const char * grp_name
)
501 lck_grp_attr_t
* grp_attrs
;
503 grp_attrs
= lck_grp_attr_alloc_init();
504 grp
= lck_grp_alloc_init(grp_name
, grp_attrs
);
505 lck_grp_attr_free(grp_attrs
);
509 static inline lck_mtx_t
*
510 my_lck_mtx_alloc_init(lck_grp_t
* lck_grp
)
512 lck_attr_t
* lck_attrs
;
515 lck_attrs
= lck_attr_alloc_init();
516 lck_mtx
= lck_mtx_alloc_init(lck_grp
, lck_attrs
);
517 lck_attr_free(lck_attrs
);
521 static lck_mtx_t
* feth_lck_mtx
;
526 lck_grp_t
* feth_lck_grp
;
528 feth_lck_grp
= my_lck_grp_alloc_init("fake");
529 feth_lck_mtx
= my_lck_mtx_alloc_init(feth_lck_grp
);
534 feth_assert_lock_held(void)
536 LCK_MTX_ASSERT(feth_lck_mtx
, LCK_MTX_ASSERT_OWNED
);
541 feth_assert_lock_not_held(void)
543 LCK_MTX_ASSERT(feth_lck_mtx
, LCK_MTX_ASSERT_NOTOWNED
);
551 lck_mtx_lock(feth_lck_mtx
);
558 lck_mtx_unlock(feth_lck_mtx
);
563 get_max_mtu(int bsd_mode
, unsigned int max_mtu
)
568 mtu
= (njcl
> 0) ? (M16KCLBYTES
- ETHER_HDR_LEN
)
569 : MBIGCLBYTES
- ETHER_HDR_LEN
;
579 static inline unsigned int
580 feth_max_mtu(ifnet_t ifp
)
583 unsigned int max_mtu
= ETHERMTU
;
586 fakeif
= ifnet_get_if_fake(ifp
);
587 if (fakeif
!= NULL
) {
588 max_mtu
= fakeif
->iff_max_mtu
;
595 feth_free(if_fake_ref fakeif
)
597 VERIFY(fakeif
->iff_retain_count
== 0);
598 if (feth_in_bsd_mode(fakeif
)) {
599 if (fakeif
->iff_pending_tx_packet
) {
600 m_freem(fakeif
->iff_pending_tx_packet
);
604 FETH_DPRINTF("%s\n", fakeif
->iff_name
);
605 if_clone_softc_deallocate(&feth_cloner
, fakeif
);
609 feth_release(if_fake_ref fakeif
)
611 u_int32_t old_retain_count
;
613 old_retain_count
= OSDecrementAtomic(&fakeif
->iff_retain_count
);
614 switch (old_retain_count
) {
616 VERIFY(old_retain_count
!= 0);
629 ** feth interface routines
632 feth_ifnet_set_attrs(if_fake_ref fakeif
, ifnet_t ifp
)
634 (void)ifnet_set_capabilities_enabled(ifp
, 0, -1);
635 ifnet_set_addrlen(ifp
, ETHER_ADDR_LEN
);
636 ifnet_set_baudrate(ifp
, 0);
637 ifnet_set_mtu(ifp
, ETHERMTU
);
639 IFF_BROADCAST
| IFF_MULTICAST
| IFF_SIMPLEX
,
641 ifnet_set_hdrlen(ifp
, sizeof(struct ether_header
));
642 if ((fakeif
->iff_flags
& IFF_FLAGS_HWCSUM
) != 0) {
643 ifnet_set_offload(ifp
,
644 IFNET_CSUM_IP
| IFNET_CSUM_TCP
| IFNET_CSUM_UDP
|
645 IFNET_CSUM_TCPIPV6
| IFNET_CSUM_UDPIPV6
);
647 ifnet_set_offload(ifp
, 0);
652 interface_link_event(ifnet_t ifp
, u_int32_t event_code
)
655 u_int32_t ifnet_family
;
657 char if_name
[IFNAMSIZ
];
659 _Alignas(struct kern_event_msg
) char message
[sizeof(struct kern_event_msg
) + sizeof(struct event
)] = { 0 };
660 struct kern_event_msg
*header
= (struct kern_event_msg
*)message
;
661 struct event
*data
= (struct event
*)(header
+ 1);
663 header
->total_size
= sizeof(message
);
664 header
->vendor_code
= KEV_VENDOR_APPLE
;
665 header
->kev_class
= KEV_NETWORK_CLASS
;
666 header
->kev_subclass
= KEV_DL_SUBCLASS
;
667 header
->event_code
= event_code
;
668 data
->ifnet_family
= ifnet_family(ifp
);
669 data
->unit
= (u_int32_t
)ifnet_unit(ifp
);
670 strlcpy(data
->if_name
, ifnet_name(ifp
), IFNAMSIZ
);
671 ifnet_event(ifp
, header
);
675 ifnet_get_if_fake(ifnet_t ifp
)
677 return (if_fake_ref
)ifnet_softc(ifp
);
681 feth_clone_create(struct if_clone
*ifc
, u_int32_t unit
, __unused
void *params
)
685 struct ifnet_init_eparams feth_init
;
687 uint8_t mac_address
[ETHER_ADDR_LEN
];
689 fakeif
= if_clone_softc_allocate(&feth_cloner
);
690 if (fakeif
== NULL
) {
693 fakeif
->iff_retain_count
= 1;
694 #define FAKE_ETHER_NAME_LEN (sizeof(FAKE_ETHER_NAME) - 1)
695 _CASSERT(FAKE_ETHER_NAME_LEN
== 4);
696 bcopy(FAKE_ETHER_NAME
, mac_address
, FAKE_ETHER_NAME_LEN
);
697 mac_address
[ETHER_ADDR_LEN
- 2] = (unit
& 0xff00) >> 8;
698 mac_address
[ETHER_ADDR_LEN
- 1] = unit
& 0xff;
699 if (if_fake_bsd_mode
!= 0) {
700 fakeif
->iff_flags
|= IFF_FLAGS_BSD_MODE
;
702 if (if_fake_hwcsum
!= 0) {
703 fakeif
->iff_flags
|= IFF_FLAGS_HWCSUM
;
705 fakeif
->iff_max_mtu
= get_max_mtu(if_fake_bsd_mode
, if_fake_max_mtu
);
706 fakeif
->iff_fcs
= if_fake_fcs
;
707 fakeif
->iff_trailer_length
= if_fake_trailer_length
;
709 /* use the interface name as the unique id for ifp recycle */
711 snprintf(fakeif
->iff_name
, sizeof(fakeif
->iff_name
), "%s%d",
712 ifc
->ifc_name
, unit
) >= sizeof(fakeif
->iff_name
)) {
713 feth_release(fakeif
);
716 bzero(&feth_init
, sizeof(feth_init
));
717 feth_init
.ver
= IFNET_INIT_CURRENT_VERSION
;
718 feth_init
.len
= sizeof(feth_init
);
719 if (feth_in_bsd_mode(fakeif
)) {
720 if (if_fake_txstart
!= 0) {
721 feth_init
.start
= feth_start
;
723 feth_init
.flags
|= IFNET_INIT_LEGACY
;
724 feth_init
.output
= feth_output
;
727 if (if_fake_nxattach
== 0) {
728 feth_init
.flags
|= IFNET_INIT_NX_NOAUTO
;
730 feth_init
.uniqueid
= fakeif
->iff_name
;
731 feth_init
.uniqueid_len
= strlen(fakeif
->iff_name
);
732 feth_init
.name
= ifc
->ifc_name
;
733 feth_init
.unit
= unit
;
734 feth_init
.family
= IFNET_FAMILY_ETHERNET
;
735 feth_init
.type
= IFT_ETHER
;
736 feth_init
.demux
= ether_demux
;
737 feth_init
.add_proto
= ether_add_proto
;
738 feth_init
.del_proto
= ether_del_proto
;
739 feth_init
.check_multi
= ether_check_multi
;
740 feth_init
.framer_extended
= ether_frameout_extended
;
741 feth_init
.softc
= fakeif
;
742 feth_init
.ioctl
= feth_ioctl
;
743 feth_init
.set_bpf_tap
= NULL
;
744 feth_init
.detach
= feth_if_free
;
745 feth_init
.broadcast_addr
= etherbroadcastaddr
;
746 feth_init
.broadcast_len
= ETHER_ADDR_LEN
;
747 if (feth_in_bsd_mode(fakeif
)) {
748 error
= ifnet_allocate_extended(&feth_init
, &ifp
);
750 feth_release(fakeif
);
753 feth_ifnet_set_attrs(fakeif
, ifp
);
755 fakeif
->iff_media_count
= MIN(default_media_words_count
, IF_FAKE_MEDIA_LIST_MAX
);
756 bcopy(default_media_words
, fakeif
->iff_media_list
,
757 fakeif
->iff_media_count
* sizeof(fakeif
->iff_media_list
[0]));
758 if (feth_in_bsd_mode(fakeif
)) {
759 error
= ifnet_attach(ifp
, NULL
);
762 feth_release(fakeif
);
765 fakeif
->iff_ifp
= ifp
;
768 ifnet_set_lladdr(ifp
, mac_address
, sizeof(mac_address
));
770 /* attach as ethernet */
771 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
776 feth_clone_destroy(ifnet_t ifp
)
781 fakeif
= ifnet_get_if_fake(ifp
);
782 if (fakeif
== NULL
|| feth_is_detaching(fakeif
)) {
786 feth_set_detaching(fakeif
);
789 feth_config(ifp
, NULL
);
795 feth_enqueue_input(ifnet_t ifp
, struct mbuf
* m
)
797 struct ifnet_stat_increment_param stats
= {};
799 stats
.packets_in
= 1;
800 stats
.bytes_in
= (uint32_t)mbuf_pkthdr_len(m
) + ETHER_HDR_LEN
;
801 ifnet_input(ifp
, m
, &stats
);
805 copy_mbuf(struct mbuf
*m
)
807 struct mbuf
* copy_m
;
811 if ((m
->m_flags
& M_PKTHDR
) == 0) {
814 pkt_len
= m
->m_pkthdr
.len
;
815 MGETHDR(copy_m
, M_DONTWAIT
, MT_DATA
);
816 if (copy_m
== NULL
) {
819 if (pkt_len
> MHLEN
) {
820 if (pkt_len
<= MCLBYTES
) {
821 MCLGET(copy_m
, M_DONTWAIT
);
822 } else if (pkt_len
<= MBIGCLBYTES
) {
823 copy_m
= m_mbigget(copy_m
, M_DONTWAIT
);
824 } else if (pkt_len
<= M16KCLBYTES
&& njcl
> 0) {
825 copy_m
= m_m16kget(copy_m
, M_DONTWAIT
);
827 printf("if_fake: copy_mbuf(): packet too large %d\n",
831 if (copy_m
== NULL
|| (copy_m
->m_flags
& M_EXT
) == 0) {
835 mbuf_setlen(copy_m
, pkt_len
);
836 copy_m
->m_pkthdr
.len
= pkt_len
;
837 copy_m
->m_pkthdr
.pkt_svc
= m
->m_pkthdr
.pkt_svc
;
839 while (m
!= NULL
&& offset
< pkt_len
) {
843 if (frag_len
> (pkt_len
- offset
)) {
844 printf("if_fake_: Large mbuf fragment %d > %d\n",
845 frag_len
, (pkt_len
- offset
));
848 m_copydata(m
, 0, frag_len
, mtod(copy_m
, void *) + offset
);
855 if (copy_m
!= NULL
) {
862 feth_add_mbuf_trailer(struct mbuf
*m
, void *trailer
, size_t trailer_len
)
865 ASSERT(trailer_len
<= FETH_TRAILER_LENGTH_MAX
);
867 ret
= m_append(m
, trailer_len
, (caddr_t
)trailer
);
869 FETH_DPRINTF("%s %zuB trailer added\n", __func__
, trailer_len
);
872 printf("%s m_append failed\n", __func__
);
877 feth_add_mbuf_fcs(struct mbuf
*m
)
879 uint32_t pkt_len
, offset
= 0;
883 ASSERT(sizeof(crc
) == ETHER_CRC_LEN
);
885 pkt_len
= m
->m_pkthdr
.len
;
886 struct mbuf
*iter
= m
;
887 while (iter
!= NULL
&& offset
< pkt_len
) {
888 uint32_t frag_len
= iter
->m_len
;
889 ASSERT(frag_len
<= (pkt_len
- offset
));
890 crc
= crc32(crc
, mtod(iter
, void *), frag_len
);
895 err
= feth_add_mbuf_trailer(m
, &crc
, ETHER_CRC_LEN
);
900 m
->m_flags
|= M_HASFCS
;
906 feth_output_common(ifnet_t ifp
, struct mbuf
* m
, ifnet_t peer
,
907 iff_flags_t flags
, bool fcs
, void *trailer
, size_t trailer_len
)
911 frame_header
= mbuf_data(m
);
912 if ((flags
& IFF_FLAGS_HWCSUM
) != 0) {
913 m
->m_pkthdr
.csum_data
= 0xffff;
914 m
->m_pkthdr
.csum_flags
=
915 CSUM_DATA_VALID
| CSUM_PSEUDO_HDR
|
916 CSUM_IP_CHECKED
| CSUM_IP_VALID
;
919 (void)ifnet_stat_increment_out(ifp
, 1, m
->m_pkthdr
.len
, 0);
920 bpf_tap_out(ifp
, DLT_EN10MB
, m
, NULL
, 0);
923 feth_add_mbuf_trailer(m
, trailer
, trailer_len
);
926 feth_add_mbuf_fcs(m
);
929 (void)mbuf_pkthdr_setrcvif(m
, peer
);
930 mbuf_pkthdr_setheader(m
, frame_header
);
931 mbuf_pkthdr_adjustlen(m
, -ETHER_HDR_LEN
);
932 (void)mbuf_setdata(m
, (char *)mbuf_data(m
) + ETHER_HDR_LEN
,
933 mbuf_len(m
) - ETHER_HDR_LEN
);
934 bpf_tap_in(peer
, DLT_EN10MB
, m
, frame_header
,
935 sizeof(struct ether_header
));
936 feth_enqueue_input(peer
, m
);
940 feth_start(ifnet_t ifp
)
942 struct mbuf
* copy_m
= NULL
;
944 iff_flags_t flags
= 0;
949 struct mbuf
* save_m
;
952 fakeif
= ifnet_get_if_fake(ifp
);
953 if (fakeif
== NULL
) {
958 if (fakeif
->iff_start_busy
) {
960 printf("if_fake: start is busy\n");
964 peer
= fakeif
->iff_peer
;
965 flags
= fakeif
->iff_flags
;
966 fcs
= fakeif
->iff_fcs
;
967 trailer_len
= fakeif
->iff_trailer_length
;
969 /* check for pending TX */
970 m
= fakeif
->iff_pending_tx_packet
;
973 copy_m
= copy_mbuf(m
);
974 if (copy_m
== NULL
) {
979 fakeif
->iff_pending_tx_packet
= NULL
;
983 fakeif
->iff_start_busy
= TRUE
;
987 if (copy_m
!= NULL
) {
988 VERIFY(peer
!= NULL
);
989 feth_output_common(ifp
, copy_m
, peer
, flags
, fcs
,
990 feth_trailer
, trailer_len
);
993 if (ifnet_dequeue(ifp
, &m
) != 0) {
999 copy_m
= copy_mbuf(m
);
1000 if (copy_m
== NULL
) {
1009 fakeif
= ifnet_get_if_fake(ifp
);
1010 if (fakeif
!= NULL
) {
1011 fakeif
->iff_start_busy
= FALSE
;
1012 if (save_m
!= NULL
&& fakeif
->iff_peer
!= NULL
) {
1013 /* save it for next time */
1014 fakeif
->iff_pending_tx_packet
= save_m
;
1019 if (save_m
!= NULL
) {
1020 /* didn't save packet, so free it */
1026 feth_output(ifnet_t ifp
, struct mbuf
* m
)
1028 struct mbuf
* copy_m
;
1033 ifnet_t peer
= NULL
;
1038 copy_m
= copy_mbuf(m
);
1041 if (copy_m
== NULL
) {
1042 /* count this as an output error */
1043 ifnet_stat_increment_out(ifp
, 0, 0, 1);
1047 fakeif
= ifnet_get_if_fake(ifp
);
1048 if (fakeif
!= NULL
) {
1049 peer
= fakeif
->iff_peer
;
1050 flags
= fakeif
->iff_flags
;
1051 fcs
= fakeif
->iff_fcs
;
1052 trailer_len
= fakeif
->iff_trailer_length
;
1057 ifnet_stat_increment_out(ifp
, 0, 0, 1);
1060 feth_output_common(ifp
, copy_m
, peer
, flags
, fcs
, feth_trailer
,
1066 feth_config(ifnet_t ifp
, ifnet_t peer
)
1068 int connected
= FALSE
;
1069 int disconnected
= FALSE
;
1071 if_fake_ref fakeif
= NULL
;
1074 fakeif
= ifnet_get_if_fake(ifp
);
1075 if (fakeif
== NULL
) {
1080 /* connect to peer */
1081 if_fake_ref peer_fakeif
;
1083 peer_fakeif
= ifnet_get_if_fake(peer
);
1084 if (peer_fakeif
== NULL
) {
1088 if (feth_is_detaching(fakeif
) ||
1089 feth_is_detaching(peer_fakeif
) ||
1090 peer_fakeif
->iff_peer
!= NULL
||
1091 fakeif
->iff_peer
!= NULL
) {
1095 fakeif
->iff_peer
= peer
;
1096 peer_fakeif
->iff_peer
= ifp
;
1098 } else if (fakeif
->iff_peer
!= NULL
) {
1099 /* disconnect from peer */
1100 if_fake_ref peer_fakeif
;
1102 peer
= fakeif
->iff_peer
;
1103 peer_fakeif
= ifnet_get_if_fake(peer
);
1104 if (peer_fakeif
== NULL
) {
1105 /* should not happen */
1109 fakeif
->iff_peer
= NULL
;
1110 peer_fakeif
->iff_peer
= NULL
;
1111 disconnected
= TRUE
;
1117 /* generate link status event if we connect or disconnect */
1119 interface_link_event(ifp
, KEV_DL_LINK_ON
);
1120 interface_link_event(peer
, KEV_DL_LINK_ON
);
1121 } else if (disconnected
) {
1122 interface_link_event(ifp
, KEV_DL_LINK_OFF
);
1123 interface_link_event(peer
, KEV_DL_LINK_OFF
);
1129 feth_set_media(ifnet_t ifp
, struct if_fake_request
* iffr
)
1134 if (iffr
->iffr_media
.iffm_count
> IF_FAKE_MEDIA_LIST_MAX
) {
1135 /* list is too long */
1139 fakeif
= ifnet_get_if_fake(ifp
);
1140 if (fakeif
== NULL
) {
1144 fakeif
->iff_media_count
= iffr
->iffr_media
.iffm_count
;
1145 bcopy(iffr
->iffr_media
.iffm_list
, fakeif
->iff_media_list
,
1146 iffr
->iffr_media
.iffm_count
* sizeof(fakeif
->iff_media_list
[0]));
1148 /* XXX: "auto-negotiate" active with peer? */
1149 /* generate link status event? */
1150 fakeif
->iff_media_current
= iffr
->iffr_media
.iffm_current
;
1159 if_fake_request_copyin(user_addr_t user_addr
,
1160 struct if_fake_request
*iffr
, u_int32_t len
)
1164 if (user_addr
== USER_ADDR_NULL
|| len
< sizeof(*iffr
)) {
1168 error
= copyin(user_addr
, iffr
, sizeof(*iffr
));
1172 if (iffr
->iffr_reserved
[0] != 0 || iffr
->iffr_reserved
[1] != 0 ||
1173 iffr
->iffr_reserved
[2] != 0 || iffr
->iffr_reserved
[3] != 0) {
1182 feth_set_drvspec(ifnet_t ifp
, uint32_t cmd
, u_int32_t len
,
1183 user_addr_t user_addr
)
1186 struct if_fake_request iffr
;
1190 case IF_FAKE_S_CMD_SET_PEER
:
1191 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1195 if (iffr
.iffr_peer_name
[0] == '\0') {
1196 error
= feth_config(ifp
, NULL
);
1200 /* ensure nul termination */
1201 iffr
.iffr_peer_name
[IFNAMSIZ
- 1] = '\0';
1202 peer
= ifunit(iffr
.iffr_peer_name
);
1207 if (ifnet_type(peer
) != IFT_ETHER
) {
1211 if (strcmp(ifnet_name(peer
), FAKE_ETHER_NAME
) != 0) {
1215 error
= feth_config(ifp
, peer
);
1217 case IF_FAKE_S_CMD_SET_MEDIA
:
1218 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1222 error
= feth_set_media(ifp
, &iffr
);
1224 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL
:
1225 error
= if_fake_request_copyin(user_addr
, &iffr
, len
);
1229 error
= feth_enable_dequeue_stall(ifp
,
1230 iffr
.iffr_dequeue_stall
);
1240 feth_get_drvspec(ifnet_t ifp
, u_int32_t cmd
, u_int32_t len
,
1241 user_addr_t user_addr
)
1243 int error
= EOPNOTSUPP
;
1245 struct if_fake_request iffr
;
1249 case IF_FAKE_G_CMD_GET_PEER
:
1250 if (len
< sizeof(iffr
)) {
1255 fakeif
= ifnet_get_if_fake(ifp
);
1256 if (fakeif
== NULL
) {
1261 peer
= fakeif
->iff_peer
;
1263 bzero(&iffr
, sizeof(iffr
));
1265 strlcpy(iffr
.iffr_peer_name
,
1267 sizeof(iffr
.iffr_peer_name
));
1269 error
= copyout(&iffr
, user_addr
, sizeof(iffr
));
1278 struct ifdrv32
*ifdrvu_32
;
1279 struct ifdrv64
*ifdrvu_64
;
1284 feth_ioctl(ifnet_t ifp
, u_long cmd
, void * data
)
1287 struct ifdevmtu
* devmtu_p
;
1291 boolean_t drv_set_command
= FALSE
;
1293 struct ifmediareq
* ifmr
;
1297 user_addr_t user_addr
;
1299 ifr
= (struct ifreq
*)data
;
1302 ifnet_set_flags(ifp
, IFF_UP
, IFF_UP
);
1305 case SIOCGIFMEDIA32
:
1306 case SIOCGIFMEDIA64
:
1308 fakeif
= ifnet_get_if_fake(ifp
);
1309 if (fakeif
== NULL
) {
1313 status
= (fakeif
->iff_peer
!= NULL
)
1314 ? (IFM_AVALID
| IFM_ACTIVE
) : IFM_AVALID
;
1315 ifmr
= (struct ifmediareq
*)data
;
1316 user_addr
= (cmd
== SIOCGIFMEDIA64
) ?
1317 ((struct ifmediareq64
*)ifmr
)->ifmu_ulist
:
1318 CAST_USER_ADDR_T(((struct ifmediareq32
*)ifmr
)->ifmu_ulist
);
1319 count
= ifmr
->ifm_count
;
1320 ifmr
->ifm_active
= IFM_ETHER
;
1321 ifmr
->ifm_current
= IFM_ETHER
;
1323 ifmr
->ifm_status
= status
;
1324 if (user_addr
== USER_ADDR_NULL
) {
1325 ifmr
->ifm_count
= fakeif
->iff_media_count
;
1326 } else if (count
> 0) {
1327 if (count
> fakeif
->iff_media_count
) {
1328 count
= fakeif
->iff_media_count
;
1330 ifmr
->ifm_count
= count
;
1331 error
= copyout(&fakeif
->iff_media_list
, user_addr
,
1332 count
* sizeof(int));
1338 devmtu_p
= &ifr
->ifr_devmtu
;
1339 devmtu_p
->ifdm_current
= ifnet_mtu(ifp
);
1340 devmtu_p
->ifdm_max
= feth_max_mtu(ifp
);
1341 devmtu_p
->ifdm_min
= IF_MINMTU
;
1345 if ((unsigned int)ifr
->ifr_mtu
> feth_max_mtu(ifp
) ||
1346 ifr
->ifr_mtu
< IF_MINMTU
) {
1349 error
= ifnet_set_mtu(ifp
, ifr
->ifr_mtu
);
1353 case SIOCSDRVSPEC32
:
1354 case SIOCSDRVSPEC64
:
1355 error
= proc_suser(current_proc());
1359 drv_set_command
= TRUE
;
1361 case SIOCGDRVSPEC32
:
1362 case SIOCGDRVSPEC64
:
1363 drv
.ifdrvu_p
= data
;
1364 if (cmd
== SIOCGDRVSPEC32
|| cmd
== SIOCSDRVSPEC32
) {
1365 drv_cmd
= drv
.ifdrvu_32
->ifd_cmd
;
1366 drv_len
= drv
.ifdrvu_32
->ifd_len
;
1367 user_addr
= CAST_USER_ADDR_T(drv
.ifdrvu_32
->ifd_data
);
1369 drv_cmd
= drv
.ifdrvu_64
->ifd_cmd
;
1370 drv_len
= drv
.ifdrvu_64
->ifd_len
;
1371 user_addr
= drv
.ifdrvu_64
->ifd_data
;
1373 if (drv_set_command
) {
1374 error
= feth_set_drvspec(ifp
, drv_cmd
, drv_len
,
1377 error
= feth_get_drvspec(ifp
, drv_cmd
, drv_len
,
1383 error
= ifnet_set_lladdr(ifp
, ifr
->ifr_addr
.sa_data
,
1384 ifr
->ifr_addr
.sa_len
);
1388 if ((ifp
->if_flags
& IFF_UP
) != 0) {
1389 /* marked up, set running if not already set */
1390 if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
1392 error
= ifnet_set_flags(ifp
, IFF_RUNNING
,
1395 } else if ((ifp
->if_flags
& IFF_RUNNING
) != 0) {
1396 /* marked down, clear running */
1397 error
= ifnet_set_flags(ifp
, 0, IFF_RUNNING
);
1413 feth_if_free(ifnet_t ifp
)
1421 fakeif
= ifnet_get_if_fake(ifp
);
1422 if (fakeif
== NULL
) {
1426 ifp
->if_softc
= NULL
;
1428 feth_release(fakeif
);
1433 __private_extern__
void
1439 error
= if_clone_attach(&feth_cloner
);