]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_fake.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / net / if_fake.c
CommitLineData
5ba3f43e 1/*
0a7de745 2 * Copyright (c) 2015-2019 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
124static int if_fake_copypkt_mode = 0;
125SYSCTL_INT(_net_link_fake, OID_AUTO, copypkt_mode, CTLFLAG_RW | CTLFLAG_LOCKED,
126 &if_fake_copypkt_mode, 0, "Fake interface copying packet to peer");
127
128/* sysctl net.link.fake.tx_headroom */
129#define FETH_TX_HEADROOM_MAX 32
130static unsigned int if_fake_tx_headroom = 0;
131
132static int
133feth_tx_headroom_sysctl SYSCTL_HANDLER_ARGS
134{
135#pragma unused(oidp, arg1, arg2)
136 unsigned int new_value;
137 int changed;
138 int error;
139
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) {
145 return EINVAL;
146 }
147 if_fake_tx_headroom = new_value;
148 }
149 return 0;
150}
151
152SYSCTL_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");
155
156
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)
160
161static unsigned int if_fake_max_mtu = FETH_MAX_MTU_DEFAULT;
162
163/* sysctl net.link.fake.buflet_size */
164#define FETH_BUFLET_SIZE_MIN 512
165#define FETH_BUFLET_SIZE_MAX 2048
166
167static unsigned int if_fake_buflet_size = FETH_BUFLET_SIZE_MIN;
168
169static int
170feth_max_mtu_sysctl SYSCTL_HANDLER_ARGS
171{
172#pragma unused(oidp, arg1, arg2)
173 unsigned int new_value;
174 int changed;
175 int error;
176
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) {
183 return EINVAL;
184 }
185 if_fake_max_mtu = new_value;
186 }
187 return 0;
188}
189
190SYSCTL_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");
193
194static int
195feth_buflet_size_sysctl SYSCTL_HANDLER_ARGS
196{
197#pragma unused(oidp, arg1, arg2)
198 unsigned int new_value;
199 int changed;
200 int error;
201
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) {
210 return EINVAL;
211 }
212 if_fake_buflet_size = new_value;
213 }
214 return 0;
215}
216
217SYSCTL_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");
220
221static unsigned int if_fake_user_access = 0;
222
223static int
224feth_user_access_sysctl SYSCTL_HANDLER_ARGS
225{
226#pragma unused(oidp, arg1, arg2)
227 unsigned int new_value;
228 int changed;
229 int error;
230
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) {
236 return EINVAL;
237 }
238 /*
239 * copypkt mode requires a kernel only buffer pool so
240 * it is incompatible with user access mode.
241 */
242 if (if_fake_copypkt_mode != 0) {
243 return ENOTSUP;
244 }
245 }
246 if_fake_user_access = new_value;
247 }
248 return 0;
249}
250
251SYSCTL_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");
254
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
258
259static int if_fake_if_adv_interval = 0; /* no interface advisory */
260static int
261feth_if_adv_interval_sysctl SYSCTL_HANDLER_ARGS
262{
263#pragma unused(oidp, arg1, arg2)
264 unsigned int new_value;
265 int changed;
266 int error;
267
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)) {
273 return EINVAL;
274 }
275 if_fake_if_adv_interval = new_value;
276 }
277 return 0;
278}
279
280SYSCTL_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");
284
285/* sysctl net.link.fake.tx_drops */
286/*
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.
289 */
290#define FETH_TX_DROPS_MIN 0
291#define FETH_TX_DROPS_MAX INT_MAX
292static int if_fake_tx_drops = 0; /* no packets are dropped */
293static int
294feth_fake_tx_drops_sysctl SYSCTL_HANDLER_ARGS
295{
296#pragma unused(oidp, arg1, arg2)
297 unsigned int new_value;
298 int changed;
299 int error;
300
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) {
306 return EINVAL;
307 }
308 if_fake_tx_drops = new_value;
309 }
310 return 0;
311}
312
313SYSCTL_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");
317
5ba3f43e 318/**
0a7de745
A
319** virtual ethernet structures, types
320**/
5ba3f43e 321
0a7de745
A
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
d9a64523 326
0a7de745
A
327typedef 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
cb323159
A
332#define IFF_FLAGS_MULTIBUFLETS 0x0010
333#define IFF_FLAGS_COPYPKT_MODE 0x0020
5ba3f43e
A
334
335
336struct if_fake {
0a7de745
A
337 char iff_name[IFNAMSIZ]; /* our unique id */
338 ifnet_t iff_ifp;
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;
cb323159 348 unsigned int iff_max_mtu;
5ba3f43e
A
349};
350
351typedef struct if_fake * if_fake_ref;
352
353static if_fake_ref
354ifnet_get_if_fake(ifnet_t ifp);
355
0a7de745 356#define FETH_DPRINTF(fmt, ...) \
5ba3f43e
A
357 { if (if_fake_debug != 0) printf("%s " fmt, __func__, ## __VA_ARGS__); }
358
359static inline boolean_t
360feth_in_bsd_mode(if_fake_ref fakeif)
361{
0a7de745 362 return (fakeif->iff_flags & IFF_FLAGS_BSD_MODE) != 0;
5ba3f43e
A
363}
364
365static inline void
366feth_set_detaching(if_fake_ref fakeif)
367{
368 fakeif->iff_flags |= IFF_FLAGS_DETACHING;
369}
370
371static inline boolean_t
372feth_is_detaching(if_fake_ref fakeif)
373{
0a7de745 374 return (fakeif->iff_flags & IFF_FLAGS_DETACHING) != 0;
5ba3f43e
A
375}
376
a39ff7e2
A
377static int
378feth_enable_dequeue_stall(ifnet_t ifp, uint32_t enable)
379{
380 int error;
381
0a7de745 382 if (enable != 0) {
a39ff7e2 383 error = ifnet_disable_output(ifp);
0a7de745 384 } else {
a39ff7e2 385 error = ifnet_enable_output(ifp);
0a7de745 386 }
a39ff7e2 387
0a7de745 388 return error;
a39ff7e2 389}
5ba3f43e 390
d9a64523 391
0a7de745
A
392#define FETH_MAXUNIT IF_MAXUNIT
393#define FETH_ZONE_MAX_ELEM MIN(IFNETS_MAX, FETH_MAXUNIT)
394#define M_FAKE M_DEVBUF
5ba3f43e 395
0a7de745
A
396static int feth_clone_create(struct if_clone *, u_int32_t, void *);
397static int feth_clone_destroy(ifnet_t);
398static int feth_output(ifnet_t ifp, struct mbuf *m);
399static void feth_start(ifnet_t ifp);
400static int feth_ioctl(ifnet_t ifp, u_long cmd, void * addr);
401static int feth_config(ifnet_t ifp, ifnet_t peer);
402static void feth_if_free(ifnet_t ifp);
403static void feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp);
404static void feth_free(if_fake_ref fakeif);
5ba3f43e
A
405
406static struct if_clone
0a7de745 407 feth_cloner = IF_CLONE_INITIALIZER(FAKE_ETHER_NAME,
d9a64523
A
408 feth_clone_create,
409 feth_clone_destroy,
410 0,
411 FETH_MAXUNIT,
412 FETH_ZONE_MAX_ELEM,
413 sizeof(struct if_fake));
0a7de745 414static void interface_link_event(ifnet_t ifp, u_int32_t event_code);
5ba3f43e
A
415
416/* some media words to pretend to be ethernet */
417static 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),
0a7de745
A
422
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),
5ba3f43e 432};
0a7de745
A
433#define default_media_words_count (sizeof(default_media_words) \
434 / sizeof (default_media_words[0]))
5ba3f43e
A
435
436/**
0a7de745
A
437** veth locks
438**/
5ba3f43e
A
439static inline lck_grp_t *
440my_lck_grp_alloc_init(const char * grp_name)
441{
0a7de745
A
442 lck_grp_t * grp;
443 lck_grp_attr_t * grp_attrs;
444
5ba3f43e
A
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);
0a7de745 448 return grp;
5ba3f43e
A
449}
450
451static inline lck_mtx_t *
452my_lck_mtx_alloc_init(lck_grp_t * lck_grp)
453{
0a7de745
A
454 lck_attr_t * lck_attrs;
455 lck_mtx_t * lck_mtx;
5ba3f43e
A
456
457 lck_attrs = lck_attr_alloc_init();
458 lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs);
459 lck_attr_free(lck_attrs);
0a7de745 460 return lck_mtx;
5ba3f43e
A
461}
462
0a7de745 463static lck_mtx_t * feth_lck_mtx;
5ba3f43e
A
464
465static inline void
466feth_lock_init(void)
467{
0a7de745 468 lck_grp_t * feth_lck_grp;
5ba3f43e
A
469
470 feth_lck_grp = my_lck_grp_alloc_init("fake");
471 feth_lck_mtx = my_lck_mtx_alloc_init(feth_lck_grp);
472}
473
474#if 0
475static inline void
476feth_assert_lock_not_held(void)
477{
478 LCK_MTX_ASSERT(feth_lck_mtx, LCK_MTX_ASSERT_NOTOWNED);
479 return;
480}
481#endif
482
483static inline void
484feth_lock(void)
485{
486 lck_mtx_lock(feth_lck_mtx);
487 return;
488}
489
490static inline void
491feth_unlock(void)
492{
493 lck_mtx_unlock(feth_lck_mtx);
494 return;
495}
496
497static inline int
cb323159 498get_max_mtu(int bsd_mode, unsigned int max_mtu)
5ba3f43e 499{
cb323159
A
500 unsigned int mtu;
501
502 if (bsd_mode != 0) {
503 mtu = (njcl > 0) ? (M16KCLBYTES - ETHER_HDR_LEN)
504 : MBIGCLBYTES - ETHER_HDR_LEN;
505 if (mtu > max_mtu) {
506 mtu = max_mtu;
507 }
508 } else {
509 mtu = max_mtu;
5ba3f43e 510 }
cb323159
A
511 return mtu;
512}
513
514static inline unsigned int
515feth_max_mtu(ifnet_t ifp)
516{
517 if_fake_ref fakeif;
518 unsigned int max_mtu = ETHERMTU;
519
520 feth_lock();
521 fakeif = ifnet_get_if_fake(ifp);
522 if (fakeif != NULL) {
523 max_mtu = fakeif->iff_max_mtu;
524 }
525 feth_unlock();
526 return max_mtu;
5ba3f43e
A
527}
528
529static void
530feth_free(if_fake_ref fakeif)
531{
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);
536 }
537 }
538
539 FETH_DPRINTF("%s\n", fakeif->iff_name);
d9a64523 540 if_clone_softc_deallocate(&feth_cloner, fakeif);
5ba3f43e
A
541}
542
543static void
544feth_release(if_fake_ref fakeif)
545{
0a7de745 546 u_int32_t old_retain_count;
5ba3f43e
A
547
548 old_retain_count = OSDecrementAtomic(&fakeif->iff_retain_count);
549 switch (old_retain_count) {
550 case 0:
551 assert(old_retain_count != 0);
552 break;
553 case 1:
554 feth_free(fakeif);
555 break;
556 default:
557 break;
558 }
559 return;
560}
561
562
563/**
0a7de745
A
564** feth interface routines
565**/
5ba3f43e
A
566static void
567feth_ifnet_set_attrs(if_fake_ref fakeif, ifnet_t ifp)
568{
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);
573 ifnet_set_flags(ifp,
0a7de745
A
574 IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX,
575 0xffff);
5ba3f43e
A
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);
581 } else {
582 ifnet_set_offload(ifp, 0);
583 }
584}
585
586static void
587interface_link_event(ifnet_t ifp, u_int32_t event_code)
588{
589 struct {
0a7de745
A
590 struct kern_event_msg header;
591 u_int32_t unit;
592 char if_name[IFNAMSIZ];
5ba3f43e
A
593 } event;
594
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);
605 return;
606}
607
608static if_fake_ref
609ifnet_get_if_fake(ifnet_t ifp)
610{
0a7de745 611 return (if_fake_ref)ifnet_softc(ifp);
5ba3f43e
A
612}
613
614static int
615feth_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
616{
0a7de745
A
617 int error;
618 if_fake_ref fakeif;
619 struct ifnet_init_eparams feth_init;
620 ifnet_t ifp;
621 uint8_t mac_address[ETHER_ADDR_LEN];
5ba3f43e 622
d9a64523 623 fakeif = if_clone_softc_allocate(&feth_cloner);
5ba3f43e
A
624 if (fakeif == NULL) {
625 return ENOBUFS;
626 }
627 fakeif->iff_retain_count = 1;
0a7de745 628#define FAKE_ETHER_NAME_LEN (sizeof(FAKE_ETHER_NAME) - 1)
5ba3f43e
A
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;
635 }
636 if (if_fake_hwcsum != 0) {
637 fakeif->iff_flags |= IFF_FLAGS_HWCSUM;
638 }
cb323159 639 fakeif->iff_max_mtu = get_max_mtu(if_fake_bsd_mode, if_fake_max_mtu);
5ba3f43e
A
640
641 /* use the interface name as the unique id for ifp recycle */
642 if ((unsigned int)
643 snprintf(fakeif->iff_name, sizeof(fakeif->iff_name), "%s%d",
0a7de745 644 ifc->ifc_name, unit) >= sizeof(fakeif->iff_name)) {
5ba3f43e 645 feth_release(fakeif);
0a7de745 646 return EINVAL;
5ba3f43e
A
647 }
648 bzero(&feth_init, sizeof(feth_init));
649 feth_init.ver = IFNET_INIT_CURRENT_VERSION;
0a7de745 650 feth_init.len = sizeof(feth_init);
5ba3f43e
A
651 if (feth_in_bsd_mode(fakeif)) {
652 if (if_fake_txstart != 0) {
653 feth_init.start = feth_start;
654 } else {
655 feth_init.flags |= IFNET_INIT_LEGACY;
656 feth_init.output = feth_output;
657 }
658 }
659 if (if_fake_nxattach == 0) {
660 feth_init.flags |= IFNET_INIT_NX_NOAUTO;
661 }
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);
681 if (error) {
682 feth_release(fakeif);
0a7de745 683 return error;
5ba3f43e
A
684 }
685 feth_ifnet_set_attrs(fakeif, ifp);
686 }
0a7de745 687 fakeif->iff_media_count = MIN(default_media_words_count, IF_FAKE_MEDIA_LIST_MAX);
5ba3f43e 688 bcopy(default_media_words, fakeif->iff_media_list,
0a7de745 689 fakeif->iff_media_count * sizeof(fakeif->iff_media_list[0]));
5ba3f43e
A
690 if (feth_in_bsd_mode(fakeif)) {
691 error = ifnet_attach(ifp, NULL);
692 if (error) {
693 ifnet_release(ifp);
694 feth_release(fakeif);
0a7de745 695 return error;
5ba3f43e
A
696 }
697 fakeif->iff_ifp = ifp;
698 }
699
700 ifnet_set_lladdr(ifp, mac_address, sizeof(mac_address));
0a7de745 701
5ba3f43e
A
702 /* attach as ethernet */
703 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
0a7de745 704 return 0;
5ba3f43e
A
705}
706
707static int
708feth_clone_destroy(ifnet_t ifp)
709{
0a7de745 710 if_fake_ref fakeif;
5ba3f43e
A
711
712 feth_lock();
713 fakeif = ifnet_get_if_fake(ifp);
714 if (fakeif == NULL || feth_is_detaching(fakeif)) {
715 feth_unlock();
0a7de745 716 return 0;
5ba3f43e
A
717 }
718 feth_set_detaching(fakeif);
719 feth_unlock();
720
721 feth_config(ifp, NULL);
722 ifnet_detach(ifp);
723 return 0;
724}
725
726static void
727feth_enqueue_input(ifnet_t ifp, struct mbuf * m)
728{
729 struct ifnet_stat_increment_param stats = {};
730
731 stats.packets_in = 1;
732 stats.bytes_in = (uint32_t)mbuf_pkthdr_len(m) + ETHER_HDR_LEN;
733 ifnet_input(ifp, m, &stats);
734}
735
736static struct mbuf *
737copy_mbuf(struct mbuf *m)
738{
0a7de745
A
739 struct mbuf * copy_m;
740 uint32_t pkt_len;
741 uint32_t offset;
5ba3f43e
A
742
743 if ((m->m_flags & M_PKTHDR) == 0) {
0a7de745 744 return NULL;
5ba3f43e
A
745 }
746 pkt_len = m->m_pkthdr.len;
747 MGETHDR(copy_m, M_DONTWAIT, MT_DATA);
748 if (copy_m == NULL) {
749 goto failed;
750 }
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);
758 } else {
759 printf("if_fake: copy_mbuf(): packet too large %d\n",
0a7de745 760 pkt_len);
5ba3f43e
A
761 goto failed;
762 }
763 if (copy_m == NULL || (copy_m->m_flags & M_EXT) == 0) {
764 goto failed;
765 }
766 }
767 mbuf_setlen(copy_m, pkt_len);
768 copy_m->m_pkthdr.len = pkt_len;
9d749ea3 769 copy_m->m_pkthdr.pkt_svc = m->m_pkthdr.pkt_svc;
5ba3f43e
A
770 offset = 0;
771 while (m != NULL && offset < pkt_len) {
0a7de745 772 uint32_t frag_len;
5ba3f43e
A
773
774 frag_len = m->m_len;
775 if (frag_len > (pkt_len - offset)) {
776 printf("if_fake_: Large mbuf fragment %d > %d\n",
0a7de745 777 frag_len, (pkt_len - offset));
5ba3f43e
A
778 goto failed;
779 }
780 m_copydata(m, 0, frag_len, mtod(copy_m, void *) + offset);
781 offset += frag_len;
782 m = m->m_next;
783 }
0a7de745 784 return copy_m;
5ba3f43e 785
0a7de745 786failed:
5ba3f43e
A
787 if (copy_m != NULL) {
788 m_freem(copy_m);
789 }
0a7de745 790 return NULL;
5ba3f43e
A
791}
792
793static void
794feth_output_common(ifnet_t ifp, struct mbuf * m, ifnet_t peer,
0a7de745 795 iff_flags_t flags)
5ba3f43e 796{
0a7de745 797 void * frame_header;
5ba3f43e
A
798
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 =
0a7de745
A
803 CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
804 CSUM_IP_CHECKED | CSUM_IP_VALID;
5ba3f43e
A
805 }
806
807 (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
808 bpf_tap_out(ifp, DLT_EN10MB, m, NULL, 0);
809
810 (void)mbuf_pkthdr_setrcvif(m, peer);
811 mbuf_pkthdr_setheader(m, frame_header);
0a7de745 812 mbuf_pkthdr_adjustlen(m, -ETHER_HDR_LEN);
5ba3f43e 813 (void)mbuf_setdata(m, (char *)mbuf_data(m) + ETHER_HDR_LEN,
0a7de745 814 mbuf_len(m) - ETHER_HDR_LEN);
5ba3f43e 815 bpf_tap_in(peer, DLT_EN10MB, m, frame_header,
0a7de745 816 sizeof(struct ether_header));
5ba3f43e
A
817 feth_enqueue_input(peer, m);
818}
819
820static void
821feth_start(ifnet_t ifp)
822{
0a7de745
A
823 struct mbuf * copy_m = NULL;
824 if_fake_ref fakeif;
825 iff_flags_t flags = 0;
826 ifnet_t peer = NULL;
827 struct mbuf * m;
828 struct mbuf * save_m;
5ba3f43e
A
829
830 feth_lock();
831 fakeif = ifnet_get_if_fake(ifp);
cb323159
A
832 if (fakeif == NULL) {
833 feth_unlock();
834 return;
835 }
836
5ba3f43e
A
837 if (fakeif->iff_start_busy) {
838 feth_unlock();
839 printf("if_fake: start is busy\n");
840 return;
841 }
cb323159
A
842
843 peer = fakeif->iff_peer;
844 flags = fakeif->iff_flags;
5ba3f43e
A
845
846 /* check for pending TX */
847 m = fakeif->iff_pending_tx_packet;
848 if (m != NULL) {
849 if (peer != NULL) {
850 copy_m = copy_mbuf(m);
851 if (copy_m == NULL) {
852 feth_unlock();
853 return;
854 }
855 }
856 fakeif->iff_pending_tx_packet = NULL;
857 m_freem(m);
858 m = NULL;
859 }
860 fakeif->iff_start_busy = TRUE;
861 feth_unlock();
862 save_m = NULL;
863 for (;;) {
864 if (copy_m != NULL) {
865 assert(peer != NULL);
866 feth_output_common(ifp, copy_m, peer, flags);
867 copy_m = NULL;
868 }
869 if (ifnet_dequeue(ifp, &m) != 0) {
870 break;
871 }
872 if (peer == NULL) {
873 m_freem(m);
874 } else {
875 copy_m = copy_mbuf(m);
876 if (copy_m == NULL) {
877 save_m = m;
878 break;
879 }
880 m_freem(m);
881 }
882 }
883 peer = NULL;
884 feth_lock();
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;
891 save_m = NULL;
892 }
893 }
894 feth_unlock();
895 if (save_m != NULL) {
896 /* didn't save packet, so free it */
897 m_freem(save_m);
898 }
899}
900
901static int
902feth_output(ifnet_t ifp, struct mbuf * m)
903{
0a7de745
A
904 struct mbuf * copy_m;
905 if_fake_ref fakeif;
906 iff_flags_t flags;
907 ifnet_t peer = NULL;
5ba3f43e
A
908
909 if (m == NULL) {
0a7de745 910 return 0;
5ba3f43e
A
911 }
912 copy_m = copy_mbuf(m);
913 m_freem(m);
914 m = NULL;
915 if (copy_m == NULL) {
916 /* count this as an output error */
917 ifnet_stat_increment_out(ifp, 0, 0, 1);
0a7de745 918 return 0;
5ba3f43e
A
919 }
920 feth_lock();
921 fakeif = ifnet_get_if_fake(ifp);
922 if (fakeif != NULL) {
923 peer = fakeif->iff_peer;
924 flags = fakeif->iff_flags;
925 }
926 feth_unlock();
927 if (peer == NULL) {
928 m_freem(copy_m);
929 ifnet_stat_increment_out(ifp, 0, 0, 1);
0a7de745 930 return 0;
5ba3f43e
A
931 }
932 feth_output_common(ifp, copy_m, peer, flags);
0a7de745 933 return 0;
5ba3f43e
A
934}
935
936static int
937feth_config(ifnet_t ifp, ifnet_t peer)
938{
0a7de745
A
939 int connected = FALSE;
940 int disconnected = FALSE;
941 int error = 0;
942 if_fake_ref fakeif = NULL;
5ba3f43e
A
943
944 feth_lock();
945 fakeif = ifnet_get_if_fake(ifp);
946 if (fakeif == NULL) {
947 error = EINVAL;
948 goto done;
949 }
950 if (peer != NULL) {
951 /* connect to peer */
0a7de745 952 if_fake_ref peer_fakeif;
5ba3f43e
A
953
954 peer_fakeif = ifnet_get_if_fake(peer);
955 if (peer_fakeif == NULL) {
956 error = EINVAL;
957 goto done;
958 }
959 if (feth_is_detaching(fakeif) ||
960 feth_is_detaching(peer_fakeif) ||
961 peer_fakeif->iff_peer != NULL ||
962 fakeif->iff_peer != NULL) {
963 error = EBUSY;
964 goto done;
965 }
966 fakeif->iff_peer = peer;
967 peer_fakeif->iff_peer = ifp;
968 connected = TRUE;
0a7de745 969 } else if (fakeif->iff_peer != NULL) {
5ba3f43e 970 /* disconnect from peer */
0a7de745 971 if_fake_ref peer_fakeif;
5ba3f43e
A
972
973 peer = fakeif->iff_peer;
974 peer_fakeif = ifnet_get_if_fake(peer);
975 if (peer_fakeif == NULL) {
976 /* should not happen */
977 error = EINVAL;
978 goto done;
979 }
980 fakeif->iff_peer = NULL;
981 peer_fakeif->iff_peer = NULL;
982 disconnected = TRUE;
983 }
984
0a7de745 985done:
5ba3f43e
A
986 feth_unlock();
987
988 /* generate link status event if we connect or disconnect */
989 if (connected) {
5ba3f43e
A
990 interface_link_event(ifp, KEV_DL_LINK_ON);
991 interface_link_event(peer, KEV_DL_LINK_ON);
0a7de745 992 } else if (disconnected) {
5ba3f43e
A
993 interface_link_event(ifp, KEV_DL_LINK_OFF);
994 interface_link_event(peer, KEV_DL_LINK_OFF);
995 }
0a7de745 996 return error;
5ba3f43e
A
997}
998
999static int
1000feth_set_media(ifnet_t ifp, struct if_fake_request * iffr)
1001{
0a7de745
A
1002 if_fake_ref fakeif;
1003 int error;
5ba3f43e
A
1004
1005 if (iffr->iffr_media.iffm_count > IF_FAKE_MEDIA_LIST_MAX) {
1006 /* list is too long */
0a7de745 1007 return EINVAL;
5ba3f43e
A
1008 }
1009 feth_lock();
1010 fakeif = ifnet_get_if_fake(ifp);
1011 if (fakeif == NULL) {
1012 error = EINVAL;
1013 goto done;
1014 }
1015 fakeif->iff_media_count = iffr->iffr_media.iffm_count;
1016 bcopy(iffr->iffr_media.iffm_list, fakeif->iff_media_list,
0a7de745 1017 iffr->iffr_media.iffm_count * sizeof(fakeif->iff_media_list[0]));
5ba3f43e
A
1018#if 0
1019 /* XXX: "auto-negotiate" active with peer? */
1020 /* generate link status event? */
1021 fakeif->iff_media_current = iffr->iffr_media.iffm_current;
1022#endif
1023 error = 0;
0a7de745 1024done:
5ba3f43e 1025 feth_unlock();
0a7de745 1026 return error;
5ba3f43e
A
1027}
1028
1029static int
0a7de745
A
1030if_fake_request_copyin(user_addr_t user_addr,
1031 struct if_fake_request *iffr, u_int32_t len)
5ba3f43e 1032{
0a7de745 1033 int error;
5ba3f43e
A
1034
1035 if (user_addr == USER_ADDR_NULL || len < sizeof(*iffr)) {
1036 error = EINVAL;
1037 goto done;
1038 }
1039 error = copyin(user_addr, iffr, sizeof(*iffr));
1040 if (error != 0) {
1041 goto done;
1042 }
1043 if (iffr->iffr_reserved[0] != 0 || iffr->iffr_reserved[1] != 0 ||
1044 iffr->iffr_reserved[2] != 0 || iffr->iffr_reserved[3] != 0) {
1045 error = EINVAL;
1046 goto done;
1047 }
0a7de745
A
1048done:
1049 return error;
5ba3f43e
A
1050}
1051
1052static int
1053feth_set_drvspec(ifnet_t ifp, uint32_t cmd, u_int32_t len,
0a7de745 1054 user_addr_t user_addr)
5ba3f43e 1055{
0a7de745
A
1056 int error;
1057 struct if_fake_request iffr;
1058 ifnet_t peer;
5ba3f43e
A
1059
1060 switch (cmd) {
1061 case IF_FAKE_S_CMD_SET_PEER:
1062 error = if_fake_request_copyin(user_addr, &iffr, len);
1063 if (error != 0) {
1064 break;
1065 }
1066 if (iffr.iffr_peer_name[0] == '\0') {
1067 error = feth_config(ifp, NULL);
1068 break;
1069 }
1070
1071 /* ensure nul termination */
1072 iffr.iffr_peer_name[IFNAMSIZ - 1] = '\0';
1073 peer = ifunit(iffr.iffr_peer_name);
1074 if (peer == NULL) {
1075 error = ENXIO;
1076 break;
1077 }
1078 if (ifnet_type(peer) != IFT_ETHER) {
1079 error = EINVAL;
1080 break;
1081 }
1082 if (strcmp(ifnet_name(peer), FAKE_ETHER_NAME) != 0) {
1083 error = EINVAL;
1084 break;
1085 }
1086 error = feth_config(ifp, peer);
1087 break;
1088 case IF_FAKE_S_CMD_SET_MEDIA:
1089 error = if_fake_request_copyin(user_addr, &iffr, len);
1090 if (error != 0) {
1091 break;
1092 }
1093 error = feth_set_media(ifp, &iffr);
1094 break;
a39ff7e2
A
1095 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL:
1096 error = if_fake_request_copyin(user_addr, &iffr, len);
1097 if (error != 0) {
1098 break;
1099 }
1100 error = feth_enable_dequeue_stall(ifp,
1101 iffr.iffr_dequeue_stall);
1102 break;
5ba3f43e
A
1103 default:
1104 error = EOPNOTSUPP;
1105 break;
1106 }
0a7de745 1107 return error;
5ba3f43e
A
1108}
1109
1110static int
1111feth_get_drvspec(ifnet_t ifp, u_int32_t cmd, u_int32_t len,
0a7de745 1112 user_addr_t user_addr)
5ba3f43e 1113{
0a7de745
A
1114 int error = EOPNOTSUPP;
1115 if_fake_ref fakeif;
1116 struct if_fake_request iffr;
1117 ifnet_t peer;
5ba3f43e
A
1118
1119 switch (cmd) {
1120 case IF_FAKE_G_CMD_GET_PEER:
1121 if (len < sizeof(iffr)) {
1122 error = EINVAL;
1123 break;
1124 }
1125 feth_lock();
cb323159 1126 fakeif = ifnet_get_if_fake(ifp);
5ba3f43e
A
1127 if (fakeif == NULL) {
1128 feth_unlock();
1129 error = EOPNOTSUPP;
1130 break;
1131 }
1132 peer = fakeif->iff_peer;
1133 feth_unlock();
1134 bzero(&iffr, sizeof(iffr));
1135 if (peer != NULL) {
1136 strlcpy(iffr.iffr_peer_name,
0a7de745
A
1137 if_name(peer),
1138 sizeof(iffr.iffr_peer_name));
5ba3f43e
A
1139 }
1140 error = copyout(&iffr, user_addr, sizeof(iffr));
1141 break;
1142 default:
1143 break;
1144 }
0a7de745 1145 return error;
5ba3f43e
A
1146}
1147
1148union ifdrvu {
0a7de745
A
1149 struct ifdrv32 *ifdrvu_32;
1150 struct ifdrv64 *ifdrvu_64;
1151 void *ifdrvu_p;
5ba3f43e
A
1152};
1153
1154static int
1155feth_ioctl(ifnet_t ifp, u_long cmd, void * data)
1156{
0a7de745
A
1157 unsigned int count;
1158 struct ifdevmtu * devmtu_p;
1159 union ifdrvu drv;
1160 uint32_t drv_cmd;
1161 uint32_t drv_len;
1162 boolean_t drv_set_command = FALSE;
1163 int error = 0;
1164 struct ifmediareq * ifmr;
1165 struct ifreq * ifr;
1166 if_fake_ref fakeif;
1167 int status;
1168 user_addr_t user_addr;
5ba3f43e
A
1169
1170 ifr = (struct ifreq *)data;
1171 switch (cmd) {
1172 case SIOCSIFADDR:
1173 ifnet_set_flags(ifp, IFF_UP, IFF_UP);
1174 break;
1175
1176 case SIOCGIFMEDIA32:
1177 case SIOCGIFMEDIA64:
1178 feth_lock();
cb323159 1179 fakeif = ifnet_get_if_fake(ifp);
5ba3f43e
A
1180 if (fakeif == NULL) {
1181 feth_unlock();
0a7de745 1182 return EOPNOTSUPP;
5ba3f43e
A
1183 }
1184 status = (fakeif->iff_peer != NULL)
1185 ? (IFM_AVALID | IFM_ACTIVE) : IFM_AVALID;
1186 ifmr = (struct ifmediareq *)data;
1187 user_addr = (cmd == SIOCGIFMEDIA64) ?
0a7de745
A
1188 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
1189 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
5ba3f43e
A
1190 count = ifmr->ifm_count;
1191 ifmr->ifm_active = IFM_ETHER;
1192 ifmr->ifm_current = IFM_ETHER;
1193 ifmr->ifm_mask = 0;
1194 ifmr->ifm_status = status;
1195 if (user_addr == USER_ADDR_NULL) {
1196 ifmr->ifm_count = fakeif->iff_media_count;
0a7de745 1197 } else if (count > 0) {
5ba3f43e
A
1198 if (count > fakeif->iff_media_count) {
1199 count = fakeif->iff_media_count;
1200 }
1201 ifmr->ifm_count = count;
1202 error = copyout(&fakeif->iff_media_list, user_addr,
0a7de745 1203 count * sizeof(int));
5ba3f43e
A
1204 }
1205 feth_unlock();
1206 break;
1207
1208 case SIOCGIFDEVMTU:
1209 devmtu_p = &ifr->ifr_devmtu;
1210 devmtu_p->ifdm_current = ifnet_mtu(ifp);
cb323159 1211 devmtu_p->ifdm_max = feth_max_mtu(ifp);
5ba3f43e
A
1212 devmtu_p->ifdm_min = IF_MINMTU;
1213 break;
1214
1215 case SIOCSIFMTU:
cb323159
A
1216 if ((unsigned int)ifr->ifr_mtu > feth_max_mtu(ifp) ||
1217 ifr->ifr_mtu < IF_MINMTU) {
5ba3f43e
A
1218 error = EINVAL;
1219 } else {
1220 error = ifnet_set_mtu(ifp, ifr->ifr_mtu);
1221 }
1222 break;
1223
1224 case SIOCSDRVSPEC32:
1225 case SIOCSDRVSPEC64:
1226 error = proc_suser(current_proc());
1227 if (error != 0) {
1228 break;
1229 }
1230 drv_set_command = TRUE;
0a7de745 1231 /* FALL THROUGH */
5ba3f43e
A
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);
5ba3f43e
A
1239 } else {
1240 drv_cmd = drv.ifdrvu_64->ifd_cmd;
1241 drv_len = drv.ifdrvu_64->ifd_len;
1242 user_addr = drv.ifdrvu_64->ifd_data;
1243 }
1244 if (drv_set_command) {
1245 error = feth_set_drvspec(ifp, drv_cmd, drv_len,
0a7de745 1246 user_addr);
5ba3f43e
A
1247 } else {
1248 error = feth_get_drvspec(ifp, drv_cmd, drv_len,
0a7de745 1249 user_addr);
5ba3f43e
A
1250 }
1251 break;
1252
1253 case SIOCSIFLLADDR:
1254 error = ifnet_set_lladdr(ifp, ifr->ifr_addr.sa_data,
1255 ifr->ifr_addr.sa_len);
1256 break;
1257
1258 case SIOCSIFFLAGS:
a39ff7e2
A
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) {
1262 /* set running */
1263 error = ifnet_set_flags(ifp, IFF_RUNNING,
1264 IFF_RUNNING);
1265 }
1266 } else if ((ifp->if_flags & IFF_RUNNING) != 0) {
1267 /* marked down, clear running */
1268 error = ifnet_set_flags(ifp, 0, IFF_RUNNING);
1269 }
5ba3f43e
A
1270 break;
1271
1272 case SIOCADDMULTI:
1273 case SIOCDELMULTI:
1274 error = 0;
1275 break;
1276 default:
1277 error = EOPNOTSUPP;
1278 break;
1279 }
1280 return error;
1281}
1282
0a7de745 1283static void
5ba3f43e
A
1284feth_if_free(ifnet_t ifp)
1285{
0a7de745 1286 if_fake_ref fakeif;
5ba3f43e
A
1287
1288 if (ifp == NULL) {
1289 return;
1290 }
1291 feth_lock();
1292 fakeif = ifnet_get_if_fake(ifp);
1293 if (fakeif == NULL) {
1294 feth_unlock();
1295 return;
1296 }
1297 ifp->if_softc = NULL;
1298 feth_unlock();
1299 feth_release(fakeif);
1300 ifnet_release(ifp);
1301 return;
1302}
1303
1304__private_extern__ void
1305if_fake_init(void)
1306{
1307 int error;
1308
1309 feth_lock_init();
1310 error = if_clone_attach(&feth_cloner);
1311 if (error != 0) {
1312 return;
1313 }
1314 return;
1315}