]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if_ipsec.c
xnu-3789.60.24.tar.gz
[apple/xnu.git] / bsd / net / if_ipsec.c
index e1aac8335dfade2a1139120ffa5d4f074d92d9cf..9e98a05b9ebe0005f24abd978a1f41c8f70cc0bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2012 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
 #include <net/kpi_protocol.h>
 #include <net/kpi_interface.h>
 #include <sys/socket.h>
 #include <net/kpi_protocol.h>
 #include <net/kpi_interface.h>
 #include <sys/socket.h>
+#include <sys/socketvar.h>
 #include <net/if.h>
 #include <net/if_types.h>
 #include <net/bpf.h>
 #include <net/if_ipsec.h>
 #include <net/if.h>
 #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 <sys/mbuf.h>
 #include <sys/sockio.h>
 #include <netinet/in.h>
 #include <netinet6/ipsec6.h>
 #include <netinet/ip.h>
 #include <net/flowadv.h>
 #include <netinet6/ipsec6.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,
 
 /* Kernel Control functions */
 static errno_t ipsec_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac,
@@ -83,42 +88,15 @@ static errno_t ipsec_proto_pre_output(ifnet_t interface, protocol_family_t proto
 
 static kern_ctl_ref    ipsec_kctlref;
 static u_int32_t       ipsec_family;
 
 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
 
 
 #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;
        
 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) {
        /* Find a unique value for our interface family */
        result = mbuf_tag_id_find(IPSEC_CONTROL_NAME, &ipsec_family);
        if (result != 0) {
@@ -127,7 +105,7 @@ ipsec_register_control(void)
        }
        
        bzero(&kern_ctl, sizeof(kern_ctl));
        }
        
        bzero(&kern_ctl, sizeof(kern_ctl));
-       strncpy(kern_ctl.ctl_name, IPSEC_CONTROL_NAME, sizeof(kern_ctl.ctl_name));
+       strlcpy(kern_ctl.ctl_name, IPSEC_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_name[sizeof(kern_ctl.ctl_name) - 1] = 0;
        kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED; /* Require root */
        kern_ctl.ctl_sendsize = 64 * 1024;
@@ -200,15 +178,13 @@ ipsec_ctl_connect(kern_ctl_ref            kctlref,
        struct ifnet_stats_param        stats;
        
        /* kernel control allocates, interface frees */
        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 */
        /* Setup the protocol control block */
-       bzero(pcb, sizeof(*pcb));
        *unitinfo = pcb;
        pcb->ipsec_ctlref = kctlref;
        pcb->ipsec_unit = sac->sc_unit;
        *unitinfo = pcb;
        pcb->ipsec_ctlref = kctlref;
        pcb->ipsec_unit = sac->sc_unit;
+       pcb->ipsec_output_service_class = MBUF_SC_OAM;
        
        printf("ipsec_ctl_connect: creating interface ipsec%d\n", pcb->ipsec_unit - 1);
        
        
        printf("ipsec_ctl_connect: creating interface ipsec%d\n", pcb->ipsec_unit - 1);
        
@@ -217,8 +193,7 @@ ipsec_ctl_connect(kern_ctl_ref              kctlref,
        ipsec_init.ver = IFNET_INIT_CURRENT_VERSION;
        ipsec_init.len = sizeof (ipsec_init);
        ipsec_init.name = "ipsec";
        ipsec_init.ver = IFNET_INIT_CURRENT_VERSION;
        ipsec_init.len = sizeof (ipsec_init);
        ipsec_init.name = "ipsec";
-    ipsec_init.start = ipsec_start;
-    ipsec_init.sndq_maxlen = IPSECQ_MAXLEN;
+       ipsec_init.start = ipsec_start;
        ipsec_init.unit = pcb->ipsec_unit - 1;
        ipsec_init.family = ipsec_family;
        ipsec_init.type = IFT_OTHER;
        ipsec_init.unit = pcb->ipsec_unit - 1;
        ipsec_init.family = ipsec_family;
        ipsec_init.type = IFT_OTHER;
@@ -232,10 +207,10 @@ ipsec_ctl_connect(kern_ctl_ref            kctlref,
        result = ifnet_allocate_extended(&ipsec_init, &pcb->ipsec_ifp);
        if (result != 0) {
                printf("ipsec_ctl_connect - ifnet_allocate failed: %d\n", result);
        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;
        }
                return result;
        }
-       OSIncrementAtomic(&ipsec_ifcount);
        
        /* Set flags and additional information. */
        ifnet_set_mtu(pcb->ipsec_ifp, 1500);
        
        /* Set flags and additional information. */
        ifnet_set_mtu(pcb->ipsec_ifp, 1500);
@@ -255,16 +230,15 @@ ipsec_ctl_connect(kern_ctl_ref            kctlref,
        if (result != 0) {
                printf("ipsec_ctl_connect - ifnet_allocate failed: %d\n", result);
                ifnet_release(pcb->ipsec_ifp);
        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);
        
                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);
                ifnet_set_flags(pcb->ipsec_ifp, IFF_RUNNING, IFF_RUNNING);
+       }
        
        return result;
 }
        
        return result;
 }
@@ -424,9 +398,14 @@ ipsec_ctl_disconnect(__unused kern_ctl_ref kctlref,
                                         void                                   *unitinfo)
 {
        struct ipsec_pcb        *pcb = unitinfo;
                                         void                                   *unitinfo)
 {
        struct ipsec_pcb        *pcb = unitinfo;
-       ifnet_t                 ifp = pcb->ipsec_ifp;
+       ifnet_t                 ifp = NULL;
        errno_t                 result = 0;
        errno_t                 result = 0;
-       
+
+       if (pcb == NULL)
+               return EINVAL;
+
+       ifp = pcb->ipsec_ifp;
+       VERIFY(ifp != NULL);
        pcb->ipsec_ctlref = NULL;
        pcb->ipsec_unit = 0;
        
        pcb->ipsec_ctlref = NULL;
        pcb->ipsec_unit = 0;
        
@@ -436,7 +415,7 @@ ipsec_ctl_disconnect(__unused kern_ctl_ref  kctlref,
         * addresses and detach the protocols. Finally, we can remove and
         * release the interface.
         */
         * 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);
     
        ipsec_cleanup_family(ifp, AF_INET);
        ipsec_cleanup_family(ifp, AF_INET6);
@@ -476,6 +455,7 @@ ipsec_ctl_setopt(__unused kern_ctl_ref      kctlref,
                case IPSEC_OPT_FLAGS:
                case IPSEC_OPT_EXT_IFDATA_STATS:
                case IPSEC_OPT_SET_DELEGATE_INTERFACE:
                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;
                        }
                        if (kauth_cred_issuser(kauth_cred_get()) == 0) {
                                return EPERM;
                        }
@@ -533,6 +513,10 @@ ipsec_ctl_setopt(__unused kern_ctl_ref     kctlref,
                                result = ifnet_find_by_name(name, &del_ifp);
                        }
                        if (result == 0) {
                                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);
                                result = ifnet_set_delegate(pcb->ipsec_ifp, del_ifp);
                                if (del_ifp)
                                        ifnet_release(del_ifp);
@@ -540,6 +524,23 @@ ipsec_ctl_setopt(__unused kern_ctl_ref     kctlref,
                        break;
                }
                        
                        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;
+                       }
+                       printf("%s IPSEC_OPT_OUTPUT_TRAFFIC_CLASS %s svc %d\n",
+                               __func__, pcb->ipsec_ifp->if_xname, 
+                               pcb->ipsec_output_service_class);
+                       break;
+               }
+                       
                default:
                        result = ENOPROTOOPT;
                        break;
                default:
                        result = ENOPROTOOPT;
                        break;
@@ -578,6 +579,14 @@ ipsec_ctl_getopt(__unused kern_ctl_ref     kctlref,
                        *len = snprintf(data, *len, "%s%d", ifnet_name(pcb->ipsec_ifp), ifnet_unit(pcb->ipsec_ifp)) + 1;
                        break;
                        
                        *len = snprintf(data, *len, "%s%d", ifnet_name(pcb->ipsec_ifp), ifnet_unit(pcb->ipsec_ifp)) + 1;
                        break;
                        
+               case IPSEC_OPT_OUTPUT_TRAFFIC_CLASS: {
+                       if (*len != sizeof(int)) {
+                               result = EMSGSIZE;
+                               break;
+                       }
+                       *(int *)data = so_svc2tc(pcb->ipsec_output_service_class);
+                       break;
+               }
                default:
                        result = ENOPROTOOPT;
                        break;
                default:
                        result = ENOPROTOOPT;
                        break;
@@ -598,36 +607,26 @@ ipsec_output(ifnet_t      interface,
     int        length;
     struct ip *ip;
     struct ip6_hdr *ip6;
     int        length;
     struct ip *ip;
     struct ip6_hdr *ip6;
-    struct secpolicy *sp = NULL;
     struct ip_out_args ipoa;
     struct ip6_out_args ip6oa;
     int error = 0;
     u_int ip_version = 0;
     uint32_t af;
     struct ip_out_args ipoa;
     struct ip6_out_args ip6oa;
     int error = 0;
     u_int ip_version = 0;
     uint32_t af;
-    int flags = 0;;
-    int out_interface_index = 0;
+    int flags = 0;
     struct flowadv *adv = NULL;
     
     struct flowadv *adv = NULL;
     
-    uint32_t policy_id = 0;
-    
-    /* Find policy using ID in mbuf */
-    policy_id = data->m_pkthdr.ipsec_policy;
-       sp = key_getspbyid(policy_id);
+       // 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);
        
        
-    if (sp == NULL) {
-        printf("ipsec_output: No policy specified, dropping packet.\n");
-        goto ipsec_output_err;
-    }
-    
-    /* Validate policy */
-    if (sp->ipsec_if != pcb->ipsec_ifp) {
-        printf("ipsec_output: Selected policy does not match %s interface.\n", pcb->ipsec_ifp->if_xname);
-        goto ipsec_output_err;
-    }
-    
     ip = mtod(data, struct ip *);
     ip_version = ip->ip_v;
     ip = mtod(data, struct ip *);
     ip_version = ip->ip_v;
-    
+       
     switch (ip_version) {
         case 4:
             /* Tap */
     switch (ip_version) {
         case 4:
             /* Tap */
@@ -637,18 +636,23 @@ ipsec_output(ifnet_t      interface,
             /* Apply encryption */
             bzero(&ipsec_state, sizeof(ipsec_state));
             ipsec_state.m = data;
             /* Apply encryption */
             bzero(&ipsec_state, sizeof(ipsec_state));
             ipsec_state.m = data;
-            ipsec_state.dst = (struct sockaddr *)&sp->spidx.dst;
+            ipsec_state.dst = (struct sockaddr *)&ip->ip_dst;
             bzero(&ipsec_state.ro, sizeof(ipsec_state.ro));
                        
             bzero(&ipsec_state.ro, sizeof(ipsec_state.ro));
                        
-            error = ipsec4_output(&ipsec_state, sp, 0);
+            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) {
             data = ipsec_state.m;
             if (error || data == NULL) {
-                printf("ipsec_output: ipsec4_output error.\n");
+                printf("ipsec_output: ipsec4_output error %d.\n", error);
                 goto ipsec_output_err;
             }
             
                 goto ipsec_output_err;
             }
             
-            /* Set traffic class to OAM, set flow */
-            m_set_service_class(data, MBUF_SC_OAM);
+            /* 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_flowsrc = FLOWSRC_IFNET;
             data->m_pkthdr.pkt_flowid = interface->if_flowhash;
             data->m_pkthdr.pkt_proto = ip->ip_p;
@@ -666,20 +670,17 @@ ipsec_output(ifnet_t      interface,
             /* Send to ip_output */
             bzero(&ro, sizeof(ro));
                        
             /* 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 */
-                       
-            if (sp->outgoing_if != NULL) {
-                out_interface_index = sp->outgoing_if->if_index;
-            }
+            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;
                        
             bzero(&ipoa, sizeof(ipoa));
             ipoa.ipoa_flowadv.code = 0;
             ipoa.ipoa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
-            if (out_interface_index) {
-                ipoa.ipoa_boundif = out_interface_index;
+            if (ipsec_state.outgoing_if) {
+                ipoa.ipoa_boundif = ipsec_state.outgoing_if;
                 ipoa.ipoa_flags |= IPOAF_BOUND_IF;
             }
                 ipoa.ipoa_flags |= IPOAF_BOUND_IF;
             }
+            ipsec_set_ipoa_for_interface(pcb->ipsec_ifp, &ipoa);
             
             adv = &ipoa.ipoa_flowadv;
             
             
             adv = &ipoa.ipoa_flowadv;
             
@@ -696,29 +697,33 @@ ipsec_output(ifnet_t      interface,
             af = AF_INET6;
             bpf_tap_out(pcb->ipsec_ifp, DLT_NULL, data, &af, sizeof(af));
             
             af = AF_INET6;
             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 *);
             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));
             
             
-            u_char *nexthdrp = &ip6->ip6_nxt;
-            struct mbuf *mprev = data;
-            
-            int needipsectun = 0;
-            error = ipsec6_output_trans(&ipsec_state, nexthdrp, mprev, sp, flags, &needipsectun);
-            if (needipsectun) {
-                error = ipsec6_output_tunnel(&ipsec_state, sp, flags);
-                if (ipsec_state.tunneled == 4) /* tunneled in IPv4 - packet is gone */
-                    goto done;
-            }
+            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) {
             data = ipsec_state.m;
             if (error || data == NULL) {
-                printf("ipsec_output: ipsec6_output error.\n");
+                printf("ipsec_output: ipsec6_output error %d.\n", error);
                 goto ipsec_output_err;
             }
             
                 goto ipsec_output_err;
             }
             
-            /* Set traffic class to OAM, set flow */
-            m_set_service_class(data, MBUF_SC_OAM);
+            /* 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_flowsrc = FLOWSRC_IFNET;
             data->m_pkthdr.pkt_flowid = interface->if_flowhash;
-            data->m_pkthdr.pkt_proto = ip->ip_p;
+            data->m_pkthdr.pkt_proto = ip6->ip6_nxt;
             data->m_pkthdr.pkt_flags = (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
             
             /* Increment statistics */
             data->m_pkthdr.pkt_flags = (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
             
             /* Increment statistics */
@@ -730,17 +735,14 @@ ipsec_output(ifnet_t      interface,
             
             flags = IPV6_OUTARGS;
             
             
             flags = IPV6_OUTARGS;
             
-            if (sp->outgoing_if != NULL) {
-                out_interface_index = sp->outgoing_if->if_index;
-            }
-            
             bzero(&ip6oa, sizeof(ip6oa));
             ip6oa.ip6oa_flowadv.code = 0;
             bzero(&ip6oa, sizeof(ip6oa));
             ip6oa.ip6oa_flowadv.code = 0;
-            ip6oa.ip6oa_flags = IPOAF_SELECT_SRCIF | IPOAF_BOUND_SRCADDR;
-            if (out_interface_index) {
-                ip6oa.ip6oa_boundif = out_interface_index;
-                ip6oa.ip6oa_flags |= IPOAF_BOUND_IF;
+            ip6oa.ip6oa_flags = IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR;
+            if (ipsec_state.outgoing_if) {
+                ip6oa.ip6oa_boundif = ipsec_state.outgoing_if;
+                ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF;
             }
             }
+            ipsec_set_ip6oa_for_interface(pcb->ipsec_ifp, &ip6oa);
             
             adv = &ip6oa.ip6oa_flowadv;
             
             
             adv = &ip6oa.ip6oa_flowadv;
             
@@ -760,9 +762,6 @@ ipsec_output(ifnet_t        interface,
     }
        
 done:
     }
        
 done:
-    if (sp != NULL) {
-        key_freesp(sp, KEY_SADB_UNLOCKED);
-    }
     return error;
     
 ipsec_output_err:
     return error;
     
 ipsec_output_err:
@@ -774,13 +773,14 @@ ipsec_output_err:
 static void
 ipsec_start(ifnet_t    interface)
 {
 static void
 ipsec_start(ifnet_t    interface)
 {
-    mbuf_t data;
-    
-    for (;;) {
-        if (ifnet_dequeue(interface, &data) != 0)
-            break;
-        (void) ipsec_output(interface, data);
-    }
+       mbuf_t data;
+
+       for (;;) {
+               if (ifnet_dequeue(interface, &data) != 0)
+                       break;
+               if (ipsec_output(interface, data) != 0)
+                       break;
+       }
 }
 
 /* Network Interface functions */
 }
 
 /* Network Interface functions */
@@ -872,21 +872,34 @@ ipsec_detached(
        struct ipsec_pcb        *pcb = ifnet_softc(interface);
     
        ifnet_release(pcb->ipsec_ifp);
        struct ipsec_pcb        *pcb = ifnet_softc(interface);
     
        ifnet_release(pcb->ipsec_ifp);
-       ipsec_free(pcb);
-    
-       OSDecrementAtomic(&ipsec_ifcount);
 }
 
 /* Protocol Handlers */
 
 static errno_t
 }
 
 /* Protocol Handlers */
 
 static errno_t
-ipsec_proto_input(__unused ifnet_t     interface,
+ipsec_proto_input(ifnet_t interface,
                                  protocol_family_t     protocol,
                                  protocol_family_t     protocol,
-                                 mbuf_t                                m,
-                                 __unused char         *frame_header)
+                                 mbuf_t m,
+                                 __unused char *frame_header)
 {
 {
-       if (proto_input(protocol, m) != 0)
+       struct ip *ip;
+       uint32_t af = 0;
+       ip = mtod(m, struct ip *);
+       if (ip->ip_v == 4)
+               af = AF_INET;
+       else if (ip->ip_v == 6)
+               af = AF_INET6;
+       
+       mbuf_pkthdr_setrcvif(m, interface);
+       bpf_tap_in(interface, DLT_NULL, m, &af, sizeof(af));
+       pktap_input(interface, protocol, m, NULL);
+
+       if (proto_input(protocol, m) != 0) {
+               ifnet_stat_increment_in(interface, 0, 0, 1);
                m_freem(m);
                m_freem(m);
+       } else {
+               ifnet_stat_increment_in(interface, 1, m->m_pkthdr.len, 0);
+       }
        
        return 0;
 }
        
        return 0;
 }
@@ -924,3 +937,80 @@ ipsec_attach_proto(ifnet_t                         interface,
        
        return result;
 }
        
        return result;
 }
+
+errno_t
+ipsec_inject_inbound_packet(ifnet_t    interface,
+                                                       mbuf_t packet)
+{
+       errno_t error;
+       protocol_family_t protocol;
+       if ((error = ipsec_demux(interface, packet, NULL, &protocol)) != 0) {
+               return error;
+       }
+       
+       return ipsec_proto_input(interface, protocol, packet, NULL);
+}
+
+void
+ipsec_set_pkthdr_for_interface(ifnet_t interface, mbuf_t packet, int family)
+{
+       if (packet != NULL && interface != NULL) {
+               struct ipsec_pcb *pcb = ifnet_softc(interface);
+               if (pcb != NULL) {
+                       /* Set traffic class, set flow */
+                       m_set_service_class(packet, pcb->ipsec_output_service_class);
+                       packet->m_pkthdr.pkt_flowsrc = FLOWSRC_IFNET;
+                       packet->m_pkthdr.pkt_flowid = interface->if_flowhash;
+                       if (family == AF_INET) {
+                               struct ip *ip = mtod(packet, struct ip *);
+                               packet->m_pkthdr.pkt_proto = ip->ip_p;
+                       } else if (family == AF_INET6) {
+                               struct ip6_hdr *ip6 = mtod(packet, struct ip6_hdr *);
+                               packet->m_pkthdr.pkt_proto = ip6->ip6_nxt;
+                       }
+                       packet->m_pkthdr.pkt_flags = (PKTF_FLOW_ID | PKTF_FLOW_ADV | PKTF_FLOW_LOCALSRC);
+               }
+       }
+}
+
+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;
+       }
+}