X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..143464d58d2bd6378e74eec636961ceb0d32fb91:/bsd/net/if_utun.c?ds=sidebyside diff --git a/bsd/net/if_utun.c b/bsd/net/if_utun.c index a8667845b..c4fe86099 100644 --- a/bsd/net/if_utun.c +++ b/bsd/net/if_utun.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -71,9 +71,9 @@ static errno_t utun_ctl_setopt(kern_ctl_ref kctlref, u_int32_t unit, void *uniti 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); +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); @@ -88,15 +88,7 @@ static errno_t utun_proto_input(ifnet_t interface, protocol_family_t protocol, 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); - -/* Control block allocated for each kernel control connection */ -struct utun_pcb { - kern_ctl_ref utun_ctlref; - ifnet_t utun_ifp; - u_int32_t utun_unit; - u_int32_t utun_flags; - int utun_ext_ifdata_stats; -}; +__private_extern__ errno_t utun_pkt_input (struct utun_pcb *pcb, mbuf_t m); static kern_ctl_ref utun_kctlref; static u_int32_t utun_family; @@ -104,7 +96,7 @@ static OSMallocTag utun_malloc_tag; static SInt32 utun_ifcount = 0; /* Prepend length */ -static void* +void* utun_alloc(size_t size) { size_t *mem = OSMalloc(size + sizeof(size_t), utun_malloc_tag); @@ -117,7 +109,7 @@ utun_alloc(size_t size) return (void*)mem; } -static void +void utun_free(void *ptr) { size_t *size = ptr; @@ -145,14 +137,16 @@ utun_register_control(void) 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_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; - + + utun_ctl_init_crypto(); + result = ctl_register(&kern_ctl, &utun_kctlref); if (result != 0) { printf("utun_register_control - ctl_register failed: %d\n", result); @@ -189,7 +183,7 @@ utun_ctl_connect( struct sockaddr_ctl *sac, void **unitinfo) { - struct ifnet_init_params utun_init; + struct ifnet_init_eparams utun_init; struct utun_pcb *pcb; errno_t result; struct ifnet_stats_param stats; @@ -209,20 +203,23 @@ utun_ctl_connect( /* Create the interface */ bzero(&utun_init, sizeof(utun_init)); + utun_init.ver = IFNET_INIT_CURRENT_VERSION; + utun_init.len = sizeof (utun_init); + utun_init.flags = IFNET_INIT_LEGACY; utun_init.name = "utun"; utun_init.unit = pcb->utun_unit - 1; utun_init.family = utun_family; utun_init.type = IFT_OTHER; utun_init.output = utun_output; utun_init.demux = utun_demux; - utun_init.framer = utun_framer; + 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(&utun_init, &pcb->utun_ifp); + result = ifnet_allocate_extended(&utun_init, &pcb->utun_ifp); if (result != 0) { printf("utun_ctl_connect - ifnet_allocate failed: %d\n", result); utun_free(pcb); @@ -423,7 +420,9 @@ utun_ctl_disconnect( struct utun_pcb *pcb = unitinfo; ifnet_t ifp = pcb->utun_ifp; errno_t result = 0; - + + utun_cleanup_crypto(pcb); + pcb->utun_ctlref = NULL; pcb->utun_unit = 0; @@ -455,37 +454,16 @@ utun_ctl_send( mbuf_t m, __unused int flags) { - struct utun_pcb *pcb = unitinfo; - errno_t result; - - mbuf_pkthdr_setrcvif(m, pcb->utun_ifp); - - bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0); - - if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) { - /* flush data */ - 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("utun_ctl_send - ifnet_input failed: %d\n", result); - mbuf_freem(m); - } - - return 0; + /* + * The userland ABI requires the first four bytes have the protocol family + * in network byte order: swap them + */ + if (m_pktlen(m) >= 4) + *(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 @@ -504,6 +482,7 @@ utun_ctl_setopt( 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; } @@ -518,6 +497,38 @@ utun_ctl_setopt( pcb->utun_flags = *(u_int32_t *)data; break; + case UTUN_OPT_ENABLE_CRYPTO: + result = utun_ctl_enable_crypto(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_CONFIG_CRYPTO_KEYS: + result = utun_ctl_config_crypto_keys(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_UNCONFIG_CRYPTO_KEYS: + result = utun_ctl_unconfig_crypto_keys(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_DISABLE_CRYPTO: + result = utun_ctl_disable_crypto(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_STOP_CRYPTO_DATA_TRAFFIC: + result = utun_ctl_stop_crypto_data_traffic(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_START_CRYPTO_DATA_TRAFFIC: + result = utun_ctl_start_crypto_data_traffic(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_CONFIG_CRYPTO_FRAMER: + result = utun_ctl_config_crypto_framer(kctlref, unit, unitinfo, opt, data, len); + break; + + case UTUN_OPT_UNCONFIG_CRYPTO_FRAMER: + result = utun_ctl_unconfig_crypto_framer(kctlref, unit, unitinfo, opt, data, len); + break; + case UTUN_OPT_EXT_IFDATA_STATS: if (len != sizeof(int)) { result = EMSGSIZE; @@ -546,8 +557,29 @@ utun_ctl_setopt( utsp->utsp_bytes, utsp->utsp_errors); break; } - - default: + + case UTUN_OPT_SET_DELEGATE_INTERFACE: { + ifnet_t del_ifp = NULL; + char name[IFNAMSIZ]; + + if (len > IFNAMSIZ - 1) { + result = EMSGSIZE; + break; + } + if (len != 0) { /* if len==0, del_ifp will be NULL causing the delegate to be removed */ + 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; + } + + default: result = ENOPROTOOPT; break; } @@ -586,6 +618,10 @@ utun_ctl_getopt( *len = snprintf(data, *len, "%s%d", ifnet_name(pcb->utun_ifp), ifnet_unit(pcb->utun_ifp)) + 1; break; + case UTUN_OPT_GENERATE_CRYPTO_KEYS_IDX: + result = utun_ctl_generate_crypto_keys_idx(kctlref, unit, unitinfo, opt, data, len); + break; + default: result = ENOPROTOOPT; break; @@ -603,7 +639,9 @@ utun_output( struct utun_pcb *pcb = ifnet_softc(interface); errno_t result; - bpf_tap_out(pcb->utun_ifp, DLT_NULL, data, 0, 0); + if (m_pktlen(data) >= 4) { + bpf_tap_out(pcb->utun_ifp, DLT_NULL, data, 0, 0); + } if (pcb->utun_flags & UTUN_FLAGS_NO_OUTPUT) { /* flush data */ @@ -611,8 +649,24 @@ utun_output( return 0; } + // otherwise, fall thru to ctl_enqueumbuf if (pcb->utun_ctlref) { - int length = mbuf_pkthdr_len(data); + int length; + + // only pass packets to utun-crypto if crypto is enabled and 'suspend data traffic' is not. + if ((pcb->utun_flags & (UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC)) == UTUN_FLAGS_CRYPTO) { + if (utun_pkt_crypto_output(pcb, &data) == 0) { + return 0; + } + } + + /* + * The ABI requires the protocol in network byte order + */ + if (m_pktlen(data) >= 4) + *(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); @@ -647,7 +701,7 @@ utun_demux( if (data == NULL) return ENOENT; - *protocol = ntohl(*(u_int32_t *)mbuf_data(data)); + *protocol = *(u_int32_t *)mbuf_data(data); return 0; } @@ -657,7 +711,9 @@ utun_framer( mbuf_t *packet, __unused const struct sockaddr *dest, __unused const char *desk_linkaddr, - const char *frame_type) + const char *frame_type, + u_int32_t *prepend_len, + u_int32_t *postpend_len) { if (mbuf_prepend(packet, sizeof(protocol_family_t), MBUF_DONTWAIT) != 0) { printf("utun_framer - ifnet_output prepend failed\n"); @@ -667,9 +723,13 @@ utun_framer( // just return, because the buffer was freed in mbuf_prepend return EJUSTRETURN; } + if (prepend_len != NULL) + *prepend_len = sizeof(protocol_family_t); + if (postpend_len != NULL) + *postpend_len = 0; // place protocol number at the beginning of the mbuf - *(protocol_family_t *)mbuf_data(*packet) = htonl(*(protocol_family_t *)(uintptr_t)(size_t)frame_type); + *(protocol_family_t *)mbuf_data(*packet) = *(protocol_family_t *)(uintptr_t)(size_t)frame_type; return 0; } @@ -791,3 +851,50 @@ utun_attach_proto( return result; } +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) >= 4) { + 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) { + /* flush data */ + mbuf_freem(m); + return 0; + } + + // quick exit for keepalive packets + if (protocol == AF_UTUN && pcb->utun_flags & UTUN_FLAGS_CRYPTO) { + if (utun_pkt_crypto_output(pcb, &m) == 0) { + return 0; + } + printf("%s: utun_pkt_crypto_output failed, flags %x\n", __FUNCTION__, pcb->utun_flags); + return EINVAL; + } + + 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; +}