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