/*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
/* Control block allocated for each kernel control connection */
struct utun_pcb {
- kern_ctl_ref ctlref;
- u_int32_t unit;
- ifnet_t ifp;
- u_int32_t flags;
+ kern_ctl_ref utun_ctlref;
+ ifnet_t utun_ifp;
+ u_int32_t utun_unit;
+ u_int32_t utun_flags;
+ int utun_ext_ifdata_stats;
};
static kern_ctl_ref utun_kctlref;
strncpy(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; /* Require root */
+ kern_ctl.ctl_sendsize = 64 * 1024;
+ kern_ctl.ctl_recvsize = 64 * 1024;
kern_ctl.ctl_connect = utun_ctl_connect;
kern_ctl.ctl_disconnect = utun_ctl_disconnect;
kern_ctl.ctl_send = utun_ctl_send;
struct ifnet_init_params utun_init;
struct utun_pcb *pcb;
errno_t result;
+ struct ifnet_stats_param stats;
/* kernel control allocates, interface frees */
pcb = utun_alloc(sizeof(*pcb));
/* Setup the protocol control block */
bzero(pcb, sizeof(*pcb));
*unitinfo = pcb;
- pcb->ctlref = kctlref;
- pcb->unit = sac->sc_unit;
+ pcb->utun_ctlref = kctlref;
+ pcb->utun_unit = sac->sc_unit;
- printf("utun_ctl_connect: creating interface utun%d\n", pcb->unit - 1);
+ printf("utun_ctl_connect: creating interface utun%d\n", pcb->utun_unit - 1);
/* Create the interface */
bzero(&utun_init, sizeof(utun_init));
utun_init.name = "utun";
- utun_init.unit = pcb->unit - 1;
+ utun_init.unit = pcb->utun_unit - 1;
utun_init.family = utun_family;
utun_init.type = IFT_OTHER;
utun_init.output = utun_output;
utun_init.ioctl = utun_ioctl;
utun_init.detach = utun_detached;
- result = ifnet_allocate(&utun_init, &pcb->ifp);
+ result = ifnet_allocate(&utun_init, &pcb->utun_ifp);
if (result != 0) {
printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
utun_free(pcb);
OSIncrementAtomic(&utun_ifcount);
/* Set flags and additional information. */
- ifnet_set_mtu(pcb->ifp, 1500);
- ifnet_set_flags(pcb->ifp, IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT, 0xffff);
+ ifnet_set_mtu(pcb->utun_ifp, 1500);
+ ifnet_set_flags(pcb->utun_ifp, IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT, 0xffff);
/* The interface must generate its own IPv6 LinkLocal address,
* if possible following the recommendation of RFC2472 to the 64bit interface ID
*/
- ifnet_set_eflags(pcb->ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL);
+ ifnet_set_eflags(pcb->utun_ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL);
+ /* Reset the stats in case as the interface may have been recycled */
+ bzero(&stats, sizeof(struct ifnet_stats_param));
+ ifnet_set_stat(pcb->utun_ifp, &stats);
+
/* Attach the interface */
- result = ifnet_attach(pcb->ifp, NULL);
+ result = ifnet_attach(pcb->utun_ifp, NULL);
if (result != 0) {
printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result);
- ifnet_release(pcb->ifp);
+ ifnet_release(pcb->utun_ifp);
utun_free(pcb);
}
/* Attach to bpf */
if (result == 0)
- bpfattach(pcb->ifp, DLT_NULL, 4);
+ bpfattach(pcb->utun_ifp, DLT_NULL, 4);
+
+ /* The interfaces resoures allocated, mark it as running */
+ if (result == 0)
+ ifnet_set_flags(pcb->utun_ifp, IFF_RUNNING, IFF_RUNNING);
return result;
}
goto cleanup;
}
+ /* always set SS_PRIV, we want to close and detach regardless */
+ sock_setpriv(pf_socket, 1);
+
result = utun_detach_ip(interface, protocol, pf_socket);
if (result == 0 || result == ENXIO) {
/* We are done! We either detached or weren't attached. */
void *unitinfo)
{
struct utun_pcb *pcb = unitinfo;
- ifnet_t ifp = pcb->ifp;
+ ifnet_t ifp = pcb->utun_ifp;
errno_t result = 0;
- pcb->ctlref = NULL;
- pcb->unit = 0;
+ pcb->utun_ctlref = NULL;
+ pcb->utun_unit = 0;
/*
* We want to do everything in our power to ensure that the interface
__unused int flags)
{
struct utun_pcb *pcb = unitinfo;
- struct ifnet_stat_increment_param incs;
errno_t result;
- mbuf_pkthdr_setrcvif(m, pcb->ifp);
+ mbuf_pkthdr_setrcvif(m, pcb->utun_ifp);
- bpf_tap_in(pcb->ifp, DLT_NULL, m, 0, 0);
+ bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0);
- if (pcb->flags & UTUN_FLAGS_NO_INPUT) {
+ if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) {
/* flush data */
mbuf_freem(m);
return 0;
}
- bzero(&incs, sizeof(incs));
- incs.packets_in = 1;
- incs.bytes_in = mbuf_pkthdr_len(m);
- result = ifnet_input(pcb->ifp, m, &incs);
+ 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->ifp, 0, 0, 1);
+ ifnet_stat_increment_in(pcb->utun_ifp, 0, 0, 1);
+
printf("utun_ctl_send - ifnet_input failed: %d\n", result);
mbuf_freem(m);
}
/* check for privileges for privileged options */
switch (opt) {
case UTUN_OPT_FLAGS:
+ case UTUN_OPT_EXT_IFDATA_STATS:
if (kauth_cred_issuser(kauth_cred_get()) == 0) {
return EPERM;
}
if (len != sizeof(u_int32_t))
result = EMSGSIZE;
else
- pcb->flags = *(u_int32_t *)data;
+ pcb->utun_flags = *(u_int32_t *)data;
+ 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;
+ }
+
default:
result = ENOPROTOOPT;
break;
if (*len != sizeof(u_int32_t))
result = EMSGSIZE;
else
- *(u_int32_t *)data = pcb->flags;
+ *(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->ifp), ifnet_unit(pcb->ifp)) + 1;
+ *len = snprintf(data, *len, "%s%d", ifnet_name(pcb->utun_ifp), ifnet_unit(pcb->utun_ifp)) + 1;
break;
+
default:
result = ENOPROTOOPT;
break;
struct utun_pcb *pcb = ifnet_softc(interface);
errno_t result;
- bpf_tap_out(pcb->ifp, DLT_NULL, data, 0, 0);
+ bpf_tap_out(pcb->utun_ifp, DLT_NULL, data, 0, 0);
- if (pcb->flags & UTUN_FLAGS_NO_OUTPUT) {
+ if (pcb->utun_flags & UTUN_FLAGS_NO_OUTPUT) {
/* flush data */
mbuf_freem(data);
return 0;
}
- if (pcb->ctlref) {
+ if (pcb->utun_ctlref) {
int length = mbuf_pkthdr_len(data);
- result = ctl_enqueuembuf(pcb->ctlref, pcb->unit, data, CTL_DATA_EOR);
+ 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 {
- ifnet_stat_increment_out(interface, 1, length, 0);
+ if (!pcb->utun_ext_ifdata_stats)
+ ifnet_stat_increment_out(interface, 1, length, 0);
}
}
else
static errno_t
utun_framer(
- __unused ifnet_t interface,
+ __unused ifnet_t interface,
mbuf_t *packet,
__unused const struct sockaddr *dest,
__unused const char *desk_linkaddr,
const char *frame_type)
{
-
if (mbuf_prepend(packet, sizeof(protocol_family_t), MBUF_DONTWAIT) != 0) {
printf("utun_framer - ifnet_output prepend failed\n");
+
ifnet_stat_increment_out(interface, 0, 0, 1);
+
// just return, because the buffer was freed in mbuf_prepend
return EJUSTRETURN;
}
static errno_t
utun_ioctl(
- __unused ifnet_t interface,
- __unused u_long command,
+ ifnet_t interface,
+ u_long command,
void *data)
{
errno_t result = 0;
- struct ifaddr *ifa = (struct ifaddr *)data;
switch(command) {
case SIOCSIFMTU:
ifnet_set_mtu(interface, ((struct ifreq*)data)->ifr_mtu);
break;
-
- case SIOCSIFADDR:
- case SIOCAIFADDR:
- /* This will be called for called for IPv6 Address additions */
- if (ifa->ifa_addr->sa_family == AF_INET6)
- break;
- /* Fall though for other families like IPv4 */
+
+ case SIOCSIFFLAGS:
+ /* ifioctl() takes care of it */
+ break;
default:
result = EOPNOTSUPP;
// remove protocol family first
mbuf_adj(m, sizeof(u_int32_t));
- proto_input(protocol, m);
+ if (proto_input(protocol, m) != 0)
+ m_freem(m);
return 0;
}
__unused char *link_layer_dest)
{
- *(protocol_family_t *)(void *)frame_type = protocol;
- return 0;
+ *(protocol_family_t *)(void *)frame_type = protocol;
+ return 0;
}
static errno_t