#include <sys/systm.h>
#include <sys/kern_control.h>
#include <net/kpi_protocol.h>
#include <net/kpi_interface.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/if_utun.h>
#include <sys/mbuf.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet6/in6_var.h>
#include <sys/kauth.h>
static errno_t utun_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac,
void **unitinfo);
static errno_t utun_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit,
void *unitinfo);
static errno_t utun_ctl_send(kern_ctl_ref kctlref, u_int32_t unit,
void *unitinfo, mbuf_t m, int flags);
static errno_t utun_ctl_getopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo,
int opt, void *data, size_t *len);
static errno_t utun_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo,
int opt, void *data, size_t len);
static void utun_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo,
int flags);
static void utun_start(ifnet_t interface);
static errno_t utun_output(ifnet_t interface, mbuf_t data);
static errno_t utun_demux(ifnet_t interface, mbuf_t data, char *frame_header,
protocol_family_t *protocol);
static errno_t utun_framer(ifnet_t interface, mbuf_t *packet,
const struct sockaddr *dest, const char *desk_linkaddr,
const char *frame_type, u_int32_t *prepend_len, u_int32_t *postpend_len);
static errno_t utun_add_proto(ifnet_t interface, protocol_family_t protocol,
const struct ifnet_demux_desc *demux_array,
u_int32_t demux_count);
static errno_t utun_del_proto(ifnet_t interface, protocol_family_t protocol);
static errno_t utun_ioctl(ifnet_t interface, u_long cmd, void *data);
static void utun_detached(ifnet_t interface);
static errno_t utun_attach_proto(ifnet_t interface, protocol_family_t proto);
static errno_t utun_proto_input(ifnet_t interface, protocol_family_t protocol,
mbuf_t m, char *frame_header);
static errno_t utun_proto_pre_output(ifnet_t interface, protocol_family_t protocol,
mbuf_t *packet, const struct sockaddr *dest, void *route,
char *frame_type, char *link_layer_dest);
static errno_t utun_pkt_input (struct utun_pcb *pcb, mbuf_t m);
#define UTUN_DEFAULT_MTU 1500
#define UTUN_HEADER_SIZE(_pcb) (sizeof(u_int32_t) + (((_pcb)->utun_flags & UTUN_FLAGS_ENABLE_PROC_UUID) ? sizeof(uuid_t) : 0))
static kern_ctl_ref utun_kctlref;
static u_int32_t utun_family;
errno_t
utun_register_control(void)
{
struct kern_ctl_reg kern_ctl;
errno_t result = 0;
result = mbuf_tag_id_find(UTUN_CONTROL_NAME, &utun_family);
if (result != 0) {
printf("utun_register_control - mbuf_tag_id_find_internal failed: %d\n", result);
return result;
}
bzero(&kern_ctl, sizeof(kern_ctl));
strlcpy(kern_ctl.ctl_name, UTUN_CONTROL_NAME, sizeof(kern_ctl.ctl_name));
kern_ctl.ctl_name[sizeof(kern_ctl.ctl_name) - 1] = 0;
kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED | CTL_FLAG_REG_EXTENDED;
kern_ctl.ctl_sendsize = 512 * 1024;
kern_ctl.ctl_recvsize = 512 * 1024;
kern_ctl.ctl_connect = utun_ctl_connect;
kern_ctl.ctl_disconnect = utun_ctl_disconnect;
kern_ctl.ctl_send = utun_ctl_send;
kern_ctl.ctl_setopt = utun_ctl_setopt;
kern_ctl.ctl_getopt = utun_ctl_getopt;
kern_ctl.ctl_rcvd = utun_ctl_rcvd;
result = ctl_register(&kern_ctl, &utun_kctlref);
if (result != 0) {
printf("utun_register_control - ctl_register failed: %d\n", result);
return result;
}
if ((result = proto_register_plumber(PF_INET, utun_family,
utun_attach_proto, NULL)) != 0) {
printf("utun_register_control - proto_register_plumber(PF_INET, %d) failed: %d\n",
utun_family, result);
ctl_deregister(utun_kctlref);
return result;
}
if ((result = proto_register_plumber(PF_INET6, utun_family,
utun_attach_proto, NULL)) != 0) {
proto_unregister_plumber(PF_INET, utun_family);
ctl_deregister(utun_kctlref);
printf("utun_register_control - proto_register_plumber(PF_INET6, %d) failed: %d\n",
utun_family, result);
return result;
}
return 0;
}
static errno_t
utun_ctl_connect(
kern_ctl_ref kctlref,
struct sockaddr_ctl *sac,
void **unitinfo)
{
struct ifnet_init_eparams utun_init;
struct utun_pcb *pcb;
errno_t result;
struct ifnet_stats_param stats;
MALLOC(pcb, struct utun_pcb *, sizeof(*pcb), M_DEVBUF, M_WAITOK | M_ZERO);
*unitinfo = pcb;
pcb->utun_ctlref = kctlref;
pcb->utun_unit = sac->sc_unit;
pcb->utun_max_pending_packets = 1;
printf("utun_ctl_connect: creating interface utun%d\n", pcb->utun_unit - 1);
bzero(&utun_init, sizeof(utun_init));
utun_init.ver = IFNET_INIT_CURRENT_VERSION;
utun_init.len = sizeof (utun_init);
utun_init.name = "utun";
utun_init.start = utun_start;
utun_init.unit = pcb->utun_unit - 1;
utun_init.family = utun_family;
utun_init.subfamily = IFNET_SUBFAMILY_UTUN;
utun_init.type = IFT_OTHER;
utun_init.demux = utun_demux;
utun_init.framer_extended = utun_framer;
utun_init.add_proto = utun_add_proto;
utun_init.del_proto = utun_del_proto;
utun_init.softc = pcb;
utun_init.ioctl = utun_ioctl;
utun_init.detach = utun_detached;
result = ifnet_allocate_extended(&utun_init, &pcb->utun_ifp);
if (result != 0) {
printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
*unitinfo = NULL;
FREE(pcb, M_DEVBUF);
return result;
}
ifnet_set_mtu(pcb->utun_ifp, UTUN_DEFAULT_MTU);
ifnet_set_flags(pcb->utun_ifp, IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT, 0xffff);
ifnet_set_eflags(pcb->utun_ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL);
bzero(&stats, sizeof(struct ifnet_stats_param));
ifnet_set_stat(pcb->utun_ifp, &stats);
result = ifnet_attach(pcb->utun_ifp, NULL);
if (result != 0) {
printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
ifnet_release(pcb->utun_ifp);
*unitinfo = NULL;
FREE(pcb, M_DEVBUF);
} else {
bpfattach(pcb->utun_ifp, DLT_NULL, UTUN_HEADER_SIZE(pcb));
ifnet_set_flags(pcb->utun_ifp, IFF_RUNNING, IFF_RUNNING);
}
return result;
}
static errno_t
utun_detach_ip(
ifnet_t interface,
protocol_family_t protocol,
socket_t pf_socket)
{
errno_t result = EPROTONOSUPPORT;
if (protocol == PF_INET) {
struct ifreq ifr;
bzero(&ifr, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
ifnet_name(interface), ifnet_unit(interface));
result = sock_ioctl(pf_socket, SIOCPROTODETACH, &ifr);
}
else if (protocol == PF_INET6) {
struct in6_ifreq ifr6;
bzero(&ifr6, sizeof(ifr6));
snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d",
ifnet_name(interface), ifnet_unit(interface));
result = sock_ioctl(pf_socket, SIOCPROTODETACH_IN6, &ifr6);
}
return result;
}
static void
utun_remove_address(
ifnet_t interface,
protocol_family_t protocol,
ifaddr_t address,
socket_t pf_socket)
{
errno_t result = 0;
if (protocol == PF_INET) {
struct ifreq ifr;
bzero(&ifr, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
ifnet_name(interface), ifnet_unit(interface));
result = ifaddr_address(address, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
if (result != 0) {
printf("utun_remove_address - ifaddr_address failed: %d", result);
}
else {
result = sock_ioctl(pf_socket, SIOCDIFADDR, &ifr);
if (result != 0) {
printf("utun_remove_address - SIOCDIFADDR failed: %d", result);
}
}
}
else if (protocol == PF_INET6) {
struct in6_ifreq ifr6;
bzero(&ifr6, sizeof(ifr6));
snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d",
ifnet_name(interface), ifnet_unit(interface));
result = ifaddr_address(address, (struct sockaddr*)&ifr6.ifr_addr,
sizeof(ifr6.ifr_addr));
if (result != 0) {
printf("utun_remove_address - ifaddr_address failed (v6): %d",
result);
}
else {
result = sock_ioctl(pf_socket, SIOCDIFADDR_IN6, &ifr6);
if (result != 0) {
printf("utun_remove_address - SIOCDIFADDR_IN6 failed: %d",
result);
}
}
}
}
static void
utun_cleanup_family(
ifnet_t interface,
protocol_family_t protocol)
{
errno_t result = 0;
socket_t pf_socket = NULL;
ifaddr_t *addresses = NULL;
int i;
if (protocol != PF_INET && protocol != PF_INET6) {
printf("utun_cleanup_family - invalid protocol family %d\n", protocol);
return;
}
result = sock_socket(protocol, SOCK_DGRAM, 0, NULL, NULL, &pf_socket);
if (result != 0) {
if (result != EAFNOSUPPORT)
printf("utun_cleanup_family - failed to create %s socket: %d\n",
protocol == PF_INET ? "IP" : "IPv6", result);
goto cleanup;
}
sock_setpriv(pf_socket, 1);
result = utun_detach_ip(interface, protocol, pf_socket);
if (result == 0 || result == ENXIO) {
goto cleanup;
}
else if (result != EBUSY) {
printf("utun_cleanup_family - utun_detach_ip failed: %d\n", result);
goto cleanup;
}
result = ifnet_get_address_list_family(interface, &addresses, protocol);
if (result != 0) {
printf("fnet_get_address_list_family(%s%d, 0xblah, %s) - failed: %d\n",
ifnet_name(interface), ifnet_unit(interface),
protocol == PF_INET ? "PF_INET" : "PF_INET6", result);
goto cleanup;
}
for (i = 0; addresses[i] != 0; i++) {
utun_remove_address(interface, protocol, addresses[i], pf_socket);
}
ifnet_free_address_list(addresses);
addresses = NULL;
result = utun_detach_ip(interface, protocol, pf_socket);
if (result != 0 && result != ENXIO) {
printf("utun_cleanup_family - utun_detach_ip failed: %d\n", result);
}
cleanup:
if (pf_socket != NULL)
sock_close(pf_socket);
if (addresses != NULL)
ifnet_free_address_list(addresses);
}
static errno_t
utun_ctl_disconnect(
__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
void *unitinfo)
{
struct utun_pcb *pcb = unitinfo;
ifnet_t ifp = NULL;
errno_t result = 0;
if (pcb == NULL)
return EINVAL;
ifp = pcb->utun_ifp;
VERIFY(ifp != NULL);
pcb->utun_ctlref = NULL;
pcb->utun_unit = 0;
utun_cleanup_family(ifp, AF_INET);
utun_cleanup_family(ifp, AF_INET6);
if ((result = ifnet_detach(ifp)) != 0) {
printf("utun_ctl_disconnect - ifnet_detach failed: %d\n", result);
}
return 0;
}
static errno_t
utun_ctl_send(
__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
void *unitinfo,
mbuf_t m,
__unused int flags)
{
if (m_pktlen(m) >= (int32_t)UTUN_HEADER_SIZE((struct utun_pcb *)unitinfo)) {
*(protocol_family_t *)mbuf_data(m) = ntohl(*(protocol_family_t *)mbuf_data(m));
} else {
printf("%s - unexpected short mbuf pkt len %d\n", __func__, m_pktlen(m) );
}
return utun_pkt_input((struct utun_pcb *)unitinfo, m);
}
static errno_t
utun_ctl_setopt(
__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
void *unitinfo,
int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
errno_t result = 0;
switch (opt) {
case UTUN_OPT_FLAGS:
case UTUN_OPT_EXT_IFDATA_STATS:
case UTUN_OPT_SET_DELEGATE_INTERFACE:
if (kauth_cred_issuser(kauth_cred_get()) == 0) {
return EPERM;
}
break;
}
switch (opt) {
case UTUN_OPT_FLAGS:
if (len != sizeof(u_int32_t)) {
result = EMSGSIZE;
} else {
u_int32_t old_flags = pcb->utun_flags;
pcb->utun_flags = *(u_int32_t *)data;
if (((old_flags ^ pcb->utun_flags) & UTUN_FLAGS_ENABLE_PROC_UUID)) {
bpfdetach(pcb->utun_ifp);
bpfattach(pcb->utun_ifp, DLT_NULL, UTUN_HEADER_SIZE(pcb));
}
}
break;
case UTUN_OPT_EXT_IFDATA_STATS:
if (len != sizeof(int)) {
result = EMSGSIZE;
break;
}
pcb->utun_ext_ifdata_stats = (*(int *)data) ? 1 : 0;
break;
case UTUN_OPT_INC_IFDATA_STATS_IN:
case UTUN_OPT_INC_IFDATA_STATS_OUT: {
struct utun_stats_param *utsp = (struct utun_stats_param *)data;
if (utsp == NULL || len < sizeof(struct utun_stats_param)) {
result = EINVAL;
break;
}
if (!pcb->utun_ext_ifdata_stats) {
result = EINVAL;
break;
}
if (opt == UTUN_OPT_INC_IFDATA_STATS_IN)
ifnet_stat_increment_in(pcb->utun_ifp, utsp->utsp_packets,
utsp->utsp_bytes, utsp->utsp_errors);
else
ifnet_stat_increment_out(pcb->utun_ifp, utsp->utsp_packets,
utsp->utsp_bytes, utsp->utsp_errors);
break;
}
case UTUN_OPT_SET_DELEGATE_INTERFACE: {
ifnet_t del_ifp = NULL;
char name[IFNAMSIZ];
if (len > IFNAMSIZ - 1) {
result = EMSGSIZE;
break;
}
if (len != 0) {
bcopy(data, name, len);
name[len] = 0;
result = ifnet_find_by_name(name, &del_ifp);
}
if (result == 0) {
result = ifnet_set_delegate(pcb->utun_ifp, del_ifp);
if (del_ifp)
ifnet_release(del_ifp);
}
break;
}
case UTUN_OPT_MAX_PENDING_PACKETS: {
u_int32_t max_pending_packets = 0;
if (len != sizeof(u_int32_t)) {
result = EMSGSIZE;
break;
}
max_pending_packets = *(u_int32_t *)data;
if (max_pending_packets == 0) {
result = EINVAL;
break;
}
pcb->utun_max_pending_packets = max_pending_packets;
break;
}
default: {
result = ENOPROTOOPT;
break;
}
}
return result;
}
static errno_t
utun_ctl_getopt(
__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
void *unitinfo,
int opt,
void *data,
size_t *len)
{
struct utun_pcb *pcb = unitinfo;
errno_t result = 0;
switch (opt) {
case UTUN_OPT_FLAGS:
if (*len != sizeof(u_int32_t))
result = EMSGSIZE;
else
*(u_int32_t *)data = pcb->utun_flags;
break;
case UTUN_OPT_EXT_IFDATA_STATS:
if (*len != sizeof(int))
result = EMSGSIZE;
else
*(int *)data = (pcb->utun_ext_ifdata_stats) ? 1 : 0;
break;
case UTUN_OPT_IFNAME:
*len = snprintf(data, *len, "%s%d", ifnet_name(pcb->utun_ifp), ifnet_unit(pcb->utun_ifp)) + 1;
break;
case UTUN_OPT_MAX_PENDING_PACKETS: {
*len = sizeof(u_int32_t);
*((u_int32_t *)data) = pcb->utun_max_pending_packets;
break;
}
default:
result = ENOPROTOOPT;
break;
}
return result;
}
static void
utun_ctl_rcvd(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, int flags)
{
#pragma unused(flags)
bool reenable_output = false;
struct utun_pcb *pcb = unitinfo;
if (pcb == NULL) {
return;
}
ifnet_lock_exclusive(pcb->utun_ifp);
u_int32_t utun_packet_cnt;
errno_t error_pc = ctl_getenqueuepacketcount(kctlref, unit, &utun_packet_cnt);
if (error_pc != 0) {
printf("utun_ctl_rcvd: ctl_getenqueuepacketcount returned error %d\n", error_pc);
utun_packet_cnt = 0;
}
if (utun_packet_cnt < pcb->utun_max_pending_packets) {
reenable_output = true;
}
if (reenable_output) {
errno_t error = ifnet_enable_output(pcb->utun_ifp);
if (error != 0) {
printf("utun_ctl_rcvd: ifnet_enable_output returned error %d\n", error);
}
}
ifnet_lock_done(pcb->utun_ifp);
}
static void
utun_start(ifnet_t interface)
{
mbuf_t data;
struct utun_pcb *pcb = ifnet_softc(interface);
VERIFY(pcb != NULL);
for (;;) {
bool can_accept_packets = true;
ifnet_lock_shared(pcb->utun_ifp);
u_int32_t utun_packet_cnt;
errno_t error_pc = ctl_getenqueuepacketcount(pcb->utun_ctlref, pcb->utun_unit, &utun_packet_cnt);
if (error_pc != 0) {
printf("utun_start: ctl_getenqueuepacketcount returned error %d\n", error_pc);
utun_packet_cnt = 0;
}
can_accept_packets = (utun_packet_cnt < pcb->utun_max_pending_packets);
if (!can_accept_packets && pcb->utun_ctlref) {
u_int32_t difference = 0;
if (ctl_getenqueuereadable(pcb->utun_ctlref, pcb->utun_unit, &difference) == 0) {
if (difference > 0) {
can_accept_packets = true;
}
}
}
if (!can_accept_packets) {
errno_t error = ifnet_disable_output(interface);
if (error != 0) {
printf("utun_start: ifnet_disable_output returned error %d\n", error);
}
ifnet_lock_done(pcb->utun_ifp);
break;
}
ifnet_lock_done(pcb->utun_ifp);
if (ifnet_dequeue(interface, &data) != 0)
break;
if (utun_output(interface, data) != 0)
break;
}
}
static errno_t
utun_output(ifnet_t interface,
mbuf_t data)
{
struct utun_pcb *pcb = ifnet_softc(interface);
errno_t result;
VERIFY(interface == pcb->utun_ifp);
if (m_pktlen(data) >= (int32_t)UTUN_HEADER_SIZE(pcb)) {
bpf_tap_out(pcb->utun_ifp, DLT_NULL, data, 0, 0);
}
if (pcb->utun_flags & UTUN_FLAGS_NO_OUTPUT) {
mbuf_freem(data);
return 0;
}
if (pcb->utun_ctlref) {
int length;
if (m_pktlen(data) >= (int32_t)UTUN_HEADER_SIZE(pcb)) {
*(u_int32_t *)mbuf_data(data) = htonl(*(u_int32_t *)mbuf_data(data));
}
length = mbuf_pkthdr_len(data);
result = ctl_enqueuembuf(pcb->utun_ctlref, pcb->utun_unit, data, CTL_DATA_EOR);
if (result != 0) {
mbuf_freem(data);
printf("utun_output - ctl_enqueuembuf failed: %d\n", result);
ifnet_stat_increment_out(interface, 0, 0, 1);
}
else {
if (!pcb->utun_ext_ifdata_stats)
ifnet_stat_increment_out(interface, 1, length, 0);
}
}
else
mbuf_freem(data);
return 0;
}
static errno_t
utun_demux(
__unused ifnet_t interface,
mbuf_t data,
__unused char *frame_header,
protocol_family_t *protocol)
{
while (data != NULL && mbuf_len(data) < 1) {
data = mbuf_next(data);
}
if (data == NULL)
return ENOENT;
*protocol = *(u_int32_t *)mbuf_data(data);
return 0;
}
static errno_t
utun_framer(
__unused ifnet_t interface,
mbuf_t *packet,
__unused const struct sockaddr *dest,
__unused const char *desk_linkaddr,
const char *frame_type,
u_int32_t *prepend_len,
u_int32_t *postpend_len)
{
struct utun_pcb *pcb = ifnet_softc(interface);
VERIFY(interface == pcb->utun_ifp);
u_int32_t header_length = UTUN_HEADER_SIZE(pcb);
if (mbuf_prepend(packet, header_length, MBUF_DONTWAIT) != 0) {
printf("utun_framer - ifnet_output prepend failed\n");
ifnet_stat_increment_out(interface, 0, 0, 1);
return EJUSTRETURN;
}
if (prepend_len != NULL)
*prepend_len = header_length;
if (postpend_len != NULL)
*postpend_len = 0;
*(protocol_family_t *)mbuf_data(*packet) = *(protocol_family_t *)(uintptr_t)(size_t)frame_type;
return 0;
}
static errno_t
utun_add_proto(
__unused ifnet_t interface,
protocol_family_t protocol,
__unused const struct ifnet_demux_desc *demux_array,
__unused u_int32_t demux_count)
{
switch(protocol) {
case PF_INET:
return 0;
case PF_INET6:
return 0;
default:
break;
}
return ENOPROTOOPT;
}
static errno_t
utun_del_proto(
__unused ifnet_t interface,
__unused protocol_family_t protocol)
{
return 0;
}
static errno_t
utun_ioctl(
ifnet_t interface,
u_long command,
void *data)
{
errno_t result = 0;
switch(command) {
case SIOCSIFMTU:
ifnet_set_mtu(interface, ((struct ifreq*)data)->ifr_mtu);
break;
case SIOCSIFFLAGS:
break;
default:
result = EOPNOTSUPP;
}
return result;
}
static void
utun_detached(
ifnet_t interface)
{
struct utun_pcb *pcb = ifnet_softc(interface);
FREE(pcb, M_DEVBUF);
(void) ifnet_release(interface);
}
static errno_t
utun_proto_input(
ifnet_t interface,
protocol_family_t protocol,
mbuf_t m,
__unused char *frame_header)
{
struct utun_pcb *pcb = ifnet_softc(interface);
mbuf_adj(m, UTUN_HEADER_SIZE(pcb));
if (proto_input(protocol, m) != 0) {
m_freem(m);
}
return 0;
}
static errno_t
utun_proto_pre_output(
__unused ifnet_t interface,
protocol_family_t protocol,
__unused mbuf_t *packet,
__unused const struct sockaddr *dest,
__unused void *route,
char *frame_type,
__unused char *link_layer_dest)
{
*(protocol_family_t *)(void *)frame_type = protocol;
return 0;
}
static errno_t
utun_attach_proto(
ifnet_t interface,
protocol_family_t protocol)
{
struct ifnet_attach_proto_param proto;
errno_t result;
bzero(&proto, sizeof(proto));
proto.input = utun_proto_input;
proto.pre_output = utun_proto_pre_output;
result = ifnet_attach_protocol(interface, protocol, &proto);
if (result != 0 && result != EEXIST) {
printf("utun_attach_inet - ifnet_attach_protocol %d failed: %d\n",
protocol, result);
}
return result;
}
static errno_t
utun_pkt_input (struct utun_pcb *pcb, mbuf_t m)
{
errno_t result;
protocol_family_t protocol = 0;
mbuf_pkthdr_setrcvif(m, pcb->utun_ifp);
if (m_pktlen(m) >= (int32_t)UTUN_HEADER_SIZE(pcb)) {
protocol = *(u_int32_t *)mbuf_data(m);
bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0);
}
if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) {
mbuf_freem(m);
return 0;
}
if (!pcb->utun_ext_ifdata_stats) {
struct ifnet_stat_increment_param incs;
bzero(&incs, sizeof(incs));
incs.packets_in = 1;
incs.bytes_in = mbuf_pkthdr_len(m);
result = ifnet_input(pcb->utun_ifp, m, &incs);
} else {
result = ifnet_input(pcb->utun_ifp, m, NULL);
}
if (result != 0) {
ifnet_stat_increment_in(pcb->utun_ifp, 0, 0, 1);
printf("%s - ifnet_input failed: %d\n", __FUNCTION__, result);
mbuf_freem(m);
}
return 0;
}
errno_t utun_ctl_register_dtls (void *reg);
int utun_pkt_dtls_input(struct utun_pcb *pcb, mbuf_t *pkt, protocol_family_t family);
void utun_ctl_disable_crypto_dtls(struct utun_pcb *pcb);
errno_t
utun_ctl_register_dtls (void *reg)
{
#pragma unused(reg)
return 0;
}
int
utun_pkt_dtls_input(struct utun_pcb *pcb, mbuf_t *pkt, protocol_family_t family)
{
#pragma unused(pcb)
#pragma unused(pkt)
#pragma unused(family)
return 0;
}
void
utun_ctl_disable_crypto_dtls(struct utun_pcb *pcb)
{
#pragma unused(pcb)
}