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