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