]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_fake.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / net / if_fake.c
CommitLineData
5ba3f43e 1/*
f427ee49 2 * Copyright (c) 2015-2020 Apple Inc. All rights reserved.
5ba3f43e
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
5ba3f43e
A
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.
0a7de745 14 *
5ba3f43e
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
5ba3f43e
A
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.
0a7de745 25 *
5ba3f43e
A
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>
d9a64523 74#include <kern/zalloc.h>
5ba3f43e
A
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
cb323159
A
84static boolean_t
85is_power_of_two(unsigned int val)
86{
87 return (val & (val - 1)) == 0;
88}
89
0a7de745 90#define FAKE_ETHER_NAME "feth"
5ba3f43e
A
91
92SYSCTL_DECL(_net_link);
0a7de745
A
93SYSCTL_NODE(_net_link, OID_AUTO, fake, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
94 "Fake interface");
5ba3f43e
A
95
96static int if_fake_txstart = 1;
97SYSCTL_INT(_net_link_fake, OID_AUTO, txstart, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 98 &if_fake_txstart, 0, "Fake interface TXSTART mode");
5ba3f43e
A
99
100static int if_fake_hwcsum = 0;
101SYSCTL_INT(_net_link_fake, OID_AUTO, hwcsum, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 102 &if_fake_hwcsum, 0, "Fake interface simulate hardware checksum");
5ba3f43e
A
103
104static int if_fake_nxattach = 0;
105SYSCTL_INT(_net_link_fake, OID_AUTO, nxattach, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 106 &if_fake_nxattach, 0, "Fake interface auto-attach nexus");
5ba3f43e
A
107
108static int if_fake_bsd_mode = 1;
109SYSCTL_INT(_net_link_fake, OID_AUTO, bsd_mode, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 110 &if_fake_bsd_mode, 0, "Fake interface attach as BSD interface");
5ba3f43e
A
111
112static int if_fake_debug = 0;
113SYSCTL_INT(_net_link_fake, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 114 &if_fake_debug, 0, "Fake interface debug logs");
5ba3f43e 115
d9a64523
A
116static int if_fake_wmm_mode = 0;
117SYSCTL_INT(_net_link_fake, OID_AUTO, wmm_mode, CTLFLAG_RW | CTLFLAG_LOCKED,
0a7de745 118 &if_fake_wmm_mode, 0, "Fake interface in 802.11 WMM mode");
d9a64523 119
cb323159
A
120static int if_fake_multibuflet = 0;
121SYSCTL_INT(_net_link_fake, OID_AUTO, multibuflet, CTLFLAG_RW | CTLFLAG_LOCKED,
122 &if_fake_multibuflet, 0, "Fake interface using multi-buflet packets");
123
f427ee49
A
124typedef enum {
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;
129static iff_pktpool_mode_t if_fake_pktpool_mode = 0;
130SYSCTL_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");
133
134#define FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX 32
135static int if_fake_link_layer_aggregation_factor =
136 FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX;
137static int
138feth_link_layer_aggregation_factor_sysctl SYSCTL_HANDLER_ARGS
139{
140#pragma unused(oidp, arg1, arg2)
141 unsigned int new_value;
142 int changed;
143 int error;
cb323159 144
f427ee49
A
145 error = sysctl_io_number(req, if_fake_link_layer_aggregation_factor,
146 sizeof(if_fake_link_layer_aggregation_factor), &new_value,
147 &changed);
148 if (error == 0 && changed != 0) {
149 if (new_value <= 0 ||
150 new_value > FETH_LINK_LAYER_AGGRETATION_FACTOR_MAX) {
151 return EINVAL;
152 }
153 if_fake_link_layer_aggregation_factor = new_value;
154 }
155 return error;
156}
cb323159 157
f427ee49
A
158SYSCTL_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");
162
163#define FETH_TX_HEADROOM_MAX 32
164static unsigned int if_fake_tx_headroom = FETH_TX_HEADROOM_MAX;
cb323159
A
165static int
166feth_tx_headroom_sysctl SYSCTL_HANDLER_ARGS
167{
168#pragma unused(oidp, arg1, arg2)
169 unsigned int new_value;
170 int changed;
171 int error;
172
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) {
178 return EINVAL;
179 }
180 if_fake_tx_headroom = new_value;
181 }
182 return 0;
183}
184
185SYSCTL_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");
188
f427ee49
A
189static int if_fake_fcs = 0;
190SYSCTL_INT(_net_link_fake, OID_AUTO, fcs, CTLFLAG_RW | CTLFLAG_LOCKED,
191 &if_fake_fcs, 0, "Fake interface using frame check sequence");
192
193#define FETH_TRAILER_LENGTH_MAX 28
194char feth_trailer[FETH_TRAILER_LENGTH_MAX + 1] = "trailertrailertrailertrailer";
195static unsigned int if_fake_trailer_length = 0;
196static int
197feth_trailer_length_sysctl SYSCTL_HANDLER_ARGS
198{
199#pragma unused(oidp, arg1, arg2)
200 unsigned int new_value;
201 int changed;
202 int error;
203
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) {
208 return EINVAL;
209 }
210 if_fake_trailer_length = new_value;
211 }
212 return 0;
213}
214
215SYSCTL_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");
cb323159
A
218
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)
222
223static unsigned int if_fake_max_mtu = FETH_MAX_MTU_DEFAULT;
224
225/* sysctl net.link.fake.buflet_size */
226#define FETH_BUFLET_SIZE_MIN 512
227#define FETH_BUFLET_SIZE_MAX 2048
228
229static unsigned int if_fake_buflet_size = FETH_BUFLET_SIZE_MIN;
230
231static int
232feth_max_mtu_sysctl SYSCTL_HANDLER_ARGS
233{
234#pragma unused(oidp, arg1, arg2)
235 unsigned int new_value;
236 int changed;
237 int error;
238
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) {
245 return EINVAL;
246 }
247 if_fake_max_mtu = new_value;
248 }
249 return 0;
250}
251
252SYSCTL_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");
255
256static int
257feth_buflet_size_sysctl SYSCTL_HANDLER_ARGS
258{
259#pragma unused(oidp, arg1, arg2)
260 unsigned int new_value;
261 int changed;
262 int error;
263
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) {
272 return EINVAL;
273 }
274 if_fake_buflet_size = new_value;
275 }
276 return 0;
277}
278
279SYSCTL_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");
282
283static unsigned int if_fake_user_access = 0;
284
285static int
286feth_user_access_sysctl SYSCTL_HANDLER_ARGS
287{
288#pragma unused(oidp, arg1, arg2)
289 unsigned int new_value;
290 int changed;
291 int error;
292
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) {
298 return EINVAL;
299 }
cb323159
A
300 }
301 if_fake_user_access = new_value;
302 }
303 return 0;
304}
305
306SYSCTL_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");
309
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
313
314static int if_fake_if_adv_interval = 0; /* no interface advisory */
315static int
316feth_if_adv_interval_sysctl SYSCTL_HANDLER_ARGS
317{
318#pragma unused(oidp, arg1, arg2)
319 unsigned int new_value;
320 int changed;
321 int error;
322
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)) {
328 return EINVAL;
329 }
330 if_fake_if_adv_interval = new_value;
331 }
332 return 0;
333}
334
335SYSCTL_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");
339
340/* sysctl net.link.fake.tx_drops */
341/*
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.
344 */
345#define FETH_TX_DROPS_MIN 0
346#define FETH_TX_DROPS_MAX INT_MAX
347static int if_fake_tx_drops = 0; /* no packets are dropped */
348static int
349feth_fake_tx_drops_sysctl SYSCTL_HANDLER_ARGS
350{
351#pragma unused(oidp, arg1, arg2)
352 unsigned int new_value;
353 int changed;
354 int error;
355
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) {
361 return EINVAL;
362 }
363 if_fake_tx_drops = new_value;
364 }
365 return 0;
366}
367
368SYSCTL_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");
372
5ba3f43e 373/**
0a7de745
A
374** virtual ethernet structures, types
375**/
5ba3f43e 376
0a7de745
A
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
d9a64523 381
f427ee49
A
382#define IFF_MAX_BATCH_SIZE 32
383
0a7de745
A
384typedef 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
cb323159 389#define IFF_FLAGS_MULTIBUFLETS 0x0010
5ba3f43e
A
390
391
392struct if_fake {
0a7de745
A
393 char iff_name[IFNAMSIZ]; /* our unique id */
394 ifnet_t iff_ifp;
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;
cb323159 404 unsigned int iff_max_mtu;
f427ee49
A
405 uint32_t iff_fcs;
406 uint32_t iff_trailer_length;
5ba3f43e
A
407};
408
409typedef struct if_fake * if_fake_ref;
410
411static if_fake_ref
412ifnet_get_if_fake(ifnet_t ifp);
413
0a7de745 414#define FETH_DPRINTF(fmt, ...) \
5ba3f43e
A
415 { if (if_fake_debug != 0) printf("%s " fmt, __func__, ## __VA_ARGS__); }
416
417static inline boolean_t
418feth_in_bsd_mode(if_fake_ref fakeif)
419{
0a7de745 420 return (fakeif->iff_flags & IFF_FLAGS_BSD_MODE) != 0;
5ba3f43e
A
421}
422
423static inline void
424feth_set_detaching(if_fake_ref fakeif)
425{
426 fakeif->iff_flags |= IFF_FLAGS_DETACHING;
427}
428
429static inline boolean_t
430feth_is_detaching(if_fake_ref fakeif)
431{
0a7de745 432 return (fakeif->iff_flags & IFF_FLAGS_DETACHING) != 0;
5ba3f43e
A
433}
434
a39ff7e2
A
435static int
436feth_enable_dequeue_stall(ifnet_t ifp, uint32_t enable)
437{
438 int error;
439
0a7de745 440 if (enable != 0) {
a39ff7e2 441 error = ifnet_disable_output(ifp);
0a7de745 442 } else {
a39ff7e2 443 error = ifnet_enable_output(ifp);
0a7de745 444 }
a39ff7e2 445
0a7de745 446 return error;
a39ff7e2 447}
5ba3f43e 448
d9a64523 449
0a7de745
A
450#define FETH_MAXUNIT IF_MAXUNIT
451#define FETH_ZONE_MAX_ELEM MIN(IFNETS_MAX, FETH_MAXUNIT)
452#define M_FAKE M_DEVBUF
5ba3f43e 453
0a7de745
A
454static int feth_clone_create(struct if_clone *, u_int32_t, void *);
455static int feth_clone_destroy(ifnet_t);
456static int feth_output(ifnet_t ifp, struct mbuf *m);
457static void feth_start(ifnet_t ifp);
458static int feth_ioctl(ifnet_t ifp, u_long cmd, void * addr);
459static int feth_config(ifnet_t ifp, ifnet_t peer);
460static void feth_if_free(ifnet_t ifp);
461static void feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp);
462static void feth_free(if_fake_ref fakeif);
5ba3f43e
A
463
464static struct if_clone
0a7de745 465 feth_cloner = IF_CLONE_INITIALIZER(FAKE_ETHER_NAME,
d9a64523
A
466 feth_clone_create,
467 feth_clone_destroy,
468 0,
469 FETH_MAXUNIT,
470 FETH_ZONE_MAX_ELEM,
471 sizeof(struct if_fake));
0a7de745 472static void interface_link_event(ifnet_t ifp, u_int32_t event_code);
5ba3f43e
A
473
474/* some media words to pretend to be ethernet */
475static 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),
0a7de745
A
480
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),
5ba3f43e 490};
0a7de745
A
491#define default_media_words_count (sizeof(default_media_words) \
492 / sizeof (default_media_words[0]))
5ba3f43e
A
493
494/**
0a7de745
A
495** veth locks
496**/
5ba3f43e
A
497static inline lck_grp_t *
498my_lck_grp_alloc_init(const char * grp_name)
499{
0a7de745
A
500 lck_grp_t * grp;
501 lck_grp_attr_t * grp_attrs;
502
5ba3f43e
A
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);
0a7de745 506 return grp;
5ba3f43e
A
507}
508
509static inline lck_mtx_t *
510my_lck_mtx_alloc_init(lck_grp_t * lck_grp)
511{
0a7de745
A
512 lck_attr_t * lck_attrs;
513 lck_mtx_t * lck_mtx;
5ba3f43e
A
514
515 lck_attrs = lck_attr_alloc_init();
516 lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs);
517 lck_attr_free(lck_attrs);
0a7de745 518 return lck_mtx;
5ba3f43e
A
519}
520
0a7de745 521static lck_mtx_t * feth_lck_mtx;
5ba3f43e
A
522
523static inline void
524feth_lock_init(void)
525{
0a7de745 526 lck_grp_t * feth_lck_grp;
5ba3f43e
A
527
528 feth_lck_grp = my_lck_grp_alloc_init("fake");
529 feth_lck_mtx = my_lck_mtx_alloc_init(feth_lck_grp);
530}
531
532#if 0
f427ee49
A
533static inline void
534feth_assert_lock_held(void)
535{
536 LCK_MTX_ASSERT(feth_lck_mtx, LCK_MTX_ASSERT_OWNED);
537 return;
538}
539
5ba3f43e
A
540static inline void
541feth_assert_lock_not_held(void)
542{
543 LCK_MTX_ASSERT(feth_lck_mtx, LCK_MTX_ASSERT_NOTOWNED);
544 return;
545}
546#endif
547
548static inline void
549feth_lock(void)
550{
551 lck_mtx_lock(feth_lck_mtx);
552 return;
553}
554
555static inline void
556feth_unlock(void)
557{
558 lck_mtx_unlock(feth_lck_mtx);
559 return;
560}
561
562static inline int
cb323159 563get_max_mtu(int bsd_mode, unsigned int max_mtu)
5ba3f43e 564{
cb323159
A
565 unsigned int mtu;
566
567 if (bsd_mode != 0) {
568 mtu = (njcl > 0) ? (M16KCLBYTES - ETHER_HDR_LEN)
569 : MBIGCLBYTES - ETHER_HDR_LEN;
570 if (mtu > max_mtu) {
571 mtu = max_mtu;
572 }
573 } else {
574 mtu = max_mtu;
5ba3f43e 575 }
cb323159
A
576 return mtu;
577}
578
579static inline unsigned int
580feth_max_mtu(ifnet_t ifp)
581{
582 if_fake_ref fakeif;
583 unsigned int max_mtu = ETHERMTU;
584
585 feth_lock();
586 fakeif = ifnet_get_if_fake(ifp);
587 if (fakeif != NULL) {
588 max_mtu = fakeif->iff_max_mtu;
589 }
590 feth_unlock();
591 return max_mtu;
5ba3f43e
A
592}
593
594static void
595feth_free(if_fake_ref fakeif)
596{
f427ee49 597 VERIFY(fakeif->iff_retain_count == 0);
5ba3f43e
A
598 if (feth_in_bsd_mode(fakeif)) {
599 if (fakeif->iff_pending_tx_packet) {
600 m_freem(fakeif->iff_pending_tx_packet);
601 }
602 }
603
604 FETH_DPRINTF("%s\n", fakeif->iff_name);
d9a64523 605 if_clone_softc_deallocate(&feth_cloner, fakeif);
5ba3f43e
A
606}
607
608static void
609feth_release(if_fake_ref fakeif)
610{
0a7de745 611 u_int32_t old_retain_count;
5ba3f43e
A
612
613 old_retain_count = OSDecrementAtomic(&fakeif->iff_retain_count);
614 switch (old_retain_count) {
615 case 0:
f427ee49 616 VERIFY(old_retain_count != 0);
5ba3f43e
A
617 break;
618 case 1:
619 feth_free(fakeif);
620 break;
621 default:
622 break;
623 }
624 return;
625}
626
627
628/**
0a7de745
A
629** feth interface routines
630**/
5ba3f43e
A
631static void
632feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp)
633{
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);
638 ifnet_set_flags(ifp,
0a7de745
A
639 IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX,
640 0xffff);
5ba3f43e
A
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);
646 } else {
647 ifnet_set_offload(ifp, 0);
648 }
649}
650
651static void
652interface_link_event(ifnet_t ifp, u_int32_t event_code)
653{
f427ee49
A
654 struct event {
655 u_int32_t ifnet_family;
656 u_int32_t unit;
657 char if_name[IFNAMSIZ];
658 };
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);
662
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);
5ba3f43e
A
672}
673
674static if_fake_ref
675ifnet_get_if_fake(ifnet_t ifp)
676{
0a7de745 677 return (if_fake_ref)ifnet_softc(ifp);
5ba3f43e
A
678}
679
680static int
681feth_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
682{
0a7de745
A
683 int error;
684 if_fake_ref fakeif;
685 struct ifnet_init_eparams feth_init;
686 ifnet_t ifp;
687 uint8_t mac_address[ETHER_ADDR_LEN];
5ba3f43e 688
d9a64523 689 fakeif = if_clone_softc_allocate(&feth_cloner);
5ba3f43e
A
690 if (fakeif == NULL) {
691 return ENOBUFS;
692 }
693 fakeif->iff_retain_count = 1;
0a7de745 694#define FAKE_ETHER_NAME_LEN (sizeof(FAKE_ETHER_NAME) - 1)
5ba3f43e
A
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;
701 }
702 if (if_fake_hwcsum != 0) {
703 fakeif->iff_flags |= IFF_FLAGS_HWCSUM;
704 }
cb323159 705 fakeif->iff_max_mtu = get_max_mtu(if_fake_bsd_mode, if_fake_max_mtu);
f427ee49
A
706 fakeif->iff_fcs = if_fake_fcs;
707 fakeif->iff_trailer_length = if_fake_trailer_length;
5ba3f43e
A
708
709 /* use the interface name as the unique id for ifp recycle */
710 if ((unsigned int)
711 snprintf(fakeif->iff_name, sizeof(fakeif->iff_name), "%s%d",
0a7de745 712 ifc->ifc_name, unit) >= sizeof(fakeif->iff_name)) {
5ba3f43e 713 feth_release(fakeif);
0a7de745 714 return EINVAL;
5ba3f43e
A
715 }
716 bzero(&feth_init, sizeof(feth_init));
717 feth_init.ver = IFNET_INIT_CURRENT_VERSION;
0a7de745 718 feth_init.len = sizeof(feth_init);
5ba3f43e
A
719 if (feth_in_bsd_mode(fakeif)) {
720 if (if_fake_txstart != 0) {
721 feth_init.start = feth_start;
722 } else {
723 feth_init.flags |= IFNET_INIT_LEGACY;
724 feth_init.output = feth_output;
725 }
726 }
727 if (if_fake_nxattach == 0) {
728 feth_init.flags |= IFNET_INIT_NX_NOAUTO;
729 }
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);
749 if (error) {
750 feth_release(fakeif);
0a7de745 751 return error;
5ba3f43e
A
752 }
753 feth_ifnet_set_attrs(fakeif, ifp);
754 }
0a7de745 755 fakeif->iff_media_count = MIN(default_media_words_count, IF_FAKE_MEDIA_LIST_MAX);
5ba3f43e 756 bcopy(default_media_words, fakeif->iff_media_list,
0a7de745 757 fakeif->iff_media_count * sizeof(fakeif->iff_media_list[0]));
5ba3f43e
A
758 if (feth_in_bsd_mode(fakeif)) {
759 error = ifnet_attach(ifp, NULL);
760 if (error) {
761 ifnet_release(ifp);
762 feth_release(fakeif);
0a7de745 763 return error;
5ba3f43e
A
764 }
765 fakeif->iff_ifp = ifp;
766 }
767
768 ifnet_set_lladdr(ifp, mac_address, sizeof(mac_address));
0a7de745 769
5ba3f43e
A
770 /* attach as ethernet */
771 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
0a7de745 772 return 0;
5ba3f43e
A
773}
774
775static int
776feth_clone_destroy(ifnet_t ifp)
777{
0a7de745 778 if_fake_ref fakeif;
5ba3f43e
A
779
780 feth_lock();
781 fakeif = ifnet_get_if_fake(ifp);
782 if (fakeif == NULL || feth_is_detaching(fakeif)) {
783 feth_unlock();
0a7de745 784 return 0;
5ba3f43e
A
785 }
786 feth_set_detaching(fakeif);
787 feth_unlock();
788
789 feth_config(ifp, NULL);
790 ifnet_detach(ifp);
791 return 0;
792}
793
794static void
795feth_enqueue_input(ifnet_t ifp, struct mbuf * m)
796{
797 struct ifnet_stat_increment_param stats = {};
798
799 stats.packets_in = 1;
800 stats.bytes_in = (uint32_t)mbuf_pkthdr_len(m) + ETHER_HDR_LEN;
801 ifnet_input(ifp, m, &stats);
802}
803
804static struct mbuf *
805copy_mbuf(struct mbuf *m)
806{
0a7de745
A
807 struct mbuf * copy_m;
808 uint32_t pkt_len;
809 uint32_t offset;
5ba3f43e
A
810
811 if ((m->m_flags & M_PKTHDR) == 0) {
0a7de745 812 return NULL;
5ba3f43e
A
813 }
814 pkt_len = m->m_pkthdr.len;
815 MGETHDR(copy_m, M_DONTWAIT, MT_DATA);
816 if (copy_m == NULL) {
817 goto failed;
818 }
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);
826 } else {
827 printf("if_fake: copy_mbuf(): packet too large %d\n",
0a7de745 828 pkt_len);
5ba3f43e
A
829 goto failed;
830 }
831 if (copy_m == NULL || (copy_m->m_flags & M_EXT) == 0) {
832 goto failed;
833 }
834 }
835 mbuf_setlen(copy_m, pkt_len);
836 copy_m->m_pkthdr.len = pkt_len;
9d749ea3 837 copy_m->m_pkthdr.pkt_svc = m->m_pkthdr.pkt_svc;
5ba3f43e
A
838 offset = 0;
839 while (m != NULL && offset < pkt_len) {
0a7de745 840 uint32_t frag_len;
5ba3f43e
A
841
842 frag_len = m->m_len;
843 if (frag_len > (pkt_len - offset)) {
844 printf("if_fake_: Large mbuf fragment %d > %d\n",
0a7de745 845 frag_len, (pkt_len - offset));
5ba3f43e
A
846 goto failed;
847 }
848 m_copydata(m, 0, frag_len, mtod(copy_m, void *) + offset);
849 offset += frag_len;
850 m = m->m_next;
851 }
0a7de745 852 return copy_m;
5ba3f43e 853
0a7de745 854failed:
5ba3f43e
A
855 if (copy_m != NULL) {
856 m_freem(copy_m);
857 }
0a7de745 858 return NULL;
5ba3f43e
A
859}
860
f427ee49
A
861static int
862feth_add_mbuf_trailer(struct mbuf *m, void *trailer, size_t trailer_len)
863{
864 int ret;
865 ASSERT(trailer_len <= FETH_TRAILER_LENGTH_MAX);
866
867 ret = m_append(m, trailer_len, (caddr_t)trailer);
868 if (ret == 1) {
869 FETH_DPRINTF("%s %zuB trailer added\n", __func__, trailer_len);
870 return 0;
871 }
872 printf("%s m_append failed\n", __func__);
873 return ENOTSUP;
874}
875
876static int
877feth_add_mbuf_fcs(struct mbuf *m)
878{
879 uint32_t pkt_len, offset = 0;
880 uint32_t crc = 0;
881 int err = 0;
882
883 ASSERT(sizeof(crc) == ETHER_CRC_LEN);
884
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);
891 offset += frag_len;
892 iter = m->m_next;
893 }
894
895 err = feth_add_mbuf_trailer(m, &crc, ETHER_CRC_LEN);
896 if (err != 0) {
897 return err;
898 }
899
900 m->m_flags |= M_HASFCS;
901
902 return 0;
903}
904
5ba3f43e
A
905static void
906feth_output_common(ifnet_t ifp, struct mbuf * m, ifnet_t peer,
f427ee49 907 iff_flags_t flags, bool fcs, void *trailer, size_t trailer_len)
5ba3f43e 908{
0a7de745 909 void * frame_header;
5ba3f43e
A
910
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 =
0a7de745
A
915 CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
916 CSUM_IP_CHECKED | CSUM_IP_VALID;
5ba3f43e
A
917 }
918
919 (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
920 bpf_tap_out(ifp, DLT_EN10MB, m, NULL, 0);
921
f427ee49
A
922 if (trailer != 0) {
923 feth_add_mbuf_trailer(m, trailer, trailer_len);
924 }
925 if (fcs) {
926 feth_add_mbuf_fcs(m);
927 }
928
5ba3f43e
A
929 (void)mbuf_pkthdr_setrcvif(m, peer);
930 mbuf_pkthdr_setheader(m, frame_header);
0a7de745 931 mbuf_pkthdr_adjustlen(m, -ETHER_HDR_LEN);
5ba3f43e 932 (void)mbuf_setdata(m, (char *)mbuf_data(m) + ETHER_HDR_LEN,
0a7de745 933 mbuf_len(m) - ETHER_HDR_LEN);
5ba3f43e 934 bpf_tap_in(peer, DLT_EN10MB, m, frame_header,
0a7de745 935 sizeof(struct ether_header));
5ba3f43e
A
936 feth_enqueue_input(peer, m);
937}
938
939static void
940feth_start(ifnet_t ifp)
941{
0a7de745
A
942 struct mbuf * copy_m = NULL;
943 if_fake_ref fakeif;
944 iff_flags_t flags = 0;
f427ee49
A
945 bool fcs;
946 size_t trailer_len;
947 ifnet_t peer = NULL;
0a7de745
A
948 struct mbuf * m;
949 struct mbuf * save_m;
5ba3f43e
A
950
951 feth_lock();
952 fakeif = ifnet_get_if_fake(ifp);
cb323159
A
953 if (fakeif == NULL) {
954 feth_unlock();
955 return;
956 }
957
5ba3f43e
A
958 if (fakeif->iff_start_busy) {
959 feth_unlock();
960 printf("if_fake: start is busy\n");
961 return;
962 }
cb323159
A
963
964 peer = fakeif->iff_peer;
965 flags = fakeif->iff_flags;
f427ee49
A
966 fcs = fakeif->iff_fcs;
967 trailer_len = fakeif->iff_trailer_length;
5ba3f43e
A
968
969 /* check for pending TX */
970 m = fakeif->iff_pending_tx_packet;
971 if (m != NULL) {
972 if (peer != NULL) {
973 copy_m = copy_mbuf(m);
974 if (copy_m == NULL) {
975 feth_unlock();
976 return;
977 }
978 }
979 fakeif->iff_pending_tx_packet = NULL;
980 m_freem(m);
981 m = NULL;
982 }
983 fakeif->iff_start_busy = TRUE;
984 feth_unlock();
985 save_m = NULL;
986 for (;;) {
987 if (copy_m != NULL) {
f427ee49
A
988 VERIFY(peer != NULL);
989 feth_output_common(ifp, copy_m, peer, flags, fcs,
990 feth_trailer, trailer_len);
5ba3f43e
A
991 copy_m = NULL;
992 }
993 if (ifnet_dequeue(ifp, &m) != 0) {
994 break;
995 }
996 if (peer == NULL) {
997 m_freem(m);
998 } else {
999 copy_m = copy_mbuf(m);
1000 if (copy_m == NULL) {
1001 save_m = m;
1002 break;
1003 }
1004 m_freem(m);
1005 }
1006 }
1007 peer = NULL;
1008 feth_lock();
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;
1015 save_m = NULL;
1016 }
1017 }
1018 feth_unlock();
1019 if (save_m != NULL) {
1020 /* didn't save packet, so free it */
1021 m_freem(save_m);
1022 }
1023}
1024
1025static int
1026feth_output(ifnet_t ifp, struct mbuf * m)
1027{
0a7de745
A
1028 struct mbuf * copy_m;
1029 if_fake_ref fakeif;
1030 iff_flags_t flags;
f427ee49
A
1031 bool fcs;
1032 size_t trailer_len;
1033 ifnet_t peer = NULL;
5ba3f43e
A
1034
1035 if (m == NULL) {
0a7de745 1036 return 0;
5ba3f43e
A
1037 }
1038 copy_m = copy_mbuf(m);
1039 m_freem(m);
1040 m = NULL;
1041 if (copy_m == NULL) {
1042 /* count this as an output error */
1043 ifnet_stat_increment_out(ifp, 0, 0, 1);
0a7de745 1044 return 0;
5ba3f43e
A
1045 }
1046 feth_lock();
1047 fakeif = ifnet_get_if_fake(ifp);
1048 if (fakeif != NULL) {
1049 peer = fakeif->iff_peer;
1050 flags = fakeif->iff_flags;
f427ee49
A
1051 fcs = fakeif->iff_fcs;
1052 trailer_len = fakeif->iff_trailer_length;
5ba3f43e
A
1053 }
1054 feth_unlock();
1055 if (peer == NULL) {
1056 m_freem(copy_m);
1057 ifnet_stat_increment_out(ifp, 0, 0, 1);
0a7de745 1058 return 0;
5ba3f43e 1059 }
f427ee49
A
1060 feth_output_common(ifp, copy_m, peer, flags, fcs, feth_trailer,
1061 trailer_len);
0a7de745 1062 return 0;
5ba3f43e
A
1063}
1064
1065static int
1066feth_config(ifnet_t ifp, ifnet_t peer)
1067{
0a7de745
A
1068 int connected = FALSE;
1069 int disconnected = FALSE;
1070 int error = 0;
1071 if_fake_ref fakeif = NULL;
5ba3f43e
A
1072
1073 feth_lock();
1074 fakeif = ifnet_get_if_fake(ifp);
1075 if (fakeif == NULL) {
1076 error = EINVAL;
1077 goto done;
1078 }
1079 if (peer != NULL) {
1080 /* connect to peer */
0a7de745 1081 if_fake_ref peer_fakeif;
5ba3f43e
A
1082
1083 peer_fakeif = ifnet_get_if_fake(peer);
1084 if (peer_fakeif == NULL) {
1085 error = EINVAL;
1086 goto done;
1087 }
1088 if (feth_is_detaching(fakeif) ||
1089 feth_is_detaching(peer_fakeif) ||
1090 peer_fakeif->iff_peer != NULL ||
1091 fakeif->iff_peer != NULL) {
1092 error = EBUSY;
1093 goto done;
1094 }
1095 fakeif->iff_peer = peer;
1096 peer_fakeif->iff_peer = ifp;
1097 connected = TRUE;
0a7de745 1098 } else if (fakeif->iff_peer != NULL) {
5ba3f43e 1099 /* disconnect from peer */
0a7de745 1100 if_fake_ref peer_fakeif;
5ba3f43e
A
1101
1102 peer = fakeif->iff_peer;
1103 peer_fakeif = ifnet_get_if_fake(peer);
1104 if (peer_fakeif == NULL) {
1105 /* should not happen */
1106 error = EINVAL;
1107 goto done;
1108 }
1109 fakeif->iff_peer = NULL;
1110 peer_fakeif->iff_peer = NULL;
1111 disconnected = TRUE;
1112 }
1113
0a7de745 1114done:
5ba3f43e
A
1115 feth_unlock();
1116
1117 /* generate link status event if we connect or disconnect */
1118 if (connected) {
5ba3f43e
A
1119 interface_link_event(ifp, KEV_DL_LINK_ON);
1120 interface_link_event(peer, KEV_DL_LINK_ON);
0a7de745 1121 } else if (disconnected) {
5ba3f43e
A
1122 interface_link_event(ifp, KEV_DL_LINK_OFF);
1123 interface_link_event(peer, KEV_DL_LINK_OFF);
1124 }
0a7de745 1125 return error;
5ba3f43e
A
1126}
1127
1128static int
1129feth_set_media(ifnet_t ifp, struct if_fake_request * iffr)
1130{
0a7de745
A
1131 if_fake_ref fakeif;
1132 int error;
5ba3f43e
A
1133
1134 if (iffr->iffr_media.iffm_count > IF_FAKE_MEDIA_LIST_MAX) {
1135 /* list is too long */
0a7de745 1136 return EINVAL;
5ba3f43e
A
1137 }
1138 feth_lock();
1139 fakeif = ifnet_get_if_fake(ifp);
1140 if (fakeif == NULL) {
1141 error = EINVAL;
1142 goto done;
1143 }
1144 fakeif->iff_media_count = iffr->iffr_media.iffm_count;
1145 bcopy(iffr->iffr_media.iffm_list, fakeif->iff_media_list,
0a7de745 1146 iffr->iffr_media.iffm_count * sizeof(fakeif->iff_media_list[0]));
5ba3f43e
A
1147#if 0
1148 /* XXX: "auto-negotiate" active with peer? */
1149 /* generate link status event? */
1150 fakeif->iff_media_current = iffr->iffr_media.iffm_current;
1151#endif
1152 error = 0;
0a7de745 1153done:
5ba3f43e 1154 feth_unlock();
0a7de745 1155 return error;
5ba3f43e
A
1156}
1157
1158static int
0a7de745
A
1159if_fake_request_copyin(user_addr_t user_addr,
1160 struct if_fake_request *iffr, u_int32_t len)
5ba3f43e 1161{
0a7de745 1162 int error;
5ba3f43e
A
1163
1164 if (user_addr == USER_ADDR_NULL || len < sizeof(*iffr)) {
1165 error = EINVAL;
1166 goto done;
1167 }
1168 error = copyin(user_addr, iffr, sizeof(*iffr));
1169 if (error != 0) {
1170 goto done;
1171 }
1172 if (iffr->iffr_reserved[0] != 0 || iffr->iffr_reserved[1] != 0 ||
1173 iffr->iffr_reserved[2] != 0 || iffr->iffr_reserved[3] != 0) {
1174 error = EINVAL;
1175 goto done;
1176 }
0a7de745
A
1177done:
1178 return error;
5ba3f43e
A
1179}
1180
1181static int
1182feth_set_drvspec(ifnet_t ifp, uint32_t cmd, u_int32_t len,
0a7de745 1183 user_addr_t user_addr)
5ba3f43e 1184{
0a7de745
A
1185 int error;
1186 struct if_fake_request iffr;
1187 ifnet_t peer;
5ba3f43e
A
1188
1189 switch (cmd) {
1190 case IF_FAKE_S_CMD_SET_PEER:
1191 error = if_fake_request_copyin(user_addr, &iffr, len);
1192 if (error != 0) {
1193 break;
1194 }
1195 if (iffr.iffr_peer_name[0] == '\0') {
1196 error = feth_config(ifp, NULL);
1197 break;
1198 }
1199
1200 /* ensure nul termination */
1201 iffr.iffr_peer_name[IFNAMSIZ - 1] = '\0';
1202 peer = ifunit(iffr.iffr_peer_name);
1203 if (peer == NULL) {
1204 error = ENXIO;
1205 break;
1206 }
1207 if (ifnet_type(peer) != IFT_ETHER) {
1208 error = EINVAL;
1209 break;
1210 }
1211 if (strcmp(ifnet_name(peer), FAKE_ETHER_NAME) != 0) {
1212 error = EINVAL;
1213 break;
1214 }
1215 error = feth_config(ifp, peer);
1216 break;
1217 case IF_FAKE_S_CMD_SET_MEDIA:
1218 error = if_fake_request_copyin(user_addr, &iffr, len);
1219 if (error != 0) {
1220 break;
1221 }
1222 error = feth_set_media(ifp, &iffr);
1223 break;
a39ff7e2
A
1224 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL:
1225 error = if_fake_request_copyin(user_addr, &iffr, len);
1226 if (error != 0) {
1227 break;
1228 }
1229 error = feth_enable_dequeue_stall(ifp,
1230 iffr.iffr_dequeue_stall);
1231 break;
5ba3f43e
A
1232 default:
1233 error = EOPNOTSUPP;
1234 break;
1235 }
0a7de745 1236 return error;
5ba3f43e
A
1237}
1238
1239static int
1240feth_get_drvspec(ifnet_t ifp, u_int32_t cmd, u_int32_t len,
0a7de745 1241 user_addr_t user_addr)
5ba3f43e 1242{
0a7de745
A
1243 int error = EOPNOTSUPP;
1244 if_fake_ref fakeif;
1245 struct if_fake_request iffr;
1246 ifnet_t peer;
5ba3f43e
A
1247
1248 switch (cmd) {
1249 case IF_FAKE_G_CMD_GET_PEER:
1250 if (len < sizeof(iffr)) {
1251 error = EINVAL;
1252 break;
1253 }
1254 feth_lock();
cb323159 1255 fakeif = ifnet_get_if_fake(ifp);
5ba3f43e
A
1256 if (fakeif == NULL) {
1257 feth_unlock();
1258 error = EOPNOTSUPP;
1259 break;
1260 }
1261 peer = fakeif->iff_peer;
1262 feth_unlock();
1263 bzero(&iffr, sizeof(iffr));
1264 if (peer != NULL) {
1265 strlcpy(iffr.iffr_peer_name,
0a7de745
A
1266 if_name(peer),
1267 sizeof(iffr.iffr_peer_name));
5ba3f43e
A
1268 }
1269 error = copyout(&iffr, user_addr, sizeof(iffr));
1270 break;
1271 default:
1272 break;
1273 }
0a7de745 1274 return error;
5ba3f43e
A
1275}
1276
1277union ifdrvu {
0a7de745
A
1278 struct ifdrv32 *ifdrvu_32;
1279 struct ifdrv64 *ifdrvu_64;
1280 void *ifdrvu_p;
5ba3f43e
A
1281};
1282
1283static int
1284feth_ioctl(ifnet_t ifp, u_long cmd, void * data)
1285{
0a7de745
A
1286 unsigned int count;
1287 struct ifdevmtu * devmtu_p;
1288 union ifdrvu drv;
1289 uint32_t drv_cmd;
1290 uint32_t drv_len;
1291 boolean_t drv_set_command = FALSE;
1292 int error = 0;
1293 struct ifmediareq * ifmr;
1294 struct ifreq * ifr;
1295 if_fake_ref fakeif;
1296 int status;
1297 user_addr_t user_addr;
5ba3f43e
A
1298
1299 ifr = (struct ifreq *)data;
1300 switch (cmd) {
1301 case SIOCSIFADDR:
1302 ifnet_set_flags(ifp, IFF_UP, IFF_UP);
1303 break;
1304
1305 case SIOCGIFMEDIA32:
1306 case SIOCGIFMEDIA64:
1307 feth_lock();
cb323159 1308 fakeif = ifnet_get_if_fake(ifp);
5ba3f43e
A
1309 if (fakeif == NULL) {
1310 feth_unlock();
0a7de745 1311 return EOPNOTSUPP;
5ba3f43e
A
1312 }
1313 status = (fakeif->iff_peer != NULL)
1314 ? (IFM_AVALID | IFM_ACTIVE) : IFM_AVALID;
1315 ifmr = (struct ifmediareq *)data;
1316 user_addr = (cmd == SIOCGIFMEDIA64) ?
0a7de745
A
1317 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
1318 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
5ba3f43e
A
1319 count = ifmr->ifm_count;
1320 ifmr->ifm_active = IFM_ETHER;
1321 ifmr->ifm_current = IFM_ETHER;
1322 ifmr->ifm_mask = 0;
1323 ifmr->ifm_status = status;
1324 if (user_addr == USER_ADDR_NULL) {
1325 ifmr->ifm_count = fakeif->iff_media_count;
0a7de745 1326 } else if (count > 0) {
5ba3f43e
A
1327 if (count > fakeif->iff_media_count) {
1328 count = fakeif->iff_media_count;
1329 }
1330 ifmr->ifm_count = count;
1331 error = copyout(&fakeif->iff_media_list, user_addr,
0a7de745 1332 count * sizeof(int));
5ba3f43e
A
1333 }
1334 feth_unlock();
1335 break;
1336
1337 case SIOCGIFDEVMTU:
1338 devmtu_p = &ifr->ifr_devmtu;
1339 devmtu_p->ifdm_current = ifnet_mtu(ifp);
cb323159 1340 devmtu_p->ifdm_max = feth_max_mtu(ifp);
5ba3f43e
A
1341 devmtu_p->ifdm_min = IF_MINMTU;
1342 break;
1343
1344 case SIOCSIFMTU:
cb323159
A
1345 if ((unsigned int)ifr->ifr_mtu > feth_max_mtu(ifp) ||
1346 ifr->ifr_mtu < IF_MINMTU) {
5ba3f43e
A
1347 error = EINVAL;
1348 } else {
1349 error = ifnet_set_mtu(ifp, ifr->ifr_mtu);
1350 }
1351 break;
1352
1353 case SIOCSDRVSPEC32:
1354 case SIOCSDRVSPEC64:
1355 error = proc_suser(current_proc());
1356 if (error != 0) {
1357 break;
1358 }
1359 drv_set_command = TRUE;
f427ee49 1360 OS_FALLTHROUGH;
5ba3f43e
A
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);
5ba3f43e
A
1368 } else {
1369 drv_cmd = drv.ifdrvu_64->ifd_cmd;
1370 drv_len = drv.ifdrvu_64->ifd_len;
1371 user_addr = drv.ifdrvu_64->ifd_data;
1372 }
1373 if (drv_set_command) {
1374 error = feth_set_drvspec(ifp, drv_cmd, drv_len,
0a7de745 1375 user_addr);
5ba3f43e
A
1376 } else {
1377 error = feth_get_drvspec(ifp, drv_cmd, drv_len,
0a7de745 1378 user_addr);
5ba3f43e
A
1379 }
1380 break;
1381
1382 case SIOCSIFLLADDR:
1383 error = ifnet_set_lladdr(ifp, ifr->ifr_addr.sa_data,
1384 ifr->ifr_addr.sa_len);
1385 break;
1386
1387 case SIOCSIFFLAGS:
a39ff7e2
A
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) {
1391 /* set running */
1392 error = ifnet_set_flags(ifp, IFF_RUNNING,
1393 IFF_RUNNING);
1394 }
1395 } else if ((ifp->if_flags & IFF_RUNNING) != 0) {
1396 /* marked down, clear running */
1397 error = ifnet_set_flags(ifp, 0, IFF_RUNNING);
1398 }
5ba3f43e
A
1399 break;
1400
1401 case SIOCADDMULTI:
1402 case SIOCDELMULTI:
1403 error = 0;
1404 break;
1405 default:
1406 error = EOPNOTSUPP;
1407 break;
1408 }
1409 return error;
1410}
1411
0a7de745 1412static void
5ba3f43e
A
1413feth_if_free(ifnet_t ifp)
1414{
0a7de745 1415 if_fake_ref fakeif;
5ba3f43e
A
1416
1417 if (ifp == NULL) {
1418 return;
1419 }
1420 feth_lock();
1421 fakeif = ifnet_get_if_fake(ifp);
1422 if (fakeif == NULL) {
1423 feth_unlock();
1424 return;
1425 }
1426 ifp->if_softc = NULL;
1427 feth_unlock();
1428 feth_release(fakeif);
1429 ifnet_release(ifp);
1430 return;
1431}
1432
1433__private_extern__ void
1434if_fake_init(void)
1435{
1436 int error;
1437
1438 feth_lock_init();
1439 error = if_clone_attach(&feth_cloner);
1440 if (error != 0) {
1441 return;
1442 }
1443 return;
1444}