]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/if_fake.c
xnu-4570.51.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 offset = 0;
512 while (m != NULL && offset < pkt_len) {
513 uint32_t frag_len;
514
515 frag_len = m->m_len;
516 if (frag_len > (pkt_len - offset)) {
517 printf("if_fake_: Large mbuf fragment %d > %d\n",
518 frag_len, (pkt_len - offset));
519 goto failed;
520 }
521 m_copydata(m, 0, frag_len, mtod(copy_m, void *) + offset);
522 offset += frag_len;
523 m = m->m_next;
524 }
525 return (copy_m);
526
527 failed:
528 if (copy_m != NULL) {
529 m_freem(copy_m);
530 }
531 return (NULL);
532 }
533
534 static void
535 feth_output_common(ifnet_t ifp, struct mbuf * m, ifnet_t peer,
536 iff_flags_t flags)
537 {
538 void * frame_header;
539
540 frame_header = mbuf_data(m);
541 if ((flags & IFF_FLAGS_HWCSUM) != 0) {
542 m->m_pkthdr.csum_data = 0xffff;
543 m->m_pkthdr.csum_flags =
544 CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
545 CSUM_IP_CHECKED | CSUM_IP_VALID;
546 }
547
548 (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
549 bpf_tap_out(ifp, DLT_EN10MB, m, NULL, 0);
550
551 (void)mbuf_pkthdr_setrcvif(m, peer);
552 mbuf_pkthdr_setheader(m, frame_header);
553 mbuf_pkthdr_adjustlen(m, - ETHER_HDR_LEN);
554 (void)mbuf_setdata(m, (char *)mbuf_data(m) + ETHER_HDR_LEN,
555 mbuf_len(m) - ETHER_HDR_LEN);
556 bpf_tap_in(peer, DLT_EN10MB, m, frame_header,
557 sizeof(struct ether_header));
558 feth_enqueue_input(peer, m);
559 }
560
561 static void
562 feth_start(ifnet_t ifp)
563 {
564 struct mbuf * copy_m = NULL;
565 if_fake_ref fakeif;
566 iff_flags_t flags = 0;
567 ifnet_t peer = NULL;
568 struct mbuf * m;
569 struct mbuf * save_m;
570
571 feth_lock();
572 fakeif = ifnet_get_if_fake(ifp);
573 if (fakeif->iff_start_busy) {
574 feth_unlock();
575 printf("if_fake: start is busy\n");
576 return;
577 }
578 if (fakeif != NULL) {
579 peer = fakeif->iff_peer;
580 flags = fakeif->iff_flags;
581 }
582
583 /* check for pending TX */
584 m = fakeif->iff_pending_tx_packet;
585 if (m != NULL) {
586 if (peer != NULL) {
587 copy_m = copy_mbuf(m);
588 if (copy_m == NULL) {
589 feth_unlock();
590 return;
591 }
592 }
593 fakeif->iff_pending_tx_packet = NULL;
594 m_freem(m);
595 m = NULL;
596 }
597 fakeif->iff_start_busy = TRUE;
598 feth_unlock();
599 save_m = NULL;
600 for (;;) {
601 if (copy_m != NULL) {
602 assert(peer != NULL);
603 feth_output_common(ifp, copy_m, peer, flags);
604 copy_m = NULL;
605 }
606 if (ifnet_dequeue(ifp, &m) != 0) {
607 break;
608 }
609 if (peer == NULL) {
610 m_freem(m);
611 } else {
612 copy_m = copy_mbuf(m);
613 if (copy_m == NULL) {
614 save_m = m;
615 break;
616 }
617 m_freem(m);
618 }
619 }
620 peer = NULL;
621 feth_lock();
622 fakeif = ifnet_get_if_fake(ifp);
623 if (fakeif != NULL) {
624 fakeif->iff_start_busy = FALSE;
625 if (save_m != NULL && fakeif->iff_peer != NULL) {
626 /* save it for next time */
627 fakeif->iff_pending_tx_packet = save_m;
628 save_m = NULL;
629 }
630 }
631 feth_unlock();
632 if (save_m != NULL) {
633 /* didn't save packet, so free it */
634 m_freem(save_m);
635 }
636 }
637
638 static int
639 feth_output(ifnet_t ifp, struct mbuf * m)
640 {
641 struct mbuf * copy_m;
642 if_fake_ref fakeif;
643 iff_flags_t flags;
644 ifnet_t peer = NULL;
645
646 if (m == NULL) {
647 return (0);
648 }
649 copy_m = copy_mbuf(m);
650 m_freem(m);
651 m = NULL;
652 if (copy_m == NULL) {
653 /* count this as an output error */
654 ifnet_stat_increment_out(ifp, 0, 0, 1);
655 return (0);
656 }
657 feth_lock();
658 fakeif = ifnet_get_if_fake(ifp);
659 if (fakeif != NULL) {
660 peer = fakeif->iff_peer;
661 flags = fakeif->iff_flags;
662 }
663 feth_unlock();
664 if (peer == NULL) {
665 m_freem(copy_m);
666 ifnet_stat_increment_out(ifp, 0, 0, 1);
667 return (0);
668 }
669 feth_output_common(ifp, copy_m, peer, flags);
670 return (0);
671 }
672
673 static int
674 feth_config(ifnet_t ifp, ifnet_t peer)
675 {
676 int connected = FALSE;
677 int disconnected = FALSE;
678 int error = 0;
679 if_fake_ref fakeif = NULL;
680
681 feth_lock();
682 fakeif = ifnet_get_if_fake(ifp);
683 if (fakeif == NULL) {
684 error = EINVAL;
685 goto done;
686 }
687 if (peer != NULL) {
688 /* connect to peer */
689 if_fake_ref peer_fakeif;
690
691 peer_fakeif = ifnet_get_if_fake(peer);
692 if (peer_fakeif == NULL) {
693 error = EINVAL;
694 goto done;
695 }
696 if (feth_is_detaching(fakeif) ||
697 feth_is_detaching(peer_fakeif) ||
698 peer_fakeif->iff_peer != NULL ||
699 fakeif->iff_peer != NULL) {
700 error = EBUSY;
701 goto done;
702 }
703 fakeif->iff_peer = peer;
704 peer_fakeif->iff_peer = ifp;
705 connected = TRUE;
706 }
707 else if (fakeif->iff_peer != NULL) {
708 /* disconnect from peer */
709 if_fake_ref peer_fakeif;
710
711 peer = fakeif->iff_peer;
712 peer_fakeif = ifnet_get_if_fake(peer);
713 if (peer_fakeif == NULL) {
714 /* should not happen */
715 error = EINVAL;
716 goto done;
717 }
718 fakeif->iff_peer = NULL;
719 peer_fakeif->iff_peer = NULL;
720 disconnected = TRUE;
721 }
722
723 done:
724 feth_unlock();
725
726 /* generate link status event if we connect or disconnect */
727 if (connected) {
728 interface_link_event(ifp, KEV_DL_LINK_ON);
729 interface_link_event(peer, KEV_DL_LINK_ON);
730 }
731 else if (disconnected) {
732 interface_link_event(ifp, KEV_DL_LINK_OFF);
733 interface_link_event(peer, KEV_DL_LINK_OFF);
734 }
735 return (error);
736 }
737
738 static int
739 feth_set_media(ifnet_t ifp, struct if_fake_request * iffr)
740 {
741 if_fake_ref fakeif;
742 int error;
743
744 if (iffr->iffr_media.iffm_count > IF_FAKE_MEDIA_LIST_MAX) {
745 /* list is too long */
746 return (EINVAL);
747 }
748 feth_lock();
749 fakeif = ifnet_get_if_fake(ifp);
750 if (fakeif == NULL) {
751 error = EINVAL;
752 goto done;
753 }
754 fakeif->iff_media_count = iffr->iffr_media.iffm_count;
755 bcopy(iffr->iffr_media.iffm_list, fakeif->iff_media_list,
756 iffr->iffr_media.iffm_count * sizeof(fakeif->iff_media_list[0]));
757 #if 0
758 /* XXX: "auto-negotiate" active with peer? */
759 /* generate link status event? */
760 fakeif->iff_media_current = iffr->iffr_media.iffm_current;
761 #endif
762 error = 0;
763 done:
764 feth_unlock();
765 return (error);
766 }
767
768 static int
769 if_fake_request_copyin(user_addr_t user_addr,
770 struct if_fake_request *iffr, u_int32_t len)
771 {
772 int error;
773
774 if (user_addr == USER_ADDR_NULL || len < sizeof(*iffr)) {
775 error = EINVAL;
776 goto done;
777 }
778 error = copyin(user_addr, iffr, sizeof(*iffr));
779 if (error != 0) {
780 goto done;
781 }
782 if (iffr->iffr_reserved[0] != 0 || iffr->iffr_reserved[1] != 0 ||
783 iffr->iffr_reserved[2] != 0 || iffr->iffr_reserved[3] != 0) {
784 error = EINVAL;
785 goto done;
786 }
787 done:
788 return (error);
789 }
790
791 static int
792 feth_set_drvspec(ifnet_t ifp, uint32_t cmd, u_int32_t len,
793 user_addr_t user_addr)
794 {
795 int error;
796 struct if_fake_request iffr;
797 ifnet_t peer;
798
799 switch (cmd) {
800 case IF_FAKE_S_CMD_SET_PEER:
801 error = if_fake_request_copyin(user_addr, &iffr, len);
802 if (error != 0) {
803 break;
804 }
805 if (iffr.iffr_peer_name[0] == '\0') {
806 error = feth_config(ifp, NULL);
807 break;
808 }
809
810 /* ensure nul termination */
811 iffr.iffr_peer_name[IFNAMSIZ - 1] = '\0';
812 peer = ifunit(iffr.iffr_peer_name);
813 if (peer == NULL) {
814 error = ENXIO;
815 break;
816 }
817 if (ifnet_type(peer) != IFT_ETHER) {
818 error = EINVAL;
819 break;
820 }
821 if (strcmp(ifnet_name(peer), FAKE_ETHER_NAME) != 0) {
822 error = EINVAL;
823 break;
824 }
825 error = feth_config(ifp, peer);
826 break;
827 case IF_FAKE_S_CMD_SET_MEDIA:
828 error = if_fake_request_copyin(user_addr, &iffr, len);
829 if (error != 0) {
830 break;
831 }
832 error = feth_set_media(ifp, &iffr);
833 break;
834 case IF_FAKE_S_CMD_SET_DEQUEUE_STALL:
835 error = if_fake_request_copyin(user_addr, &iffr, len);
836 if (error != 0) {
837 break;
838 }
839 error = feth_enable_dequeue_stall(ifp,
840 iffr.iffr_dequeue_stall);
841 break;
842 default:
843 error = EOPNOTSUPP;
844 break;
845 }
846 return (error);
847 }
848
849 static int
850 feth_get_drvspec(ifnet_t ifp, u_int32_t cmd, u_int32_t len,
851 user_addr_t user_addr)
852 {
853 int error = EOPNOTSUPP;
854 if_fake_ref fakeif;
855 struct if_fake_request iffr;
856 ifnet_t peer;
857
858 switch (cmd) {
859 case IF_FAKE_G_CMD_GET_PEER:
860 if (len < sizeof(iffr)) {
861 error = EINVAL;
862 break;
863 }
864 feth_lock();
865 fakeif = (if_fake_ref)ifnet_softc(ifp);
866 if (fakeif == NULL) {
867 feth_unlock();
868 error = EOPNOTSUPP;
869 break;
870 }
871 peer = fakeif->iff_peer;
872 feth_unlock();
873 bzero(&iffr, sizeof(iffr));
874 if (peer != NULL) {
875 strlcpy(iffr.iffr_peer_name,
876 if_name(peer),
877 sizeof(iffr.iffr_peer_name));
878 }
879 error = copyout(&iffr, user_addr, sizeof(iffr));
880 break;
881 default:
882 break;
883 }
884 return (error);
885 }
886
887 union ifdrvu {
888 struct ifdrv32 *ifdrvu_32;
889 struct ifdrv64 *ifdrvu_64;
890 void *ifdrvu_p;
891 };
892
893 static int
894 feth_ioctl(ifnet_t ifp, u_long cmd, void * data)
895 {
896 unsigned int count;
897 struct ifdevmtu * devmtu_p;
898 union ifdrvu drv;
899 uint32_t drv_cmd;
900 uint32_t drv_len;
901 boolean_t drv_set_command = FALSE;
902 int error = 0;
903 struct ifmediareq * ifmr;
904 struct ifreq * ifr;
905 if_fake_ref fakeif;
906 int status;
907 user_addr_t user_addr;
908
909 ifr = (struct ifreq *)data;
910 switch (cmd) {
911 case SIOCSIFADDR:
912 ifnet_set_flags(ifp, IFF_UP, IFF_UP);
913 break;
914
915 case SIOCGIFMEDIA32:
916 case SIOCGIFMEDIA64:
917 feth_lock();
918 fakeif = (if_fake_ref)ifnet_softc(ifp);
919 if (fakeif == NULL) {
920 feth_unlock();
921 return (EOPNOTSUPP);
922 }
923 status = (fakeif->iff_peer != NULL)
924 ? (IFM_AVALID | IFM_ACTIVE) : IFM_AVALID;
925 ifmr = (struct ifmediareq *)data;
926 user_addr = (cmd == SIOCGIFMEDIA64) ?
927 ((struct ifmediareq64 *)ifmr)->ifmu_ulist :
928 CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
929 count = ifmr->ifm_count;
930 ifmr->ifm_active = IFM_ETHER;
931 ifmr->ifm_current = IFM_ETHER;
932 ifmr->ifm_mask = 0;
933 ifmr->ifm_status = status;
934 if (user_addr == USER_ADDR_NULL) {
935 ifmr->ifm_count = fakeif->iff_media_count;
936 }
937 else if (count > 0) {
938 if (count > fakeif->iff_media_count) {
939 count = fakeif->iff_media_count;
940 }
941 ifmr->ifm_count = count;
942 error = copyout(&fakeif->iff_media_list, user_addr,
943 count * sizeof(int));
944 }
945 feth_unlock();
946 break;
947
948 case SIOCGIFDEVMTU:
949 devmtu_p = &ifr->ifr_devmtu;
950 devmtu_p->ifdm_current = ifnet_mtu(ifp);
951 devmtu_p->ifdm_max = feth_max_mtu();
952 devmtu_p->ifdm_min = IF_MINMTU;
953 break;
954
955 case SIOCSIFMTU:
956 if (ifr->ifr_mtu > feth_max_mtu() || ifr->ifr_mtu < IF_MINMTU) {
957 error = EINVAL;
958 } else {
959 error = ifnet_set_mtu(ifp, ifr->ifr_mtu);
960 }
961 break;
962
963 case SIOCSDRVSPEC32:
964 case SIOCSDRVSPEC64:
965 error = proc_suser(current_proc());
966 if (error != 0) {
967 break;
968 }
969 drv_set_command = TRUE;
970 /* FALL THROUGH */
971 case SIOCGDRVSPEC32:
972 case SIOCGDRVSPEC64:
973 drv.ifdrvu_p = data;
974 if (cmd == SIOCGDRVSPEC32 || cmd == SIOCSDRVSPEC32) {
975 drv_cmd = drv.ifdrvu_32->ifd_cmd;
976 drv_len = drv.ifdrvu_32->ifd_len;
977 user_addr = CAST_USER_ADDR_T(drv.ifdrvu_32->ifd_data);
978
979 } else {
980 drv_cmd = drv.ifdrvu_64->ifd_cmd;
981 drv_len = drv.ifdrvu_64->ifd_len;
982 user_addr = drv.ifdrvu_64->ifd_data;
983 }
984 if (drv_set_command) {
985 error = feth_set_drvspec(ifp, drv_cmd, drv_len,
986 user_addr);
987 } else {
988 error = feth_get_drvspec(ifp, drv_cmd, drv_len,
989 user_addr);
990 }
991 break;
992
993 case SIOCSIFLLADDR:
994 error = ifnet_set_lladdr(ifp, ifr->ifr_addr.sa_data,
995 ifr->ifr_addr.sa_len);
996 break;
997
998 case SIOCSIFFLAGS:
999 if ((ifp->if_flags & IFF_UP) != 0) {
1000 /* marked up, set running if not already set */
1001 if ((ifp->if_flags & IFF_RUNNING) == 0) {
1002 /* set running */
1003 error = ifnet_set_flags(ifp, IFF_RUNNING,
1004 IFF_RUNNING);
1005 }
1006 } else if ((ifp->if_flags & IFF_RUNNING) != 0) {
1007 /* marked down, clear running */
1008 error = ifnet_set_flags(ifp, 0, IFF_RUNNING);
1009 }
1010 break;
1011
1012 case SIOCADDMULTI:
1013 case SIOCDELMULTI:
1014 error = 0;
1015 break;
1016 default:
1017 error = EOPNOTSUPP;
1018 break;
1019 }
1020 return error;
1021 }
1022
1023 static void
1024 feth_if_free(ifnet_t ifp)
1025 {
1026 if_fake_ref fakeif;
1027
1028 if (ifp == NULL) {
1029 return;
1030 }
1031 feth_lock();
1032 fakeif = ifnet_get_if_fake(ifp);
1033 if (fakeif == NULL) {
1034 feth_unlock();
1035 return;
1036 }
1037 ifp->if_softc = NULL;
1038 feth_unlock();
1039 feth_release(fakeif);
1040 ifnet_release(ifp);
1041 return;
1042 }
1043
1044 __private_extern__ void
1045 if_fake_init(void)
1046 {
1047 int error;
1048
1049 feth_lock_init();
1050 error = if_clone_attach(&feth_cloner);
1051 if (error != 0) {
1052 return;
1053 }
1054 return;
1055 }