]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ipsec.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / bsd / netinet6 / ipsec.c
index 5c14febecad3f4dc5a3242fcc0215099fb542165..9d6201d6a606c467ac9799e502205a9d272cf2e1 100644 (file)
@@ -47,6 +47,8 @@
 #include <sys/kernel.h>
 #include <sys/syslog.h>
 #include <sys/sysctl.h>
+#include <kern/locks.h>
+#include <sys/kauth.h>
 
 #include <net/if.h>
 #include <net/route.h>
@@ -104,6 +106,16 @@ int ipsec_debug = 1;
 int ipsec_debug = 0;
 #endif
 
+#include <sys/kdebug.h>
+#define DBG_LAYER_BEG                  NETDBG_CODE(DBG_NETIPSEC, 1)
+#define DBG_LAYER_END                  NETDBG_CODE(DBG_NETIPSEC, 3)
+#define DBG_FNC_GETPOL_SOCK            NETDBG_CODE(DBG_NETIPSEC, (1 << 8))
+#define DBG_FNC_GETPOL_ADDR            NETDBG_CODE(DBG_NETIPSEC, (2 << 8))
+#define DBG_FNC_IPSEC_OUT              NETDBG_CODE(DBG_NETIPSEC, (3 << 8))
+
+extern lck_mtx_t *sadb_mutex;
+extern lck_mtx_t *ip6_mutex;
+
 struct ipsecstat ipsecstat;
 int ip4_ah_cleartos = 1;
 int ip4_ah_offsetmask = 0;     /* maybe IP_DF? */
@@ -115,7 +127,9 @@ int ip4_ah_net_deflev = IPSEC_LEVEL_USE;
 struct secpolicy ip4_def_policy;
 int ip4_ipsec_ecn = 0;         /* ECN ignore(-1)/forbidden(0)/allowed(1) */
 int ip4_esp_randpad = -1;
+int    esp_udp_encap_port = 0;
 static int sysctl_def_policy SYSCTL_HANDLER_ARGS;
+extern u_int32_t natt_now;
 
 SYSCTL_DECL(_net_inet_ipsec);
 #if INET6
@@ -151,6 +165,15 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD,
 int ipsec_bypass = 1;
 SYSCTL_INT(_net_inet_ipsec, OID_AUTO, bypass, CTLFLAG_RD, &ipsec_bypass,0, "");
 
+/*
+ * NAT Traversal requires a UDP port for encapsulation,
+ * esp_udp_encap_port controls which port is used. Racoon
+ * must set this port to the port racoon is using locally
+ * for nat traversal.
+ */
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, esp_port,
+                  CTLFLAG_RW, &esp_udp_encap_port, 0, "");
+
 #if INET6
 struct ipsecstat ipsec6stat;
 int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;
@@ -182,43 +205,44 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD,
        esp_randpad, CTLFLAG_RW,        &ip6_esp_randpad,       0, "");
 #endif /* INET6 */
 
-static int ipsec_setspidx_mbuf
-       __P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int));
-static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb));
+static int ipsec_setspidx_mbuf(struct secpolicyindex *, u_int, u_int,
+       struct mbuf *, int);
+static int ipsec4_setspidx_inpcb(struct mbuf *, struct inpcb *pcb);
 #if INET6
-static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb));
+static int ipsec6_setspidx_in6pcb(struct mbuf *, struct in6pcb *pcb);
 #endif
-static int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int));
-static void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));
-static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));
+static int ipsec_setspidx(struct mbuf *, struct secpolicyindex *, int);
+static void ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
+static int ipsec4_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
 #if INET6
-static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));
-static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));
+static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
+static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
 #endif
-static struct inpcbpolicy *ipsec_newpcbpolicy __P((void));
-static void ipsec_delpcbpolicy __P((struct inpcbpolicy *));
-static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src));
-static int ipsec_set_policy __P((struct secpolicy **pcb_sp,
-       int optname, caddr_t request, size_t len, int priv));
-static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp));
-static void vshiftl __P((unsigned char *, int, int));
-static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *));
-static size_t ipsec_hdrsiz __P((struct secpolicy *));
+static struct inpcbpolicy *ipsec_newpcbpolicy(void);
+static void ipsec_delpcbpolicy(struct inpcbpolicy *);
+static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src);
+static int ipsec_set_policy(struct secpolicy **pcb_sp,
+       int optname, caddr_t request, size_t len, int priv);
+static int ipsec_get_policy(struct secpolicy *pcb_sp, struct mbuf **mp);
+static void vshiftl(unsigned char *, int, int);
+static int ipsec_in_reject(struct secpolicy *, struct mbuf *);
+static size_t ipsec_hdrsiz(struct secpolicy *);
 #if INET
-static struct mbuf *ipsec4_splithdr __P((struct mbuf *));
+static struct mbuf *ipsec4_splithdr(struct mbuf *);
 #endif
 #if INET6
-static struct mbuf *ipsec6_splithdr __P((struct mbuf *));
+static struct mbuf *ipsec6_splithdr(struct mbuf *);
 #endif
 #if INET
-static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *));
+static int ipsec4_encapsulate(struct mbuf *, struct secasvar *);
 #endif
 #if INET6
-static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *));
+static int ipsec6_encapsulate(struct mbuf *, struct secasvar *);
 #endif
-static struct mbuf *ipsec_addaux __P((struct mbuf *));
-static struct mbuf *ipsec_findaux __P((struct mbuf *));
-static void ipsec_optaux __P((struct mbuf *, struct mbuf *));
+static struct mbuf *ipsec_addaux(struct mbuf *);
+static struct mbuf *ipsec_findaux(struct mbuf *);
+static void ipsec_optaux(struct mbuf *, struct mbuf *);
+void ipsec_send_natt_keepalive(struct secasvar *sav);
 
 static int
 sysctl_def_policy SYSCTL_HANDLER_ARGS
@@ -246,7 +270,7 @@ sysctl_def_policy SYSCTL_HANDLER_ARGS
  *             0       : bypass
  *             EACCES  : discard packet.
  *             ENOENT  : ipsec_acquire() in progress, maybe.
- *             others  : error occured.
+ *             others  : error occurred.
  *     others: a pointer to SP
  *
  * NOTE: IPv6 mapped adddress concern is implemented here.
@@ -262,10 +286,16 @@ ipsec4_getpolicybysock(m, dir, so, error)
        struct secpolicy *currsp = NULL;        /* policy on socket */
        struct secpolicy *kernsp = NULL;        /* policy on kernel */
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
        /* sanity check */
        if (m == NULL || so == NULL || error == NULL)
                panic("ipsec4_getpolicybysock: NULL pointer was passed.\n");
        
+       if (so->so_pcb == NULL) {
+               printf("ipsec4_getpolicybysock: so->so_pcb == NULL\n");
+               return ipsec4_getpolicybyaddr(m, dir, 0, error);
+       }
+       
        switch (so->so_proto->pr_domain->dom_family) {
        case AF_INET:
                pcbsp = sotoinpcb(so)->inp_sp;
@@ -282,6 +312,8 @@ ipsec4_getpolicybysock(m, dir, so, error)
                return ipsec4_getpolicybyaddr(m, dir, 0, error);
        }
 
+       KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_START, 0,0,0,0,0);
+
        switch (so->so_proto->pr_domain->dom_family) {
        case AF_INET:
                /* set spidx in pcb */
@@ -296,8 +328,10 @@ ipsec4_getpolicybysock(m, dir, so, error)
        default:
                panic("ipsec4_getpolicybysock: unsupported address family\n");
        }
-       if (*error)
+       if (*error) {
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 1,*error,0,0,0);
                return NULL;
+       }
 
        /* sanity check */
        if (pcbsp == NULL)
@@ -324,6 +358,7 @@ ipsec4_getpolicybysock(m, dir, so, error)
                case IPSEC_POLICY_BYPASS:
                        currsp->refcnt++;
                        *error = 0;
+                       KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 2,*error,0,0,0);
                        return currsp;
 
                case IPSEC_POLICY_ENTRUST:
@@ -336,6 +371,7 @@ ipsec4_getpolicybysock(m, dir, so, error)
                                        printf("DP ipsec4_getpolicybysock called "
                                               "to allocate SP:%p\n", kernsp));
                                *error = 0;
+                               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 3,*error,0,0,0);
                                return kernsp;
                        }
 
@@ -349,17 +385,20 @@ ipsec4_getpolicybysock(m, dir, so, error)
                        }
                        ip4_def_policy.refcnt++;
                        *error = 0;
+                       KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 4,*error,0,0,0);
                        return &ip4_def_policy;
                        
                case IPSEC_POLICY_IPSEC:
                        currsp->refcnt++;
                        *error = 0;
+                       KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 5,*error,0,0,0);
                        return currsp;
 
                default:
                        ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
                              "Invalid policy for PCB %d\n", currsp->policy));
                        *error = EINVAL;
+                       KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 6,*error,0,0,0);
                        return NULL;
                }
                /* NOTREACHED */
@@ -375,6 +414,7 @@ ipsec4_getpolicybysock(m, dir, so, error)
                        printf("DP ipsec4_getpolicybysock called "
                               "to allocate SP:%p\n", kernsp));
                *error = 0;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 7,*error,0,0,0);
                return kernsp;
        }
 
@@ -385,6 +425,7 @@ ipsec4_getpolicybysock(m, dir, so, error)
                       "Illegal policy for non-priviliged defined %d\n",
                        currsp->policy));
                *error = EINVAL;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 8,*error,0,0,0);
                return NULL;
 
        case IPSEC_POLICY_ENTRUST:
@@ -397,17 +438,20 @@ ipsec4_getpolicybysock(m, dir, so, error)
                }
                ip4_def_policy.refcnt++;
                *error = 0;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 9,*error,0,0,0);
                return &ip4_def_policy;
 
        case IPSEC_POLICY_IPSEC:
                currsp->refcnt++;
                *error = 0;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 10,*error,0,0,0);
                return currsp;
 
        default:
                ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
                   "Invalid policy for PCB %d\n", currsp->policy));
                *error = EINVAL;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_SOCK | DBG_FUNC_END, 11,*error,0,0,0);
                return NULL;
        }
        /* NOTREACHED */
@@ -421,7 +465,7 @@ ipsec4_getpolicybysock(m, dir, so, error)
  *             0       : bypass
  *             EACCES  : discard packet.
  *             ENOENT  : ipsec_acquire() in progress, maybe.
- *             others  : error occured.
+ *             others  : error occurred.
  */
 struct secpolicy *
 ipsec4_getpolicybyaddr(m, dir, flag, error)
@@ -435,6 +479,8 @@ ipsec4_getpolicybyaddr(m, dir, flag, error)
        if (ipsec_bypass != 0)
                return 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL || error == NULL)
                panic("ipsec4_getpolicybyaddr: NULL pointer was passed.\n");
@@ -442,14 +488,17 @@ ipsec4_getpolicybyaddr(m, dir, flag, error)
     {
        struct secpolicyindex spidx;
 
+       KERNEL_DEBUG(DBG_FNC_GETPOL_ADDR | DBG_FUNC_START, 0,0,0,0,0);
        bzero(&spidx, sizeof(spidx));
 
        /* make a index to look for a policy */
        *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m,
            (flag & IP_FORWARDING) ? 0 : 1);
 
-       if (*error != 0)
+       if (*error != 0) {
+               KERNEL_DEBUG(DBG_FNC_GETPOL_ADDR | DBG_FUNC_END, 1,*error,0,0,0);
                return NULL;
+       }
 
        sp = key_allocsp(&spidx, dir);
     }
@@ -460,6 +509,7 @@ ipsec4_getpolicybyaddr(m, dir, flag, error)
                        printf("DP ipsec4_getpolicybyaddr called "
                               "to allocate SP:%p\n", sp));
                *error = 0;
+               KERNEL_DEBUG(DBG_FNC_GETPOL_ADDR | DBG_FUNC_END, 2,*error,0,0,0);
                return sp;
        }
 
@@ -473,6 +523,7 @@ ipsec4_getpolicybyaddr(m, dir, flag, error)
        }
        ip4_def_policy.refcnt++;
        *error = 0;
+       KERNEL_DEBUG(DBG_FNC_GETPOL_ADDR | DBG_FUNC_END, 3,*error,0,0,0);
        return &ip4_def_policy;
 }
 
@@ -484,7 +535,7 @@ ipsec4_getpolicybyaddr(m, dir, flag, error)
  *             0       : bypass
  *             EACCES  : discard packet.
  *             ENOENT  : ipsec_acquire() in progress, maybe.
- *             others  : error occured.
+ *             others  : error occurred.
  *     others: a pointer to SP
  */
 struct secpolicy *
@@ -498,6 +549,8 @@ ipsec6_getpolicybysock(m, dir, so, error)
        struct secpolicy *currsp = NULL;        /* policy on socket */
        struct secpolicy *kernsp = NULL;        /* policy on kernel */
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL || so == NULL || error == NULL)
                panic("ipsec6_getpolicybysock: NULL pointer was passed.\n");
@@ -641,7 +694,7 @@ ipsec6_getpolicybysock(m, dir, so, error)
  *             0       : bypass
  *             EACCES  : discard packet.
  *             ENOENT  : ipsec_acquire() in progress, maybe.
- *             others  : error occured.
+ *             others  : error occurred.
  */
 #ifndef IP_FORWARDING
 #define IP_FORWARDING 1
@@ -656,6 +709,8 @@ ipsec6_getpolicybyaddr(m, dir, flag, error)
 {
        struct secpolicy *sp = NULL;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL || error == NULL)
                panic("ipsec6_getpolicybyaddr: NULL pointer was passed.\n");
@@ -803,9 +858,11 @@ ipsec6_setspidx_in6pcb(m, pcb)
                goto bad;
        spidx->dir = IPSEC_DIR_INBOUND;
 
-       KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
-               printf("ipsec_setspidx_mbuf: end\n");
-               kdebug_secpolicyindex(spidx));
+       spidx = &pcb->in6p_sp->sp_out->spidx;
+       error = ipsec_setspidx(m, spidx, 1);
+       if (error)
+               goto bad;
+       spidx->dir = IPSEC_DIR_OUTBOUND;
 
        return 0;
 
@@ -965,7 +1022,7 @@ ipsec4_get_ulp(m, spidx, needport)
                            uh.uh_dport;
                        return;
                case IPPROTO_AH:
-                       if (m->m_pkthdr.len > off + sizeof(ip6e))
+                       if (off + sizeof(ip6e) > m->m_pkthdr.len)
                                return;
                        m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
                        off += (ip6e.ip6e_len + 2) << 2;
@@ -1137,6 +1194,8 @@ ipsec_init_policy(so, pcb_sp)
 {
        struct inpcbpolicy *new;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (so == NULL || pcb_sp == NULL)
                panic("ipsec_init_policy: NULL pointer was passed.\n");
@@ -1151,7 +1210,7 @@ ipsec_init_policy(so, pcb_sp)
 #ifdef __APPLE__
        if (so->so_uid == 0)
 #else
-       if (so->so_cred != 0 && so->so_cred->pc_ucred->cr_uid == 0)
+       if (so->so_cred != 0 && !suser(so->so_cred->pc_ucred, NULL))
 #endif
                new->priv = 1;
        else
@@ -1187,6 +1246,8 @@ ipsec_copy_policy(old, new)
        if (ipsec_bypass != 0)
                return 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        sp = ipsec_deepcopy_policy(old->sp_in);
        if (sp) {
                key_freesp(new->sp_in);
@@ -1277,6 +1338,8 @@ ipsec_set_policy(pcb_sp, optname, request, len, priv)
        struct secpolicy *newsp = NULL;
        int error;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
                return EINVAL;
@@ -1320,6 +1383,8 @@ ipsec_get_policy(pcb_sp, mp)
        struct mbuf **mp;
 {
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (pcb_sp == NULL || mp == NULL)
                return EINVAL;
@@ -1350,6 +1415,8 @@ ipsec4_set_policy(inp, optname, request, len, priv)
        struct secpolicy **pcb_sp;
        int     error = 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (inp == NULL || request == NULL)
                return EINVAL;
@@ -1395,6 +1462,8 @@ ipsec4_get_policy(inp, request, len, mp)
        struct secpolicy *pcb_sp;
        int     error = 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (inp == NULL || request == NULL || mp == NULL)
                return EINVAL;
@@ -1430,6 +1499,8 @@ int
 ipsec4_delete_pcbpolicy(inp)
        struct inpcb *inp;
 {
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (inp == NULL)
                panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.\n");
@@ -1466,6 +1537,8 @@ ipsec6_set_policy(in6p, optname, request, len, priv)
        struct secpolicy **pcb_sp;
        int error = 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (in6p == NULL || request == NULL)
                return EINVAL;
@@ -1511,6 +1584,8 @@ ipsec6_get_policy(in6p, request, len, mp)
        struct secpolicy *pcb_sp;
        int error = 0;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (in6p == NULL || request == NULL || mp == NULL)
                return EINVAL;
@@ -1545,6 +1620,8 @@ int
 ipsec6_delete_pcbpolicy(in6p)
        struct in6pcb *in6p;
 {
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check. */
        if (in6p == NULL)
                panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.\n");
@@ -1580,6 +1657,8 @@ ipsec_get_reqlevel(isr)
        u_int level = 0;
        u_int esp_trans_deflev, esp_net_deflev, ah_trans_deflev, ah_net_deflev;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (isr == NULL || isr->sp == NULL)
                panic("ipsec_get_reqlevel: NULL pointer is passed.\n");
@@ -1639,6 +1718,7 @@ ipsec_get_reqlevel(isr)
                                level = ah_net_deflev;
                        else
                                level = ah_trans_deflev;
+                       break;
                case IPPROTO_IPCOMP:
                        /*
                         * we don't really care, as IPcomp document says that
@@ -1688,6 +1768,8 @@ ipsec_in_reject(sp, m)
                printf("ipsec_in_reject: using SP\n");
                kdebug_secpolicy(sp));
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* check policy */
        switch (sp->policy) {
        case IPSEC_POLICY_DISCARD:
@@ -1768,6 +1850,8 @@ ipsec4_in_reject_so(m, so)
        int error;
        int result;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL)
                return 0;       /* XXX should be panic ? */
@@ -1798,12 +1882,17 @@ ipsec4_in_reject(m, inp)
        struct mbuf *m;
        struct inpcb *inp;
 {
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        if (inp == NULL)
                return ipsec4_in_reject_so(m, NULL);
        if (inp->inp_socket)
                return ipsec4_in_reject_so(m, inp->inp_socket);
        else
                panic("ipsec4_in_reject: invalid inpcb/socket");
+
+       /* NOTREACHED */
+       return 0;
 }
 
 #if INET6
@@ -1821,6 +1910,8 @@ ipsec6_in_reject_so(m, so)
        int error;
        int result;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL)
                return 0;       /* XXX should be panic ? */
@@ -1850,12 +1941,17 @@ ipsec6_in_reject(m, in6p)
        struct mbuf *m;
        struct in6pcb *in6p;
 {
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        if (in6p == NULL)
                return ipsec6_in_reject_so(m, NULL);
        if (in6p->in6p_socket)
                return ipsec6_in_reject_so(m, in6p->in6p_socket);
        else
                panic("ipsec6_in_reject: invalid in6p/socket");
+
+       /* NOTREACHED */
+       return 0;
 }
 #endif
 
@@ -1872,9 +1968,11 @@ ipsec_hdrsiz(sp)
        size_t siz, clen;
 
        KEYDEBUG(KEYDEBUG_IPSEC_DATA,
-               printf("ipsec_in_reject: using SP\n");
+               printf("ipsec_hdrsiz: using SP\n");
                kdebug_secpolicy(sp));
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* check policy */
        switch (sp->policy) {
        case IPSEC_POLICY_DISCARD:
@@ -1946,6 +2044,8 @@ ipsec4_hdrsiz(m, dir, inp)
        int error;
        size_t size;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL)
                return 0;       /* XXX should be panic ? */
@@ -1988,6 +2088,8 @@ ipsec6_hdrsiz(m, dir, in6p)
        int error;
        size_t size;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (m == NULL)
                return 0;       /* XXX shoud be panic ? */
@@ -2029,6 +2131,8 @@ ipsec4_encapsulate(m, sav)
        size_t hlen;
        size_t plen;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* can't tunnel between different AFs */
        if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
                != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
@@ -2105,13 +2209,13 @@ ipsec4_encapsulate(m, sav)
        ip->ip_off &= htons(~IP_OFFMASK);
        ip->ip_off &= htons(~IP_MF);
        switch (ip4_ipsec_dfbit) {
-       case 0: /*clear DF bit*/
+       case 0: /* clear DF bit */
                ip->ip_off &= htons(~IP_DF);
                break;
-       case 1: /*set DF bit*/
+       case 1: /* set DF bit */
                ip->ip_off |= htons(IP_DF);
                break;
-       default:        /*copy DF bit*/
+       default:        /* copy DF bit */
                break;
        }
        ip->ip_p = IPPROTO_IPIP;
@@ -2148,6 +2252,8 @@ ipsec6_encapsulate(m, sav)
        struct ip6_hdr *ip6;
        size_t plen;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* can't tunnel between different AFs */
        if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
                != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
@@ -2239,6 +2345,8 @@ ipsec_chkreplay(seq, sav)
        u_int32_t wsizeb;       /* constant: bits of window size */
        int frlast;             /* constant: last frame */
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (sav == NULL)
                panic("ipsec_chkreplay: NULL pointer was passed.\n");
@@ -2298,6 +2406,8 @@ ipsec_updatereplay(seq, sav)
        u_int32_t wsizeb;       /* constant: bits of window size */
        int frlast;             /* constant: last frame */
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* sanity check */
        if (sav == NULL)
                panic("ipsec_chkreplay: NULL pointer was passed.\n");
@@ -2381,7 +2491,7 @@ ok:
 }
 
 /*
- * shift variable length bunffer to left.
+ * shift variable length buffer to left.
  * IN: bitmap: pointer to the buffer
  *     nbit:   the number of to shift.
  *     wsize:  buffer size (bytes).
@@ -2471,6 +2581,8 @@ ipsec_logsastr(sav)
        char *p;
        struct secasindex *saidx = &sav->sah->saidx;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
        /* validity check */
        if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
                        != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family)
@@ -2545,7 +2657,6 @@ ipsec4_output(state, sp, flags)
        struct ip *ip = NULL;
        struct ipsecrequest *isr = NULL;
        struct secasindex saidx;
-       int s;
        int error;
        struct sockaddr_in *dst4;
        struct sockaddr_in *sin;
@@ -2559,6 +2670,10 @@ ipsec4_output(state, sp, flags)
        if (!state->dst)
                panic("state->dst == NULL in ipsec4_output");
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
+       KERNEL_DEBUG(DBG_FNC_IPSEC_OUT | DBG_FUNC_START, 0,0,0,0,0);
+
        KEYDEBUG(KEYDEBUG_IPSEC_DATA,
                printf("ipsec4_output: applyed SP\n");
                kdebug_secpolicy(sp));
@@ -2639,7 +2754,6 @@ ipsec4_output(state, sp, flags)
                 * There may be the case that SA status will be changed when
                 * we are refering to one. So calling splsoftnet().
                 */
-               s = splnet();
 
                if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
                        /*
@@ -2650,19 +2764,16 @@ ipsec4_output(state, sp, flags)
                                ipseclog((LOG_ERR, "ipsec4_output: "
                                    "family mismatched between inner and outer spi=%u\n",
                                    (u_int32_t)ntohl(isr->sav->spi)));
-                               splx(s);
                                error = EAFNOSUPPORT;
                                goto bad;
                        }
 
                        state->m = ipsec4_splithdr(state->m);
                        if (!state->m) {
-                               splx(s);
                                error = ENOMEM;
                                goto bad;
                        }
                        error = ipsec4_encapsulate(state->m, isr->sav);
-                       splx(s);
                        if (error) {
                                state->m = NULL;
                                goto bad;
@@ -2695,8 +2806,7 @@ ipsec4_output(state, sp, flags)
                                state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
                                dst4 = (struct sockaddr_in *)state->dst;
                        }
-               } else
-                       splx(s);
+               }
 
                state->m = ipsec4_splithdr(state->m);
                if (!state->m) {
@@ -2746,11 +2856,13 @@ ipsec4_output(state, sp, flags)
                ip = mtod(state->m, struct ip *);
        }
 
+       KERNEL_DEBUG(DBG_FNC_IPSEC_OUT | DBG_FUNC_END, 0,0,0,0,0);
        return 0;
 
 bad:
        m_freem(state->m);
        state->m = NULL;
+       KERNEL_DEBUG(DBG_FNC_IPSEC_OUT | DBG_FUNC_END, error,0,0,0,0);
        return error;
 }
 #endif
@@ -2776,22 +2888,23 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun)
        struct sockaddr_in6 *sin6;
 
        if (!state)
-               panic("state == NULL in ipsec6_output");
+               panic("state == NULL in ipsec6_output_trans");
        if (!state->m)
-               panic("state->m == NULL in ipsec6_output");
+               panic("state->m == NULL in ipsec6_output_trans");
        if (!nexthdrp)
-               panic("nexthdrp == NULL in ipsec6_output");
+               panic("nexthdrp == NULL in ipsec6_output_trans");
        if (!mprev)
-               panic("mprev == NULL in ipsec6_output");
+               panic("mprev == NULL in ipsec6_output_trans");
        if (!sp)
-               panic("sp == NULL in ipsec6_output");
+               panic("sp == NULL in ipsec6_output_trans");
        if (!tun)
-               panic("tun == NULL in ipsec6_output");
+               panic("tun == NULL in ipsec6_output_trans");
 
        KEYDEBUG(KEYDEBUG_IPSEC_DATA,
                printf("ipsec6_output_trans: applyed SP\n");
                kdebug_secpolicy(sp));
-
+       
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
        *tun = 0;
        for (isr = sp->req; isr; isr = isr->next) {
                if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
@@ -2850,8 +2963,10 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun)
                         * XXX: should we directly notify sockets via
                         *      pfctlinputs?
                         */
+                       lck_mtx_unlock(sadb_mutex);
                        icmp6_error(state->m, ICMP6_DST_UNREACH,
                                    ICMP6_DST_UNREACH_ADMIN, 0);
+                       lck_mtx_lock(sadb_mutex);
                        state->m = NULL; /* icmp6_error freed the mbuf */
                        goto bad;
                }
@@ -2944,14 +3059,15 @@ ipsec6_output_tunnel(state, sp, flags)
        int error = 0;
        int plen;
        struct sockaddr_in6* dst6;
-       int s;
 
        if (!state)
-               panic("state == NULL in ipsec6_output");
+               panic("state == NULL in ipsec6_output_tunnel");
        if (!state->m)
-               panic("state->m == NULL in ipsec6_output");
+               panic("state->m == NULL in ipsec6_output_tunnel");
        if (!sp)
-               panic("sp == NULL in ipsec6_output");
+               panic("sp == NULL in ipsec6_output_tunnel");
+
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
 
        KEYDEBUG(KEYDEBUG_IPSEC_DATA,
                printf("ipsec6_output_tunnel: applyed SP\n");
@@ -2966,9 +3082,48 @@ ipsec6_output_tunnel(state, sp, flags)
                        break;
        }
 
-       for (/*already initialized*/; isr; isr = isr->next) {
-               /* When tunnel mode, SA peers must be specified. */
-               bcopy(&isr->saidx, &saidx, sizeof(saidx));
+       for (/* already initialized */; isr; isr = isr->next) {
+               if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+                       /* When tunnel mode, SA peers must be specified. */
+                       bcopy(&isr->saidx, &saidx, sizeof(saidx));
+               } else {
+                       /* make SA index to look for a proper SA */
+                       struct sockaddr_in6 *sin6;
+
+                       bzero(&saidx, sizeof(saidx));
+                       saidx.proto = isr->saidx.proto;
+                       saidx.mode = isr->saidx.mode;
+                       saidx.reqid = isr->saidx.reqid;
+
+                       ip6 = mtod(state->m, struct ip6_hdr *);
+                       sin6 = (struct sockaddr_in6 *)&saidx.src;
+                       if (sin6->sin6_len == 0) {
+                               sin6->sin6_len = sizeof(*sin6);
+                               sin6->sin6_family = AF_INET6;
+                               sin6->sin6_port = IPSEC_PORT_ANY;
+                               bcopy(&ip6->ip6_src, &sin6->sin6_addr,
+                                   sizeof(ip6->ip6_src));
+                               if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+                                       /* fix scope id for comparing SPD */
+                                       sin6->sin6_addr.s6_addr16[1] = 0;
+                                       sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+                               }
+                       }
+                       sin6 = (struct sockaddr_in6 *)&saidx.dst;
+                       if (sin6->sin6_len == 0) {
+                               sin6->sin6_len = sizeof(*sin6);
+                               sin6->sin6_family = AF_INET6;
+                               sin6->sin6_port = IPSEC_PORT_ANY;
+                               bcopy(&ip6->ip6_dst, &sin6->sin6_addr,
+                                   sizeof(ip6->ip6_dst));
+                               if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+                                       /* fix scope id for comparing SPD */
+                                       sin6->sin6_addr.s6_addr16[1] = 0;
+                                       sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+                               }
+                       }
+               }
+
                if (key_checkrequest(isr, &saidx) == ENOENT) {
                        /*
                         * IPsec processing is required, but no SA found.
@@ -3008,7 +3163,6 @@ ipsec6_output_tunnel(state, sp, flags)
                 * There may be the case that SA status will be changed when
                 * we are refering to one. So calling splsoftnet().
                 */
-               s = splnet();
 
                if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
                        /*
@@ -3019,7 +3173,6 @@ ipsec6_output_tunnel(state, sp, flags)
                                ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
                                    "family mismatched between inner and outer, spi=%u\n",
                                    (u_int32_t)ntohl(isr->sav->spi)));
-                               splx(s);
                                ipsec6stat.out_inval++;
                                error = EAFNOSUPPORT;
                                goto bad;
@@ -3027,13 +3180,11 @@ ipsec6_output_tunnel(state, sp, flags)
 
                        state->m = ipsec6_splithdr(state->m);
                        if (!state->m) {
-                               splx(s);
                                ipsec6stat.out_nomem++;
                                error = ENOMEM;
                                goto bad;
                        }
                        error = ipsec6_encapsulate(state->m, isr->sav);
-                       splx(s);
                        if (error) {
                                state->m = 0;
                                goto bad;
@@ -3068,8 +3219,7 @@ ipsec6_output_tunnel(state, sp, flags)
                                state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
                                dst6 = (struct sockaddr_in6 *)state->dst;
                        }
-               } else
-                       splx(s);
+               }
 
                state->m = ipsec6_splithdr(state->m);
                if (!state->m) {
@@ -3222,6 +3372,8 @@ ipsec4_tunnel_validate(m, off, nxt0, sav)
        struct secpolicy *sp;
        struct ip *oip;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
 #if DIAGNOSTIC
        if (m->m_len < sizeof(struct ip))
                panic("too short mbuf on ipsec4_tunnel_validate");
@@ -3289,8 +3441,9 @@ ipsec4_tunnel_validate(m, off, nxt0, sav)
 
        sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
            (struct sockaddr *)&isrc, (struct sockaddr *)&idst);
-       if (!sp)
+       if (!sp) {
                return 0;
+       }
        key_freesp(sp);
 
        return 1;
@@ -3311,6 +3464,8 @@ ipsec6_tunnel_validate(m, off, nxt0, sav)
        struct secpolicy *sp;
        struct ip6_hdr *oip6;
 
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
 #if DIAGNOSTIC
        if (m->m_len < sizeof(struct ip6_hdr))
                panic("too short mbuf on ipsec6_tunnel_validate");
@@ -3354,6 +3509,14 @@ ipsec6_tunnel_validate(m, off, nxt0, sav)
 
        sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
            (struct sockaddr *)&isrc, (struct sockaddr *)&idst);
+       /*
+        * when there is no suitable inbound policy for the packet of the ipsec
+        * tunnel mode, the kernel never decapsulate the tunneled packet
+        * as the ipsec tunnel mode even when the system wide policy is "none".
+        * then the kernel leaves the generic tunnel module to process this
+        * packet.  if there is no rule of the generic tunnel, the packet
+        * is rejected and the statistics will be counted up.
+        */
        if (!sp)
                return 0;
        key_freesp(sp);
@@ -3577,7 +3740,7 @@ ipsec_addhist(m, proto, spi)
        if (!n)
                return ENOBUFS;
        if (M_TRAILINGSPACE(n) < sizeof(*p))
-               return ENOSPC;  /*XXX*/
+               return ENOSPC;  /* XXX */
        p = (struct ipsec_history *)(mtod(n, caddr_t) + n->m_len);
        n->m_len += sizeof(*p);
        bzero(p, sizeof(*p));
@@ -3620,3 +3783,51 @@ ipsec_clearhist(m)
                n->m_len = sizeof(struct socket *);
        ipsec_optaux(m, n);
 }
+
+__private_extern__ void
+ipsec_send_natt_keepalive(
+       struct secasvar *sav)
+{
+       struct mbuf     *m;
+       struct udphdr *uh;
+       struct ip *ip;
+       int error;
+
+       if ((esp_udp_encap_port & 0xFFFF) == 0 || sav->remote_ike_port == 0) return;
+       
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
+
+       m = m_gethdr(M_NOWAIT, MT_DATA);
+       if (m == NULL) return;
+       
+       /*
+        * Create a UDP packet complete with IP header.
+        * We must do this because UDP output requires
+        * an inpcb which we don't have. UDP packet
+        * contains one byte payload. The byte is set
+        * to 0xFF.
+        */
+       ip = (struct ip*)m_mtod(m);
+       uh = (struct udphdr*)((char*)m_mtod(m) + sizeof(struct ip));
+       m->m_len = sizeof(struct udpiphdr) + 1;
+       bzero(m_mtod(m), m->m_len);
+       m->m_pkthdr.len = m->m_len;
+
+       ip->ip_len = m->m_len;
+       ip->ip_ttl = ip_defttl;
+       ip->ip_p = IPPROTO_UDP;
+       ip->ip_src = ((struct sockaddr_in*)&sav->sah->saidx.src)->sin_addr;
+       ip->ip_dst = ((struct sockaddr_in*)&sav->sah->saidx.dst)->sin_addr;
+       uh->uh_sport = htons((u_short)esp_udp_encap_port);
+       uh->uh_dport = htons(sav->remote_ike_port);
+       uh->uh_ulen = htons(1 + sizeof(struct udphdr));
+       uh->uh_sum = 0;
+       *(u_int8_t*)((char*)m_mtod(m) + sizeof(struct ip) + sizeof(struct udphdr)) = 0xFF;
+       
+       lck_mtx_unlock(sadb_mutex);
+       error = ip_output(m, NULL, &sav->sah->sa_route, IP_NOIPSEC, NULL);
+       lck_mtx_lock(sadb_mutex);
+       if (error == 0)
+               sav->natt_last_activity = natt_now;
+
+}