]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_utun.c
xnu-1699.24.23.tar.gz
[apple/xnu.git] / bsd / net / if_utun.c
CommitLineData
b0d623f7 1/*
6d2010ae 2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
b0d623f7
A
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
31/* ----------------------------------------------------------------------------------
32Application of kernel control for interface creation
33
34Theory of operation:
35utun (user tunnel) acts as glue between kernel control sockets and network interfaces.
36This kernel control will register an interface for every client that connects.
37---------------------------------------------------------------------------------- */
38
39#include <sys/systm.h>
40#include <sys/kern_control.h>
41#include <net/kpi_protocol.h>
42#include <net/kpi_interface.h>
43#include <sys/socket.h>
44#include <net/if.h>
45#include <net/if_types.h>
46#include <net/bpf.h>
47#include <net/if_utun.h>
48#include <libkern/OSMalloc.h>
49#include <libkern/OSAtomic.h>
50#include <sys/mbuf.h>
51#include <sys/sockio.h>
52#include <netinet/in.h>
53#include <netinet6/in6_var.h>
54#include <netinet6/in6_var.h>
55#include <sys/kauth.h>
56
57
58/* Kernel Control functions */
59static errno_t utun_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac,
60 void **unitinfo);
61static errno_t utun_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit,
62 void *unitinfo);
63static errno_t utun_ctl_send(kern_ctl_ref kctlref, u_int32_t unit,
64 void *unitinfo, mbuf_t m, int flags);
65static errno_t utun_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo,
66 int opt, void *data, size_t *len);
67static errno_t utun_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo,
68 int opt, void *data, size_t len);
69
70/* Network Interface functions */
71static errno_t utun_output(ifnet_t interface, mbuf_t data);
72static errno_t utun_demux(ifnet_t interface, mbuf_t data, char *frame_header,
73 protocol_family_t *protocol);
74static errno_t utun_framer(ifnet_t interface, mbuf_t *packet,
75 const struct sockaddr *dest, const char *desk_linkaddr,
76 const char *frame_type);
77static errno_t utun_add_proto(ifnet_t interface, protocol_family_t protocol,
78 const struct ifnet_demux_desc *demux_array,
79 u_int32_t demux_count);
80static errno_t utun_del_proto(ifnet_t interface, protocol_family_t protocol);
81static errno_t utun_ioctl(ifnet_t interface, u_long cmd, void *data);
82static void utun_detached(ifnet_t interface);
83
84/* Protocol handlers */
85static errno_t utun_attach_proto(ifnet_t interface, protocol_family_t proto);
86static errno_t utun_proto_input(ifnet_t interface, protocol_family_t protocol,
87 mbuf_t m, char *frame_header);
88static errno_t utun_proto_pre_output(ifnet_t interface, protocol_family_t protocol,
89 mbuf_t *packet, const struct sockaddr *dest, void *route,
90 char *frame_type, char *link_layer_dest);
91
92/* Control block allocated for each kernel control connection */
93struct utun_pcb {
d1ecb069
A
94 kern_ctl_ref utun_ctlref;
95 ifnet_t utun_ifp;
96 u_int32_t utun_unit;
97 u_int32_t utun_flags;
98 int utun_ext_ifdata_stats;
b0d623f7
A
99};
100
101static kern_ctl_ref utun_kctlref;
102static u_int32_t utun_family;
103static OSMallocTag utun_malloc_tag;
104static SInt32 utun_ifcount = 0;
105
106/* Prepend length */
107static void*
108utun_alloc(size_t size)
109{
110 size_t *mem = OSMalloc(size + sizeof(size_t), utun_malloc_tag);
111
112 if (mem) {
113 *mem = size + sizeof(size_t);
114 mem++;
115 }
116
117 return (void*)mem;
118}
119
120static void
121utun_free(void *ptr)
122{
123 size_t *size = ptr;
124 size--;
125 OSFree(size, *size, utun_malloc_tag);
126}
127
128errno_t
129utun_register_control(void)
130{
131 struct kern_ctl_reg kern_ctl;
132 errno_t result = 0;
133
134 /* Create a tag to allocate memory */
135 utun_malloc_tag = OSMalloc_Tagalloc(UTUN_CONTROL_NAME, OSMT_DEFAULT);
136
137 /* Find a unique value for our interface family */
138 result = mbuf_tag_id_find(UTUN_CONTROL_NAME, &utun_family);
139 if (result != 0) {
140 printf("utun_register_control - mbuf_tag_id_find_internal failed: %d\n", result);
141 return result;
142 }
143
144 bzero(&kern_ctl, sizeof(kern_ctl));
145 strncpy(kern_ctl.ctl_name, UTUN_CONTROL_NAME, sizeof(kern_ctl.ctl_name));
146 kern_ctl.ctl_name[sizeof(kern_ctl.ctl_name) - 1] = 0;
147 kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED; /* Require root */
d1ecb069
A
148 kern_ctl.ctl_sendsize = 64 * 1024;
149 kern_ctl.ctl_recvsize = 64 * 1024;
b0d623f7
A
150 kern_ctl.ctl_connect = utun_ctl_connect;
151 kern_ctl.ctl_disconnect = utun_ctl_disconnect;
152 kern_ctl.ctl_send = utun_ctl_send;
153 kern_ctl.ctl_setopt = utun_ctl_setopt;
154 kern_ctl.ctl_getopt = utun_ctl_getopt;
155
156 result = ctl_register(&kern_ctl, &utun_kctlref);
157 if (result != 0) {
158 printf("utun_register_control - ctl_register failed: %d\n", result);
159 return result;
160 }
161
162 /* Register the protocol plumbers */
163 if ((result = proto_register_plumber(PF_INET, utun_family,
164 utun_attach_proto, NULL)) != 0) {
165 printf("utun_register_control - proto_register_plumber(PF_INET, %d) failed: %d\n",
166 utun_family, result);
167 ctl_deregister(utun_kctlref);
168 return result;
169 }
170
171 /* Register the protocol plumbers */
172 if ((result = proto_register_plumber(PF_INET6, utun_family,
173 utun_attach_proto, NULL)) != 0) {
174 proto_unregister_plumber(PF_INET, utun_family);
175 ctl_deregister(utun_kctlref);
176 printf("utun_register_control - proto_register_plumber(PF_INET6, %d) failed: %d\n",
177 utun_family, result);
178 return result;
179 }
180
181 return 0;
182}
183
184/* Kernel control functions */
185
186static errno_t
187utun_ctl_connect(
188 kern_ctl_ref kctlref,
189 struct sockaddr_ctl *sac,
190 void **unitinfo)
191{
192 struct ifnet_init_params utun_init;
193 struct utun_pcb *pcb;
194 errno_t result;
d1ecb069 195 struct ifnet_stats_param stats;
b0d623f7
A
196
197 /* kernel control allocates, interface frees */
198 pcb = utun_alloc(sizeof(*pcb));
199 if (pcb == NULL)
200 return ENOMEM;
201
202 /* Setup the protocol control block */
203 bzero(pcb, sizeof(*pcb));
204 *unitinfo = pcb;
d1ecb069
A
205 pcb->utun_ctlref = kctlref;
206 pcb->utun_unit = sac->sc_unit;
b0d623f7 207
d1ecb069 208 printf("utun_ctl_connect: creating interface utun%d\n", pcb->utun_unit - 1);
b0d623f7
A
209
210 /* Create the interface */
211 bzero(&utun_init, sizeof(utun_init));
212 utun_init.name = "utun";
d1ecb069 213 utun_init.unit = pcb->utun_unit - 1;
b0d623f7
A
214 utun_init.family = utun_family;
215 utun_init.type = IFT_OTHER;
216 utun_init.output = utun_output;
217 utun_init.demux = utun_demux;
218 utun_init.framer = utun_framer;
219 utun_init.add_proto = utun_add_proto;
220 utun_init.del_proto = utun_del_proto;
221 utun_init.softc = pcb;
222 utun_init.ioctl = utun_ioctl;
223 utun_init.detach = utun_detached;
224
d1ecb069 225 result = ifnet_allocate(&utun_init, &pcb->utun_ifp);
b0d623f7
A
226 if (result != 0) {
227 printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
228 utun_free(pcb);
229 return result;
230 }
231 OSIncrementAtomic(&utun_ifcount);
232
233 /* Set flags and additional information. */
d1ecb069
A
234 ifnet_set_mtu(pcb->utun_ifp, 1500);
235 ifnet_set_flags(pcb->utun_ifp, IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT, 0xffff);
b0d623f7
A
236
237 /* The interface must generate its own IPv6 LinkLocal address,
238 * if possible following the recommendation of RFC2472 to the 64bit interface ID
239 */
d1ecb069 240 ifnet_set_eflags(pcb->utun_ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL);
b0d623f7 241
d1ecb069
A
242 /* Reset the stats in case as the interface may have been recycled */
243 bzero(&stats, sizeof(struct ifnet_stats_param));
244 ifnet_set_stat(pcb->utun_ifp, &stats);
245
b0d623f7 246 /* Attach the interface */
d1ecb069 247 result = ifnet_attach(pcb->utun_ifp, NULL);
b0d623f7
A
248 if (result != 0) {
249 printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
d1ecb069 250 ifnet_release(pcb->utun_ifp);
b0d623f7
A
251 utun_free(pcb);
252 }
253
254 /* Attach to bpf */
255 if (result == 0)
d1ecb069
A
256 bpfattach(pcb->utun_ifp, DLT_NULL, 4);
257
258 /* The interfaces resoures allocated, mark it as running */
259 if (result == 0)
260 ifnet_set_flags(pcb->utun_ifp, IFF_RUNNING, IFF_RUNNING);
b0d623f7
A
261
262 return result;
263}
264
265static errno_t
266utun_detach_ip(
267 ifnet_t interface,
268 protocol_family_t protocol,
269 socket_t pf_socket)
270{
271 errno_t result = EPROTONOSUPPORT;
272
273 /* Attempt a detach */
274 if (protocol == PF_INET) {
275 struct ifreq ifr;
276
277 bzero(&ifr, sizeof(ifr));
278 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
279 ifnet_name(interface), ifnet_unit(interface));
280
281 result = sock_ioctl(pf_socket, SIOCPROTODETACH, &ifr);
282 }
283 else if (protocol == PF_INET6) {
284 struct in6_ifreq ifr6;
285
286 bzero(&ifr6, sizeof(ifr6));
287 snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d",
288 ifnet_name(interface), ifnet_unit(interface));
289
290 result = sock_ioctl(pf_socket, SIOCPROTODETACH_IN6, &ifr6);
291 }
292
293 return result;
294}
295
296static void
297utun_remove_address(
298 ifnet_t interface,
299 protocol_family_t protocol,
300 ifaddr_t address,
301 socket_t pf_socket)
302{
303 errno_t result = 0;
304
305 /* Attempt a detach */
306 if (protocol == PF_INET) {
307 struct ifreq ifr;
308
309 bzero(&ifr, sizeof(ifr));
310 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
311 ifnet_name(interface), ifnet_unit(interface));
312 result = ifaddr_address(address, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
313 if (result != 0) {
314 printf("utun_remove_address - ifaddr_address failed: %d", result);
315 }
316 else {
317 result = sock_ioctl(pf_socket, SIOCDIFADDR, &ifr);
318 if (result != 0) {
319 printf("utun_remove_address - SIOCDIFADDR failed: %d", result);
320 }
321 }
322 }
323 else if (protocol == PF_INET6) {
324 struct in6_ifreq ifr6;
325
326 bzero(&ifr6, sizeof(ifr6));
327 snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d",
328 ifnet_name(interface), ifnet_unit(interface));
329 result = ifaddr_address(address, (struct sockaddr*)&ifr6.ifr_addr,
330 sizeof(ifr6.ifr_addr));
331 if (result != 0) {
332 printf("utun_remove_address - ifaddr_address failed (v6): %d",
333 result);
334 }
335 else {
336 result = sock_ioctl(pf_socket, SIOCDIFADDR_IN6, &ifr6);
337 if (result != 0) {
338 printf("utun_remove_address - SIOCDIFADDR_IN6 failed: %d",
339 result);
340 }
341 }
342 }
343}
344
345static void
346utun_cleanup_family(
347 ifnet_t interface,
348 protocol_family_t protocol)
349{
350 errno_t result = 0;
351 socket_t pf_socket = NULL;
352 ifaddr_t *addresses = NULL;
353 int i;
354
355 if (protocol != PF_INET && protocol != PF_INET6) {
356 printf("utun_cleanup_family - invalid protocol family %d\n", protocol);
357 return;
358 }
359
360 /* Create a socket for removing addresses and detaching the protocol */
361 result = sock_socket(protocol, SOCK_DGRAM, 0, NULL, NULL, &pf_socket);
362 if (result != 0) {
363 if (result != EAFNOSUPPORT)
364 printf("utun_cleanup_family - failed to create %s socket: %d\n",
365 protocol == PF_INET ? "IP" : "IPv6", result);
366 goto cleanup;
367 }
368
6d2010ae
A
369 /* always set SS_PRIV, we want to close and detach regardless */
370 sock_setpriv(pf_socket, 1);
371
b0d623f7
A
372 result = utun_detach_ip(interface, protocol, pf_socket);
373 if (result == 0 || result == ENXIO) {
374 /* We are done! We either detached or weren't attached. */
375 goto cleanup;
376 }
377 else if (result != EBUSY) {
378 /* Uh, not really sure what happened here... */
379 printf("utun_cleanup_family - utun_detach_ip failed: %d\n", result);
380 goto cleanup;
381 }
382
383 /*
384 * At this point, we received an EBUSY error. This means there are
385 * addresses attached. We should detach them and then try again.
386 */
387 result = ifnet_get_address_list_family(interface, &addresses, protocol);
388 if (result != 0) {
389 printf("fnet_get_address_list_family(%s%d, 0xblah, %s) - failed: %d\n",
390 ifnet_name(interface), ifnet_unit(interface),
391 protocol == PF_INET ? "PF_INET" : "PF_INET6", result);
392 goto cleanup;
393 }
394
395 for (i = 0; addresses[i] != 0; i++) {
396 utun_remove_address(interface, protocol, addresses[i], pf_socket);
397 }
398 ifnet_free_address_list(addresses);
399 addresses = NULL;
400
401 /*
402 * The addresses should be gone, we should try the remove again.
403 */
404 result = utun_detach_ip(interface, protocol, pf_socket);
405 if (result != 0 && result != ENXIO) {
406 printf("utun_cleanup_family - utun_detach_ip failed: %d\n", result);
407 }
408
409cleanup:
410 if (pf_socket != NULL)
411 sock_close(pf_socket);
412
413 if (addresses != NULL)
414 ifnet_free_address_list(addresses);
415}
416
417static errno_t
418utun_ctl_disconnect(
419 __unused kern_ctl_ref kctlref,
420 __unused u_int32_t unit,
421 void *unitinfo)
422{
423 struct utun_pcb *pcb = unitinfo;
d1ecb069 424 ifnet_t ifp = pcb->utun_ifp;
b0d623f7
A
425 errno_t result = 0;
426
d1ecb069
A
427 pcb->utun_ctlref = NULL;
428 pcb->utun_unit = 0;
b0d623f7
A
429
430 /*
431 * We want to do everything in our power to ensure that the interface
432 * really goes away when the socket is closed. We must remove IP/IPv6
433 * addresses and detach the protocols. Finally, we can remove and
434 * release the interface.
435 */
436 utun_cleanup_family(ifp, AF_INET);
437 utun_cleanup_family(ifp, AF_INET6);
438
439 if ((result = ifnet_detach(ifp)) != 0) {
440 printf("utun_ctl_disconnect - ifnet_detach failed: %d\n", result);
441 }
442
443 if ((result = ifnet_release(ifp)) != 0) {
444 printf("utun_ctl_disconnect - ifnet_release failed: %d\n", result);
445 }
446
447 return 0;
448}
449
450static errno_t
451utun_ctl_send(
452 __unused kern_ctl_ref kctlref,
453 __unused u_int32_t unit,
454 void *unitinfo,
455 mbuf_t m,
456 __unused int flags)
457{
458 struct utun_pcb *pcb = unitinfo;
b0d623f7
A
459 errno_t result;
460
d1ecb069 461 mbuf_pkthdr_setrcvif(m, pcb->utun_ifp);
b0d623f7 462
d1ecb069 463 bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0);
b0d623f7 464
d1ecb069 465 if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) {
b0d623f7
A
466 /* flush data */
467 mbuf_freem(m);
468 return 0;
469 }
470
d1ecb069
A
471 if (!pcb->utun_ext_ifdata_stats) {
472 struct ifnet_stat_increment_param incs;
473
474 bzero(&incs, sizeof(incs));
475 incs.packets_in = 1;
476 incs.bytes_in = mbuf_pkthdr_len(m);
477 result = ifnet_input(pcb->utun_ifp, m, &incs);
478 } else {
479 result = ifnet_input(pcb->utun_ifp, m, NULL);
480 }
b0d623f7 481 if (result != 0) {
d1ecb069
A
482 ifnet_stat_increment_in(pcb->utun_ifp, 0, 0, 1);
483
b0d623f7
A
484 printf("utun_ctl_send - ifnet_input failed: %d\n", result);
485 mbuf_freem(m);
486 }
487
488 return 0;
489}
490
491static errno_t
492utun_ctl_setopt(
493 __unused kern_ctl_ref kctlref,
494 __unused u_int32_t unit,
495 void *unitinfo,
496 int opt,
497 void *data,
498 size_t len)
499{
500 struct utun_pcb *pcb = unitinfo;
501 errno_t result = 0;
502
503 /* check for privileges for privileged options */
504 switch (opt) {
505 case UTUN_OPT_FLAGS:
d1ecb069 506 case UTUN_OPT_EXT_IFDATA_STATS:
b0d623f7
A
507 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
508 return EPERM;
509 }
510 break;
511 }
512
513 switch (opt) {
514 case UTUN_OPT_FLAGS:
515 if (len != sizeof(u_int32_t))
516 result = EMSGSIZE;
517 else
d1ecb069
A
518 pcb->utun_flags = *(u_int32_t *)data;
519 break;
520
521 case UTUN_OPT_EXT_IFDATA_STATS:
522 if (len != sizeof(int)) {
523 result = EMSGSIZE;
524 break;
525 }
526 pcb->utun_ext_ifdata_stats = (*(int *)data) ? 1 : 0;
527 break;
528
529 case UTUN_OPT_INC_IFDATA_STATS_IN:
530 case UTUN_OPT_INC_IFDATA_STATS_OUT: {
531 struct utun_stats_param *utsp = (struct utun_stats_param *)data;
532
533 if (utsp == NULL || len < sizeof(struct utun_stats_param)) {
534 result = EINVAL;
535 break;
536 }
537 if (!pcb->utun_ext_ifdata_stats) {
538 result = EINVAL;
539 break;
540 }
541 if (opt == UTUN_OPT_INC_IFDATA_STATS_IN)
542 ifnet_stat_increment_in(pcb->utun_ifp, utsp->utsp_packets,
543 utsp->utsp_bytes, utsp->utsp_errors);
544 else
545 ifnet_stat_increment_out(pcb->utun_ifp, utsp->utsp_packets,
546 utsp->utsp_bytes, utsp->utsp_errors);
b0d623f7 547 break;
d1ecb069
A
548 }
549
b0d623f7
A
550 default:
551 result = ENOPROTOOPT;
552 break;
553 }
554
555 return result;
556}
557
558static errno_t
559utun_ctl_getopt(
560 __unused kern_ctl_ref kctlref,
561 __unused u_int32_t unit,
562 void *unitinfo,
563 int opt,
564 void *data,
565 size_t *len)
566{
567 struct utun_pcb *pcb = unitinfo;
568 errno_t result = 0;
569
570 switch (opt) {
571 case UTUN_OPT_FLAGS:
572 if (*len != sizeof(u_int32_t))
573 result = EMSGSIZE;
574 else
d1ecb069 575 *(u_int32_t *)data = pcb->utun_flags;
b0d623f7 576 break;
d1ecb069
A
577
578 case UTUN_OPT_EXT_IFDATA_STATS:
579 if (*len != sizeof(int))
580 result = EMSGSIZE;
581 else
582 *(int *)data = (pcb->utun_ext_ifdata_stats) ? 1 : 0;
583 break;
584
b0d623f7 585 case UTUN_OPT_IFNAME:
d1ecb069 586 *len = snprintf(data, *len, "%s%d", ifnet_name(pcb->utun_ifp), ifnet_unit(pcb->utun_ifp)) + 1;
b0d623f7 587 break;
d1ecb069 588
b0d623f7
A
589 default:
590 result = ENOPROTOOPT;
591 break;
592 }
593
594 return result;
595}
596
597/* Network Interface functions */
598static errno_t
599utun_output(
600 ifnet_t interface,
601 mbuf_t data)
602{
603 struct utun_pcb *pcb = ifnet_softc(interface);
604 errno_t result;
605
d1ecb069 606 bpf_tap_out(pcb->utun_ifp, DLT_NULL, data, 0, 0);
b0d623f7 607
d1ecb069 608 if (pcb->utun_flags & UTUN_FLAGS_NO_OUTPUT) {
b0d623f7
A
609 /* flush data */
610 mbuf_freem(data);
611 return 0;
612 }
613
d1ecb069 614 if (pcb->utun_ctlref) {
b0d623f7 615 int length = mbuf_pkthdr_len(data);
d1ecb069 616 result = ctl_enqueuembuf(pcb->utun_ctlref, pcb->utun_unit, data, CTL_DATA_EOR);
b0d623f7
A
617 if (result != 0) {
618 mbuf_freem(data);
619 printf("utun_output - ctl_enqueuembuf failed: %d\n", result);
d1ecb069 620
b0d623f7
A
621 ifnet_stat_increment_out(interface, 0, 0, 1);
622 }
623 else {
d1ecb069
A
624 if (!pcb->utun_ext_ifdata_stats)
625 ifnet_stat_increment_out(interface, 1, length, 0);
b0d623f7
A
626 }
627 }
628 else
629 mbuf_freem(data);
630
631 return 0;
632}
633
634/* Network Interface functions */
635static errno_t
636utun_demux(
637 __unused ifnet_t interface,
638 mbuf_t data,
639 __unused char *frame_header,
640 protocol_family_t *protocol)
641{
642
643 while (data != NULL && mbuf_len(data) < 1) {
644 data = mbuf_next(data);
645 }
646
647 if (data == NULL)
648 return ENOENT;
649
650 *protocol = ntohl(*(u_int32_t *)mbuf_data(data));
651 return 0;
652}
653
654static errno_t
655utun_framer(
d1ecb069 656 __unused ifnet_t interface,
b0d623f7
A
657 mbuf_t *packet,
658 __unused const struct sockaddr *dest,
659 __unused const char *desk_linkaddr,
660 const char *frame_type)
661{
b0d623f7
A
662 if (mbuf_prepend(packet, sizeof(protocol_family_t), MBUF_DONTWAIT) != 0) {
663 printf("utun_framer - ifnet_output prepend failed\n");
d1ecb069 664
b0d623f7 665 ifnet_stat_increment_out(interface, 0, 0, 1);
d1ecb069 666
b0d623f7
A
667 // just return, because the buffer was freed in mbuf_prepend
668 return EJUSTRETURN;
669 }
670
671 // place protocol number at the beginning of the mbuf
672 *(protocol_family_t *)mbuf_data(*packet) = htonl(*(protocol_family_t *)(uintptr_t)(size_t)frame_type);
673
674 return 0;
675}
676
677static errno_t
678utun_add_proto(
679 __unused ifnet_t interface,
680 protocol_family_t protocol,
681 __unused const struct ifnet_demux_desc *demux_array,
682 __unused u_int32_t demux_count)
683{
684 switch(protocol) {
685 case PF_INET:
686 return 0;
687 case PF_INET6:
688 return 0;
689 default:
690 break;
691 }
692
693 return ENOPROTOOPT;
694}
695
696static errno_t
697utun_del_proto(
698 __unused ifnet_t interface,
699 __unused protocol_family_t protocol)
700{
701 return 0;
702}
703
704static errno_t
705utun_ioctl(
d1ecb069
A
706 ifnet_t interface,
707 u_long command,
b0d623f7
A
708 void *data)
709{
710 errno_t result = 0;
b0d623f7
A
711
712 switch(command) {
713 case SIOCSIFMTU:
714 ifnet_set_mtu(interface, ((struct ifreq*)data)->ifr_mtu);
715 break;
d1ecb069
A
716
717 case SIOCSIFFLAGS:
718 /* ifioctl() takes care of it */
719 break;
720
b0d623f7
A
721 default:
722 result = EOPNOTSUPP;
723 }
724
725 return result;
726}
727
728static void
729utun_detached(
730 ifnet_t interface)
731{
732 struct utun_pcb *pcb = ifnet_softc(interface);
733
734 utun_free(pcb);
735
736 OSDecrementAtomic(&utun_ifcount);
737}
738
739/* Protocol Handlers */
740
741static errno_t
742utun_proto_input(
743 __unused ifnet_t interface,
744 protocol_family_t protocol,
745 mbuf_t m,
746 __unused char *frame_header)
747{
748
749 // remove protocol family first
750 mbuf_adj(m, sizeof(u_int32_t));
751
6d2010ae
A
752 if (proto_input(protocol, m) != 0)
753 m_freem(m);
b0d623f7
A
754
755 return 0;
756}
757
758static errno_t
759utun_proto_pre_output(
760 __unused ifnet_t interface,
761 protocol_family_t protocol,
762 __unused mbuf_t *packet,
763 __unused const struct sockaddr *dest,
764 __unused void *route,
765 __unused char *frame_type,
766 __unused char *link_layer_dest)
767{
768
6d2010ae
A
769 *(protocol_family_t *)(void *)frame_type = protocol;
770 return 0;
b0d623f7
A
771}
772
773static errno_t
774utun_attach_proto(
775 ifnet_t interface,
776 protocol_family_t protocol)
777{
778 struct ifnet_attach_proto_param proto;
779 errno_t result;
780
781 bzero(&proto, sizeof(proto));
782 proto.input = utun_proto_input;
783 proto.pre_output = utun_proto_pre_output;
784
785 result = ifnet_attach_protocol(interface, protocol, &proto);
786 if (result != 0 && result != EEXIST) {
787 printf("utun_attach_inet - ifnet_attach_protocol %d failed: %d\n",
788 protocol, result);
789 }
790
791 return result;
792}
793