Exemplo n.º 1
0
/* Network Interface functions */
static errno_t
utun_output(
			   ifnet_t	interface,
			   mbuf_t	data)
{
	struct utun_pcb	*pcb = ifnet_softc(interface);
	errno_t			result;
	
	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 */
		mbuf_freem(data);
		return 0;
	}

	// otherwise, fall thru to ctl_enqueumbuf
	if (pcb->utun_ctlref) {
		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);
			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;
}
Exemplo n.º 2
0
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) {
		/* flush data */
		mbuf_freem(data);
		return 0;
	}

	// otherwise, fall thru to ctl_enqueumbuf
	if (pcb->utun_ctlref) {
		int	length;

		/*
		 * The ABI requires the protocol in network byte order
		 */
		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;
}
Exemplo n.º 3
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)
{
    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;	
    }
	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) = *(protocol_family_t *)(uintptr_t)(size_t)frame_type;
    
    return 0;
}
Exemplo n.º 4
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);

		// just	return, because the buffer was freed in mbuf_prepend
        return EJUSTRETURN;	
    }
	if (prepend_len != NULL)
		*prepend_len = header_length;
	if (postpend_len != NULL)
		*postpend_len = 0;
	
    // place protocol number at the beginning of the mbuf
    *(protocol_family_t *)mbuf_data(*packet) = *(protocol_family_t *)(uintptr_t)(size_t)frame_type;


    return 0;
}
Exemplo n.º 5
0
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;
	
	/* check for privileges for privileged options */
	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
				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;
				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) {    /* 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;
	}

	return result;
}
Exemplo n.º 6
0
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;
	/* check for privileges for privileged options */
	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)) {
					// If UTUN_FLAGS_ENABLE_PROC_UUID flag changed, update bpf
					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) {    /* 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;
		}
		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;
}
Exemplo n.º 7
0
/* Network Interface functions */
static errno_t
ipsec_output(ifnet_t	interface,
             mbuf_t     data)
{
	struct ipsec_pcb	*pcb = ifnet_softc(interface);
    struct ipsec_output_state ipsec_state;
    struct route ro;
    struct route_in6 ro6;
    int	length;
    struct ip *ip;
    struct ip6_hdr *ip6;
    struct ip_out_args ipoa;
    struct ip6_out_args ip6oa;
    int error = 0;
    u_int ip_version = 0;
    uint32_t af;
    int flags = 0;
    struct flowadv *adv = NULL;
    
	// Make sure this packet isn't looping through the interface
	if (necp_get_last_interface_index_from_packet(data) == interface->if_index) {
		error = -1;
		goto ipsec_output_err;
	}
	
	// Mark the interface so NECP can evaluate tunnel policy
	necp_mark_packet_from_interface(data, interface);
	
    ip = mtod(data, struct ip *);
    ip_version = ip->ip_v;
	
    switch (ip_version) {
        case 4:
            /* Tap */
            af = AF_INET;
            bpf_tap_out(pcb->ipsec_ifp, DLT_NULL, data, &af, sizeof(af));
			
            /* Apply encryption */
            bzero(&ipsec_state, sizeof(ipsec_state));
            ipsec_state.m = data;
            ipsec_state.dst = (struct sockaddr *)&ip->ip_dst;
            bzero(&ipsec_state.ro, sizeof(ipsec_state.ro));
			
			error = ipsec4_interface_output(&ipsec_state, interface);
            data = ipsec_state.m;
            if (error || data == NULL) {
                printf("ipsec_output: ipsec4_output error %d.\n", error);
                goto ipsec_output_err;
            }
            
            /* Set traffic class, set flow */
            m_set_service_class(data, pcb->ipsec_output_service_class);
            data->m_pkthdr.pkt_flowsrc = FLOWSRC_IFNET;
            data->m_pkthdr.pkt_flowid = interface->if_flowhash;
            data->m_pkthdr.pkt_proto = ip->ip_p;
            data->m_pkthdr.pkt_flags = (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
            
            /* Flip endian-ness for ip_output */
            ip = mtod(data, struct ip *);
            NTOHS(ip->ip_len);
            NTOHS(ip->ip_off);
            
            /* Increment statistics */
            length = mbuf_pkthdr_len(data);
            ifnet_stat_increment_out(interface, 1, length, 0);
			
            /* Send to ip_output */
            bzero(&ro, sizeof(ro));
			
            flags = IP_OUTARGS |	/* Passing out args to specify interface */
			IP_NOIPSEC;				/* To ensure the packet doesn't go through ipsec twice */
			
            bzero(&ipoa, sizeof(ipoa));
            ipoa.ipoa_flowadv.code = 0;
            ipoa.ipoa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
            if (ipsec_state.outgoing_if) {
                ipoa.ipoa_boundif = ipsec_state.outgoing_if;
                ipoa.ipoa_flags |= IPOAF_BOUND_IF;
            }
            
            adv = &ipoa.ipoa_flowadv;
            
            (void) ip_output(data, NULL, &ro, flags, NULL, &ipoa);
            data = NULL;
            
            if (adv->code == FADV_FLOW_CONTROLLED || adv->code == FADV_SUSPENDED) {
                error = ENOBUFS;
                ifnet_disable_output(interface);
            }
            
            goto done;
        case 6:
            af = AF_INET6;
            bpf_tap_out(pcb->ipsec_ifp, DLT_NULL, data, &af, sizeof(af));
            
            data = ipsec6_splithdr(data);
            ip6 = mtod(data, struct ip6_hdr *);
			
            bzero(&ipsec_state, sizeof(ipsec_state));
            ipsec_state.m = data;
            ipsec_state.dst = (struct sockaddr *)&ip6->ip6_dst;
            bzero(&ipsec_state.ro, sizeof(ipsec_state.ro));
            
            error = ipsec6_interface_output(&ipsec_state, interface, &ip6->ip6_nxt, ipsec_state.m);
            if (error == 0 && ipsec_state.tunneled == 4)	/* tunneled in IPv4 - packet is gone */
				goto done;
            data = ipsec_state.m;
            if (error || data == NULL) {
                printf("ipsec_output: ipsec6_output error %d.\n", error);
                goto ipsec_output_err;
            }
            
            /* Set traffic class, set flow */
            m_set_service_class(data, pcb->ipsec_output_service_class);
            data->m_pkthdr.pkt_flowsrc = FLOWSRC_IFNET;
            data->m_pkthdr.pkt_flowid = interface->if_flowhash;
            data->m_pkthdr.pkt_proto = ip6->ip6_nxt;
            data->m_pkthdr.pkt_flags = (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
            
            /* Increment statistics */
            length = mbuf_pkthdr_len(data);
            ifnet_stat_increment_out(interface, 1, length, 0);
            
            /* Send to ip6_output */
            bzero(&ro6, sizeof(ro6));
            
            flags = IPV6_OUTARGS;
            
            bzero(&ip6oa, sizeof(ip6oa));
            ip6oa.ip6oa_flowadv.code = 0;
            ip6oa.ip6oa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
            if (ipsec_state.outgoing_if) {
                ip6oa.ip6oa_boundif = ipsec_state.outgoing_if;
                ip6oa.ip6oa_flags |= IPOAF_BOUND_IF;
            }
            
            adv = &ip6oa.ip6oa_flowadv;
            
            (void) ip6_output(data, NULL, &ro6, flags, NULL, NULL, &ip6oa);
            data = NULL;
            
            if (adv->code == FADV_FLOW_CONTROLLED || adv->code == FADV_SUSPENDED) {
                error = ENOBUFS;
                ifnet_disable_output(interface);
            }
            
            goto done;
        default:
            printf("ipsec_output: Received unknown packet version %d.\n", ip_version);
            error = -1;
            goto ipsec_output_err;
    }
	
done:
    return error;
    
ipsec_output_err:
    if (data)
        mbuf_freem(data);
	goto done;
}
Exemplo n.º 8
0
static errno_t
ipsec_ctl_setopt(__unused kern_ctl_ref	kctlref,
				 __unused u_int32_t		unit,
				 void					*unitinfo,
				 int						opt,
				 void					*data,
				 size_t					len)
{
	struct ipsec_pcb			*pcb = unitinfo;
	errno_t					result = 0;
	
	/* check for privileges for privileged options */
	switch (opt) {
		case IPSEC_OPT_FLAGS:
		case IPSEC_OPT_EXT_IFDATA_STATS:
		case IPSEC_OPT_SET_DELEGATE_INTERFACE:
		case IPSEC_OPT_OUTPUT_TRAFFIC_CLASS:
			if (kauth_cred_issuser(kauth_cred_get()) == 0) {
				return EPERM;
			}
			break;
	}
	
	switch (opt) {
		case IPSEC_OPT_FLAGS:
			if (len != sizeof(u_int32_t))
				result = EMSGSIZE;
			else
				pcb->ipsec_flags = *(u_int32_t *)data;
			break;
			
		case IPSEC_OPT_EXT_IFDATA_STATS:
			if (len != sizeof(int)) {
				result = EMSGSIZE;
				break;
			}
			pcb->ipsec_ext_ifdata_stats = (*(int *)data) ? 1 : 0;
			break;
			
		case IPSEC_OPT_INC_IFDATA_STATS_IN:
		case IPSEC_OPT_INC_IFDATA_STATS_OUT: {
			struct ipsec_stats_param *utsp = (struct ipsec_stats_param *)data;
			
			if (utsp == NULL || len < sizeof(struct ipsec_stats_param)) {
				result = EINVAL;
				break;
			}
			if (!pcb->ipsec_ext_ifdata_stats) {
				result = EINVAL;
				break;
			}
			if (opt == IPSEC_OPT_INC_IFDATA_STATS_IN)
				ifnet_stat_increment_in(pcb->ipsec_ifp, utsp->utsp_packets,
										utsp->utsp_bytes, utsp->utsp_errors);
			else
				ifnet_stat_increment_out(pcb->ipsec_ifp, utsp->utsp_packets,
										 utsp->utsp_bytes, utsp->utsp_errors);
			break;
		}
		
		case IPSEC_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->ipsec_ifp, del_ifp);
				if (del_ifp)
					ifnet_release(del_ifp);
			}
			break;
		}
			
		case IPSEC_OPT_OUTPUT_TRAFFIC_CLASS: {
			if (len != sizeof(int)) {
				result = EMSGSIZE;
				break;
			}
			mbuf_svc_class_t output_service_class = so_tc2msc(*(int *)data);
			if (output_service_class == MBUF_SC_UNSPEC) {
				pcb->ipsec_output_service_class = MBUF_SC_OAM;
			} else {
				pcb->ipsec_output_service_class = output_service_class;
			}
			break;
		}
			
		default:
			result = ENOPROTOOPT;
			break;
	}
	
	return result;
}