/*
- * Copyright (c) 2012-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/if_ipsec.h>
-#include <libkern/OSMalloc.h>
-#include <libkern/OSAtomic.h>
#include <sys/mbuf.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/flowadv.h>
#include <net/necp.h>
+#include <netkey/key.h>
+#include <net/pktap.h>
+
+extern int net_qos_policy_restricted;
+extern int net_qos_policy_restrict_avapps;
/* Kernel Control functions */
static errno_t ipsec_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac,
static kern_ctl_ref ipsec_kctlref;
static u_int32_t ipsec_family;
-static OSMallocTag ipsec_malloc_tag;
-static SInt32 ipsec_ifcount = 0;
#define IPSECQ_MAXLEN 256
-/* Prepend length */
-static void*
-ipsec_alloc(size_t size)
-{
- size_t *mem = OSMalloc(size + sizeof(size_t), ipsec_malloc_tag);
-
- if (mem) {
- *mem = size + sizeof(size_t);
- mem++;
- }
-
- return (void*)mem;
-}
-
-static void
-ipsec_free(void *ptr)
-{
- size_t *size = ptr;
- size--;
- OSFree(size, *size, ipsec_malloc_tag);
-}
-
errno_t
ipsec_register_control(void)
{
struct kern_ctl_reg kern_ctl;
errno_t result = 0;
- /* Create a tag to allocate memory */
- ipsec_malloc_tag = OSMalloc_Tagalloc(IPSEC_CONTROL_NAME, OSMT_DEFAULT);
-
/* Find a unique value for our interface family */
result = mbuf_tag_id_find(IPSEC_CONTROL_NAME, &ipsec_family);
if (result != 0) {
struct ifnet_stats_param stats;
/* kernel control allocates, interface frees */
- pcb = ipsec_alloc(sizeof(*pcb));
- if (pcb == NULL)
- return ENOMEM;
-
+ MALLOC(pcb, struct ipsec_pcb *, sizeof(*pcb), M_DEVBUF, M_WAITOK | M_ZERO);
+
/* Setup the protocol control block */
- bzero(pcb, sizeof(*pcb));
*unitinfo = pcb;
pcb->ipsec_ctlref = kctlref;
pcb->ipsec_unit = sac->sc_unit;
result = ifnet_allocate_extended(&ipsec_init, &pcb->ipsec_ifp);
if (result != 0) {
printf("ipsec_ctl_connect - ifnet_allocate failed: %d\n", result);
- ipsec_free(pcb);
+ *unitinfo = NULL;
+ FREE(pcb, M_DEVBUF);
return result;
}
- OSIncrementAtomic(&ipsec_ifcount);
/* Set flags and additional information. */
ifnet_set_mtu(pcb->ipsec_ifp, 1500);
if (result != 0) {
printf("ipsec_ctl_connect - ifnet_allocate failed: %d\n", result);
ifnet_release(pcb->ipsec_ifp);
- ipsec_free(pcb);
- }
-
- /* Attach to bpf */
- if (result == 0)
+ *unitinfo = NULL;
+ FREE(pcb, M_DEVBUF);
+ } else {
+ /* Attach to bpf */
bpfattach(pcb->ipsec_ifp, DLT_NULL, 4);
- /* The interfaces resoures allocated, mark it as running */
- if (result == 0)
+ /* The interfaces resoures allocated, mark it as running */
ifnet_set_flags(pcb->ipsec_ifp, IFF_RUNNING, IFF_RUNNING);
+ }
return result;
}
void *unitinfo)
{
struct ipsec_pcb *pcb = unitinfo;
- ifnet_t ifp = pcb->ipsec_ifp;
+ ifnet_t ifp = NULL;
errno_t result = 0;
-
+
+ if (pcb == NULL)
+ return EINVAL;
+
+ ifp = pcb->ipsec_ifp;
+ VERIFY(ifp != NULL);
pcb->ipsec_ctlref = NULL;
pcb->ipsec_unit = 0;
* addresses and detach the protocols. Finally, we can remove and
* release the interface.
*/
- key_delsp_for_ipsec_if(ifp);
+ key_delsp_for_ipsec_if(ifp);
ipsec_cleanup_family(ifp, AF_INET);
ipsec_cleanup_family(ifp, AF_INET6);
result = ifnet_find_by_name(name, &del_ifp);
}
if (result == 0) {
+ printf("%s IPSEC_OPT_SET_DELEGATE_INTERFACE %s to %s\n",
+ __func__, pcb->ipsec_ifp->if_xname,
+ del_ifp->if_xname);
+
result = ifnet_set_delegate(pcb->ipsec_ifp, del_ifp);
if (del_ifp)
ifnet_release(del_ifp);
} else {
pcb->ipsec_output_service_class = output_service_class;
}
+ printf("%s IPSEC_OPT_OUTPUT_TRAFFIC_CLASS %s svc %d\n",
+ __func__, pcb->ipsec_ifp->if_xname,
+ pcb->ipsec_output_service_class);
break;
}
ipsec_state.dst = (struct sockaddr *)&ip->ip_dst;
bzero(&ipsec_state.ro, sizeof(ipsec_state.ro));
- error = ipsec4_interface_output(&ipsec_state, interface);
+ error = ipsec4_interface_output(&ipsec_state, interface);
+ /* Tunneled in IPv6 - packet is gone */
+ if (error == 0 && ipsec_state.tunneled == 6) {
+ goto done;
+ }
+
data = ipsec_state.m;
if (error || data == NULL) {
printf("ipsec_output: ipsec4_output error %d.\n", error);
ipoa.ipoa_boundif = ipsec_state.outgoing_if;
ipoa.ipoa_flags |= IPOAF_BOUND_IF;
}
+ ipsec_set_ipoa_for_interface(pcb->ipsec_ifp, &ipoa);
adv = &ipoa.ipoa_flowadv;
bpf_tap_out(pcb->ipsec_ifp, DLT_NULL, data, &af, sizeof(af));
data = ipsec6_splithdr(data);
+ if (data == NULL) {
+ printf("ipsec_output: ipsec6_splithdr returned NULL\n");
+ goto ipsec_output_err;
+ }
+
ip6 = mtod(data, struct ip6_hdr *);
bzero(&ipsec_state, sizeof(ipsec_state));
bzero(&ip6oa, sizeof(ip6oa));
ip6oa.ip6oa_flowadv.code = 0;
- ip6oa.ip6oa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
+ ip6oa.ip6oa_flags = IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR;
if (ipsec_state.outgoing_if) {
ip6oa.ip6oa_boundif = ipsec_state.outgoing_if;
- ip6oa.ip6oa_flags |= IPOAF_BOUND_IF;
+ ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF;
}
+ ipsec_set_ip6oa_for_interface(pcb->ipsec_ifp, &ip6oa);
adv = &ip6oa.ip6oa_flowadv;
struct ipsec_pcb *pcb = ifnet_softc(interface);
ifnet_release(pcb->ipsec_ifp);
- ipsec_free(pcb);
-
- OSDecrementAtomic(&ipsec_ifcount);
}
/* Protocol Handlers */
mbuf_pkthdr_setrcvif(m, interface);
bpf_tap_in(interface, DLT_NULL, m, &af, sizeof(af));
-
- if (proto_input(protocol, m) != 0)
+ pktap_input(interface, protocol, m, NULL);
+
+ if (proto_input(protocol, m) != 0) {
+ ifnet_stat_increment_in(interface, 0, 0, 1);
m_freem(m);
+ } else {
+ ifnet_stat_increment_in(interface, 1, m->m_pkthdr.len, 0);
+ }
return 0;
}
if (family == AF_INET) {
struct ip *ip = mtod(packet, struct ip *);
packet->m_pkthdr.pkt_proto = ip->ip_p;
- } else if (family == AF_INET) {
+ } else if (family == AF_INET6) {
struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *);
packet->m_pkthdr.pkt_proto = ip6->ip6_nxt;
}
}
}
}
+
+void
+ipsec_set_ipoa_for_interface(ifnet_t interface, struct ip_out_args *ipoa)
+{
+ struct ipsec_pcb *pcb;
+
+ if (interface == NULL || ipoa == NULL)
+ return;
+ pcb = ifnet_softc(interface);
+
+ if (net_qos_policy_restricted == 0) {
+ ipoa->ipoa_flags |= IPOAF_QOSMARKING_ALLOWED;
+ ipoa->ipoa_sotc = so_svc2tc(pcb->ipsec_output_service_class);
+ } else if (pcb->ipsec_output_service_class != MBUF_SC_VO ||
+ net_qos_policy_restrict_avapps != 0) {
+ ipoa->ipoa_flags &= ~IPOAF_QOSMARKING_ALLOWED;
+ } else {
+ ipoa->ipoa_flags |= IP6OAF_QOSMARKING_ALLOWED;
+ ipoa->ipoa_sotc = SO_TC_VO;
+ }
+}
+
+void
+ipsec_set_ip6oa_for_interface(ifnet_t interface, struct ip6_out_args *ip6oa)
+{
+ struct ipsec_pcb *pcb;
+
+ if (interface == NULL || ip6oa == NULL)
+ return;
+ pcb = ifnet_softc(interface);
+
+ if (net_qos_policy_restricted == 0) {
+ ip6oa->ip6oa_flags |= IPOAF_QOSMARKING_ALLOWED;
+ ip6oa->ip6oa_sotc = so_svc2tc(pcb->ipsec_output_service_class);
+ } else if (pcb->ipsec_output_service_class != MBUF_SC_VO ||
+ net_qos_policy_restrict_avapps != 0) {
+ ip6oa->ip6oa_flags &= ~IPOAF_QOSMARKING_ALLOWED;
+ } else {
+ ip6oa->ip6oa_flags |= IP6OAF_QOSMARKING_ALLOWED;
+ ip6oa->ip6oa_sotc = SO_TC_VO;
+ }
+}