X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e2fac8b15b12a7979f72090454d850e612fc5b13..bd504ef0e0b883cdd7917b73b3574eb9ce669905:/bsd/netkey/key.c diff --git a/bsd/netkey/key.c b/bsd/netkey/key.c index 75e405b3c..ffbdcf88e 100644 --- a/bsd/netkey/key.c +++ b/bsd/netkey/key.c @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2008-2011 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + /* $FreeBSD: src/sys/netkey/key.c,v 1.16.2.13 2002/07/24 18:17:40 ume Exp $ */ /* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */ @@ -50,6 +78,7 @@ #include #include #include +#include #include @@ -108,24 +137,19 @@ #include -#ifndef satosin -#define satosin(s) ((struct sockaddr_in *)s) -#endif - #define FULLMASK 0xff lck_grp_t *sadb_mutex_grp; lck_grp_attr_t *sadb_mutex_grp_attr; lck_attr_t *sadb_mutex_attr; -lck_mtx_t *sadb_mutex; +decl_lck_mtx_data(, sadb_mutex_data); +lck_mtx_t *sadb_mutex = &sadb_mutex_data; lck_grp_t *pfkey_stat_mutex_grp; lck_grp_attr_t *pfkey_stat_mutex_grp_attr; lck_attr_t *pfkey_stat_mutex_attr; -lck_mtx_t *pfkey_stat_mutex; - - -extern lck_mtx_t *nd6_mutex; +decl_lck_mtx_data(, pfkey_stat_mutex_data); +lck_mtx_t *pfkey_stat_mutex = &pfkey_stat_mutex_data; /* * Note on SA reference counting: @@ -148,8 +172,8 @@ static u_int key_larval_lifetime = 30; /* interval to expire acquiring, 30(s)*/ static int key_blockacq_count = 10; /* counter for blocking SADB_ACQUIRE.*/ static int key_blockacq_lifetime = 20; /* lifetime for blocking SADB_ACQUIRE.*/ static int key_preferred_oldsa = 0; /* preferred old sa rather than new sa.*/ -static int natt_keepalive_interval = 20; /* interval between natt keepalives.*/ -static int ipsec_policy_count = 0; +__private_extern__ int natt_keepalive_interval = 20; /* interval between natt keepalives.*/ +__private_extern__ int ipsec_policy_count = 0; static int ipsec_sav_count = 0; static u_int32_t acq_seq = 0; @@ -209,6 +233,8 @@ static const int minsize[] = { 0, /* SADB_X_EXT_KMPRIVATE */ sizeof(struct sadb_x_policy), /* SADB_X_EXT_POLICY */ sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ + sizeof(struct sadb_session_id), /* SADB_EXT_SESSION_ID */ + sizeof(struct sadb_sastat), /* SADB_EXT_SASTAT */ }; static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ @@ -231,6 +257,8 @@ static const int maxsize[] = { 0, /* SADB_X_EXT_KMPRIVATE */ 0, /* SADB_X_EXT_POLICY */ sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ + 0, /* SADB_EXT_SESSION_ID */ + 0, /* SADB_EXT_SASTAT */ }; static int ipsec_esp_keymin = 256; @@ -238,61 +266,61 @@ static int ipsec_esp_auth = 0; static int ipsec_ah_keymin = 128; SYSCTL_DECL(_net_key); - -SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug, CTLFLAG_RW, \ +/* Thread safe: no accumulated state */ +SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_debug_level, 0, ""); /* max count of trial for the decision of spi value */ -SYSCTL_INT(_net_key, KEYCTL_SPI_TRY, spi_trycnt, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_SPI_TRY, spi_trycnt, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_spi_trycnt, 0, ""); /* minimum spi value to allocate automatically. */ -SYSCTL_INT(_net_key, KEYCTL_SPI_MIN_VALUE, spi_minval, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_SPI_MIN_VALUE, spi_minval, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_spi_minval, 0, ""); /* maximun spi value to allocate automatically. */ -SYSCTL_INT(_net_key, KEYCTL_SPI_MAX_VALUE, spi_maxval, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_SPI_MAX_VALUE, spi_maxval, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_spi_maxval, 0, ""); /* interval to initialize randseed */ -SYSCTL_INT(_net_key, KEYCTL_RANDOM_INT, int_random, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_RANDOM_INT, int_random, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_int_random, 0, ""); -/* lifetime for larval SA */ -SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME, larval_lifetime, CTLFLAG_RW, \ +/* lifetime for larval SA; thread safe due to > compare */ +SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME, larval_lifetime, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_larval_lifetime, 0, ""); /* counter for blocking to send SADB_ACQUIRE to IKEd */ -SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_blockacq_count, 0, ""); -/* lifetime for blocking to send SADB_ACQUIRE to IKEd */ -SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ +/* lifetime for blocking to send SADB_ACQUIRE to IKEd: Thread safe, > compare */ +SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW | CTLFLAG_LOCKED, \ &key_blockacq_lifetime, 0, ""); /* ESP auth */ -SYSCTL_INT(_net_key, KEYCTL_ESP_AUTH, esp_auth, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_ESP_AUTH, esp_auth, CTLFLAG_RW | CTLFLAG_LOCKED, \ &ipsec_esp_auth, 0, ""); /* minimum ESP key length */ -SYSCTL_INT(_net_key, KEYCTL_ESP_KEYMIN, esp_keymin, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_ESP_KEYMIN, esp_keymin, CTLFLAG_RW | CTLFLAG_LOCKED, \ &ipsec_esp_keymin, 0, ""); /* minimum AH key length */ -SYSCTL_INT(_net_key, KEYCTL_AH_KEYMIN, ah_keymin, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_AH_KEYMIN, ah_keymin, CTLFLAG_RW | CTLFLAG_LOCKED, \ &ipsec_ah_keymin, 0, ""); /* perfered old SA rather than new SA */ -SYSCTL_INT(_net_key, KEYCTL_PREFERED_OLDSA, prefered_oldsa, CTLFLAG_RW,\ +SYSCTL_INT(_net_key, KEYCTL_PREFERED_OLDSA, prefered_oldsa, CTLFLAG_RW | CTLFLAG_LOCKED,\ &key_preferred_oldsa, 0, ""); /* time between NATT keepalives in seconds, 0 disabled */ -SYSCTL_INT(_net_key, KEYCTL_NATT_KEEPALIVE_INTERVAL, natt_keepalive_interval, CTLFLAG_RW,\ +SYSCTL_INT(_net_key, KEYCTL_NATT_KEEPALIVE_INTERVAL, natt_keepalive_interval, CTLFLAG_RW | CTLFLAG_LOCKED,\ &natt_keepalive_interval, 0, ""); /* PF_KEY statistics */ -SYSCTL_STRUCT(_net_key, KEYCTL_PFKEYSTAT, pfkeystat, CTLFLAG_RD,\ +SYSCTL_STRUCT(_net_key, KEYCTL_PFKEYSTAT, pfkeystat, CTLFLAG_RD | CTLFLAG_LOCKED,\ &pfkeystat, pfkeystat, ""); #ifndef LIST_FOREACH @@ -333,21 +361,21 @@ do { \ #if 1 #define KMALLOC_WAIT(p, t, n) \ - ((p) = (t) _MALLOC((unsigned long)(n), M_SECA, M_WAITOK)) + ((p) = (t) _MALLOC((u_int32_t)(n), M_SECA, M_WAITOK)) #define KMALLOC_NOWAIT(p, t, n) \ - ((p) = (t) _MALLOC((unsigned long)(n), M_SECA, M_NOWAIT)) + ((p) = (t) _MALLOC((u_int32_t)(n), M_SECA, M_NOWAIT)) #define KFREE(p) \ _FREE((caddr_t)(p), M_SECA); #else #define KMALLOC_WAIT(p, t, n) \ do { \ - ((p) = (t)_MALLOC((unsigned long)(n), M_SECA, M_WAITOK)); \ + ((p) = (t)_MALLOC((u_int32_t)(n), M_SECA, M_WAITOK)); \ printf("%s %d: %p <- KMALLOC_WAIT(%s, %d)\n", \ __FILE__, __LINE__, (p), #t, n); \ } while (0) #define KMALLOC_NOWAIT(p, t, n) \ do { \ - ((p) = (t)_MALLOC((unsigned long)(n), M_SECA, M_NOWAIT)); \ + ((p) = (t)_MALLOC((u_int32_t)(n), M_SECA, M_NOWAIT)); \ printf("%s %d: %p <- KMALLOC_NOWAIT(%s, %d)\n", \ __FILE__, __LINE__, (p), #t, n); \ } while (0) @@ -384,13 +412,13 @@ do { \ (idx)->proto = (p); \ (idx)->mode = (m); \ (idx)->reqid = (r); \ - bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ - bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ + bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ + bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ } while (0) /* key statistics */ struct _keystat { - u_long getspi_count; /* the avarage of count to try to get new SPI */ + u_int32_t getspi_count; /* the avarage of count to try to get new SPI */ } keystat; struct sadb_msghdr { @@ -425,11 +453,9 @@ static struct mbuf *key_setdumpsp(struct secpolicy *, u_int8_t, u_int32_t, u_int32_t); static u_int key_getspreqmsglen(struct secpolicy *); static int key_spdexpire(struct secpolicy *); -static struct secashead *key_newsah(struct secasindex *); -static void key_delsah(struct secashead *); +static struct secashead *key_newsah(struct secasindex *, u_int8_t); static struct secasvar *key_newsav(struct mbuf *, const struct sadb_msghdr *, struct secashead *, int *); -static void key_delsav(struct secasvar *); static struct secashead *key_getsah(struct secasindex *); static struct secasvar *key_checkspidup(struct secasindex *, u_int32_t); static void key_setspi __P((struct secasvar *, u_int32_t)); @@ -455,6 +481,7 @@ static void *key_newbuf(const void *, u_int); #if INET6 static int key_ismyaddr6(struct sockaddr_in6 *); #endif +static void key_update_natt_keepalive_timestamp(struct secasvar *, struct secasvar *); /* flags for key_cmpsaidx() */ #define CMP_HEAD 0x1 /* protocol, addresses. */ @@ -519,21 +546,36 @@ static int key_promisc(struct socket *, struct mbuf *, static int key_senderror(struct socket *, struct mbuf *, int); static int key_validate_ext(const struct sadb_ext *, int); static int key_align(struct mbuf *, struct sadb_msghdr *); -static void key_sa_chgstate(struct secasvar *, u_int8_t); static struct mbuf *key_alloc_mbuf(int); +static int key_getsastat (struct socket *, struct mbuf *, const struct sadb_msghdr *); +static int key_setsaval2(struct secasvar *sav, + u_int8_t satype, + u_int8_t alg_auth, + u_int8_t alg_enc, + u_int32_t flags, + u_int8_t replay, + struct sadb_key *key_auth, + u_int16_t key_auth_len, + struct sadb_key *key_enc, + u_int16_t key_enc_len, + u_int16_t natt_port, + u_int32_t seq, + u_int32_t spi, + u_int32_t pid, + struct sadb_lifetime *lifetime_hard, + struct sadb_lifetime *lifetime_soft); extern int ipsec_bypass; -void ipsec_send_natt_keepalive(struct secasvar *sav); +extern int esp_udp_encap_port; +int ipsec_send_natt_keepalive(struct secasvar *sav); void key_init(void); -static errno_t ipsecif_register_control(void); - /* * PF_KEY init - * setup locks and call raw_init() + * setup locks, call raw_init(), and then init timer and associated data * */ void @@ -541,33 +583,65 @@ key_init(void) { int i; - + + _CASSERT(PFKEY_ALIGN8(sizeof(struct sadb_msg)) <= _MHLEN); + sadb_mutex_grp_attr = lck_grp_attr_alloc_init(); sadb_mutex_grp = lck_grp_alloc_init("sadb", sadb_mutex_grp_attr); sadb_mutex_attr = lck_attr_alloc_init(); - if ((sadb_mutex = lck_mtx_alloc_init(sadb_mutex_grp, sadb_mutex_attr)) == NULL) { - printf("key_init: can't alloc sadb_mutex\n"); - return; - } + lck_mtx_init(sadb_mutex, sadb_mutex_grp, sadb_mutex_attr); pfkey_stat_mutex_grp_attr = lck_grp_attr_alloc_init(); pfkey_stat_mutex_grp = lck_grp_alloc_init("pfkey_stat", pfkey_stat_mutex_grp_attr); pfkey_stat_mutex_attr = lck_attr_alloc_init(); - if ((pfkey_stat_mutex = lck_mtx_alloc_init(pfkey_stat_mutex_grp, pfkey_stat_mutex_attr)) == NULL) { - printf("key_init: can't alloc pfkey_stat_mutex\n"); - return; - } + lck_mtx_init(pfkey_stat_mutex, pfkey_stat_mutex_grp, pfkey_stat_mutex_attr); for (i = 0; i < SPIHASHSIZE; i++) LIST_INIT(&spihash[i]); raw_init(); - /* register ip_if application of kernel control */ - ipsecif_register_control(); + bzero((caddr_t)&key_cb, sizeof(key_cb)); + for (i = 0; i < IPSEC_DIR_MAX; i++) { + LIST_INIT(&sptree[i]); + } + ipsec_policy_count = 0; + + LIST_INIT(&sahtree); + + for (i = 0; i <= SADB_SATYPE_MAX; i++) { + LIST_INIT(®tree[i]); + } + ipsec_sav_count = 0; + +#ifndef IPSEC_NONBLOCK_ACQUIRE + LIST_INIT(&acqtree); +#endif + LIST_INIT(&spacqtree); + + /* system default */ +#if INET + ip4_def_policy.policy = IPSEC_POLICY_NONE; + ip4_def_policy.refcnt++; /*never reclaim this*/ +#endif +#if INET6 + ip6_def_policy.policy = IPSEC_POLICY_NONE; + ip6_def_policy.refcnt++; /*never reclaim this*/ +#endif + +#ifndef IPSEC_DEBUG2 + timeout((void *)key_timehandler, (void *)0, hz); +#endif /*IPSEC_DEBUG2*/ + + /* initialize key statistics */ + keystat.getspi_count = 1; + +#ifndef __APPLE__ + printf("IPsec: Initialized Security Association Processing.\n"); +#endif } @@ -579,9 +653,9 @@ key_init(void) * others: found and return the pointer. */ struct secpolicy * -key_allocsp(spidx, dir) - struct secpolicyindex *spidx; - u_int dir; +key_allocsp( + struct secpolicyindex *spidx, + u_int dir) { struct secpolicy *sp; struct timeval tv; @@ -640,8 +714,11 @@ found: * XXX slow */ struct secpolicy * -key_gettunnel(osrc, odst, isrc, idst) - struct sockaddr *osrc, *odst, *isrc, *idst; +key_gettunnel( + struct sockaddr *osrc, + struct sockaddr *odst, + struct sockaddr *isrc, + struct sockaddr *idst) { struct secpolicy *sp; const int dir = IPSEC_DIR_INBOUND; @@ -714,10 +791,10 @@ found: * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. */ int -key_checkrequest(isr, saidx, sav) - struct ipsecrequest *isr; - struct secasindex *saidx; - struct secasvar **sav; +key_checkrequest( + struct ipsecrequest *isr, + struct secasindex *saidx, + struct secasvar **sav) { u_int level; int error; @@ -784,8 +861,8 @@ key_checkrequest(isr, saidx, sav) u_int32_t sah_search_calls = 0; u_int32_t sah_search_count = 0; struct secasvar * -key_allocsa_policy(saidx) - struct secasindex *saidx; +key_allocsa_policy( + struct secasindex *saidx) { struct secashead *sah; struct secasvar *sav; @@ -849,10 +926,10 @@ key_allocsa_policy(saidx) * others : found, pointer to a SA. */ static struct secasvar * -key_do_allocsa_policy(sah, state, dstport) - struct secashead *sah; - u_int state; - u_int16_t dstport; +key_do_allocsa_policy( + struct secashead *sah, + u_int state, + u_int16_t dstport) { struct secasvar *sav, *nextsav, *candidate, *natt_candidate, *no_natt_candidate, *d; @@ -1030,10 +1107,12 @@ key_do_allocsa_policy(sah, state, dstport) * keep source address in IPsec SA. We see a tricky situation here. */ struct secasvar * -key_allocsa(family, src, dst, proto, spi) - u_int family, proto; - caddr_t src, dst; - u_int32_t spi; +key_allocsa( + u_int family, + caddr_t src, + caddr_t dst, + u_int proto, + u_int32_t spi) { struct secasvar *sav, *match; u_int stateidx, state, tmpidx, matchidx; @@ -1184,8 +1263,8 @@ found: } u_int16_t -key_natt_get_translated_port(outsav) - struct secasvar *outsav; +key_natt_get_translated_port( + struct secasvar *outsav) { struct secasindex saidx; @@ -1241,10 +1320,10 @@ found: } static int -key_do_get_translated_port(sah, outsav, state) - struct secashead *sah; - struct secasvar *outsav; - u_int state; +key_do_get_translated_port( + struct secashead *sah, + struct secasvar *outsav, + u_int state) { struct secasvar *currsav, *nextsav, *candidate; @@ -1308,9 +1387,9 @@ key_do_get_translated_port(sah, outsav, state) * For both the packet without socket and key_freeso(). */ void -key_freesp(sp, locked) - struct secpolicy *sp; - int locked; +key_freesp( + struct secpolicy *sp, + int locked) { /* sanity check */ @@ -1341,8 +1420,8 @@ static void key_freesp_so(struct secpolicy **); * For the packet with socket. */ void -key_freeso(so) - struct socket *so; +key_freeso( + struct socket *so) { /* sanity check */ @@ -1399,8 +1478,8 @@ done: } static void -key_freesp_so(sp) - struct secpolicy **sp; +key_freesp_so( + struct secpolicy **sp) { /* sanity check */ @@ -1434,9 +1513,9 @@ key_freesp_so(sp) * for a policy. */ void -key_freesav(sav, locked) - struct secasvar *sav; - int locked; +key_freesav( + struct secasvar *sav, + int locked) { /* sanity check */ @@ -1464,8 +1543,8 @@ key_freesav(sav, locked) * free security policy entry. */ static void -key_delsp(sp) - struct secpolicy *sp; +key_delsp( + struct secpolicy *sp) { /* sanity check */ @@ -1504,8 +1583,8 @@ key_delsp(sp) * others : found, pointer to a SP. */ static struct secpolicy * -key_getsp(spidx) - struct secpolicyindex *spidx; +key_getsp( + struct secpolicyindex *spidx) { struct secpolicy *sp; @@ -1533,8 +1612,8 @@ key_getsp(spidx) * others : found, pointer to a SP. */ static struct secpolicy * -key_getspbyid(id) - u_int32_t id; +key_getspbyid( + u_int32_t id) { struct secpolicy *sp; @@ -1562,7 +1641,7 @@ key_getspbyid(id) } struct secpolicy * -key_newsp() +key_newsp(void) { struct secpolicy *newsp = NULL; @@ -1583,10 +1662,10 @@ key_newsp() * so must be set properly later. */ struct secpolicy * -key_msg2sp(xpl0, len, error) - struct sadb_x_policy *xpl0; - size_t len; - int *error; +key_msg2sp( + struct sadb_x_policy *xpl0, + size_t len, + int *error) { struct secpolicy *newsp; @@ -1788,8 +1867,8 @@ key_msg2sp(xpl0, len, error) return NULL; } - xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr - + xisr->sadb_x_ipsecrequest_len); + xisr = (struct sadb_x_ipsecrequest *)(void *) + ((caddr_t)xisr + xisr->sadb_x_ipsecrequest_len); } } break; @@ -1805,17 +1884,44 @@ key_msg2sp(xpl0, len, error) } static u_int32_t -key_newreqid() +key_newreqid(void) { lck_mtx_lock(sadb_mutex); static u_int32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1; + int done = 0; - auto_reqid = (auto_reqid == ~0 - ? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1); - lck_mtx_unlock(sadb_mutex); + /* The reqid must be limited to 16 bits because the PF_KEY message format only uses + 16 bits for this field. Once it becomes larger than 16 bits - ipsec fails to + work anymore. Changing the PF_KEY message format would introduce compatibility + issues. This code now tests to see if the tentative reqid is in use */ - /* XXX should be unique check */ + while (!done) { + struct secpolicy *sp; + struct ipsecrequest *isr; + int dir; + + auto_reqid = (auto_reqid == 0xFFFF + ? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1); + + /* check for uniqueness */ + done = 1; + for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { + LIST_FOREACH(sp, &sptree[dir], chain) { + for (isr = sp->req; isr != NULL; isr = isr->next) { + if (isr->saidx.reqid == auto_reqid) { + done = 0; + break; + } + } + if (done == 0) + break; + } + if (done == 0) + break; + } + } + lck_mtx_unlock(sadb_mutex); return auto_reqid; } @@ -1823,8 +1929,8 @@ key_newreqid() * copy secpolicy struct to sadb_x_policy structure indicated. */ struct mbuf * -key_sp2msg(sp) - struct secpolicy *sp; +key_sp2msg( + struct secpolicy *sp) { struct sadb_x_policy *xpl; int tlen; @@ -1863,7 +1969,7 @@ key_sp2msg(sp) for (isr = sp->req; isr != NULL; isr = isr->next) { - xisr = (struct sadb_x_ipsecrequest *)p; + xisr = (struct sadb_x_ipsecrequest *)(void *)p; xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto; xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode; @@ -1916,7 +2022,7 @@ key_gather_mbuf(struct mbuf *m, const struct sadb_msghdr *mhp, if (len > MHLEN) panic("assumption failed"); #endif - MGETHDR(n, M_DONTWAIT, MT_DATA); + MGETHDR(n, M_WAITOK, MT_DATA); if (!n) goto fail; n->m_len = len; @@ -1935,7 +2041,7 @@ key_gather_mbuf(struct mbuf *m, const struct sadb_msghdr *mhp, mtod(n, caddr_t)); } else { n = m_copym(m, mhp->extoff[idx], mhp->extlen[idx], - M_DONTWAIT); + M_WAITOK); } if (n == NULL) goto fail; @@ -1976,10 +2082,10 @@ fail: * m will always be freed. */ static int -key_spdadd(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spdadd( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0, *xpl; @@ -2013,12 +2119,13 @@ key_spdadd(so, m, mhp) ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n")); return key_senderror(so, m, EINVAL); } - lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; + lft = (struct sadb_lifetime *) + (void *)mhp->ext[SADB_EXT_LIFETIME_HARD]; } src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; - xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; + xpl0 = (struct sadb_x_policy *)(void *)mhp->ext[SADB_X_EXT_POLICY]; /* make secindex */ /* XXX boundary check against sa_len */ @@ -2217,7 +2324,7 @@ key_spdadd(so, m, mhp) /* n is already freed */ return key_senderror(so, m, ENOBUFS); } - xpl = (struct sadb_x_policy *)(mtod(mpolicy, caddr_t) + off); + xpl = (struct sadb_x_policy *)(void *)(mtod(mpolicy, caddr_t) + off); if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) { m_freem(n); return key_senderror(so, m, EINVAL); @@ -2236,7 +2343,7 @@ key_spdadd(so, m, mhp) * others: success. */ static u_int32_t -key_getnewspid() +key_getnewspid(void) { u_int32_t newid = 0; int count = key_spi_trycnt; /* XXX */ @@ -2274,10 +2381,10 @@ key_getnewspid() * m will always be freed. */ static int -key_spddelete(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spddelete( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0; @@ -2305,7 +2412,7 @@ key_spddelete(so, m, mhp) src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; - xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; + xpl0 = (struct sadb_x_policy *)(void *)mhp->ext[SADB_X_EXT_POLICY]; /* make secindex */ /* XXX boundary check against sa_len */ @@ -2376,10 +2483,10 @@ key_spddelete(so, m, mhp) * m will always be freed. */ static int -key_spddelete2(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spddelete2( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { u_int32_t id; struct secpolicy *sp; @@ -2397,7 +2504,8 @@ key_spddelete2(so, m, mhp) return 0; } - id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; + id = ((struct sadb_x_policy *) + (void *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; /* Is there SP in SPD ? */ lck_mtx_lock(sadb_mutex); @@ -2421,9 +2529,9 @@ key_spddelete2(so, m, mhp) if (len > MCLBYTES) return key_senderror(so, m, ENOBUFS); - MGETHDR(n, M_DONTWAIT, MT_DATA); + MGETHDR(n, M_WAITOK, MT_DATA); if (n && len > MHLEN) { - MCLGET(n, M_DONTWAIT); + MCLGET(n, M_WAITOK); if ((n->m_flags & M_EXT) == 0) { m_freem(n); n = NULL; @@ -2445,7 +2553,7 @@ key_spddelete2(so, m, mhp) #endif n->m_next = m_copym(m, mhp->extoff[SADB_X_EXT_POLICY], - mhp->extlen[SADB_X_EXT_POLICY], M_DONTWAIT); + mhp->extlen[SADB_X_EXT_POLICY], M_WAITOK); if (!n->m_next) { m_freem(n); return key_senderror(so, m, ENOBUFS); @@ -2477,10 +2585,10 @@ key_spddelete2(so, m, mhp) * m will always be freed. */ static int -key_spdget(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spdget( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { u_int32_t id; struct secpolicy *sp; @@ -2498,7 +2606,8 @@ key_spdget(so, m, mhp) return key_senderror(so, m, EINVAL); } - id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; + id = ((struct sadb_x_policy *) + (void *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; /* Is there SP in SPD ? */ lck_mtx_lock(sadb_mutex); @@ -2532,8 +2641,8 @@ key_spdget(so, m, mhp) * others: error number */ int -key_spdacquire(sp) - struct secpolicy *sp; +key_spdacquire( + struct secpolicy *sp) { struct mbuf *result = NULL, *m; struct secspacq *newspacq; @@ -2607,10 +2716,10 @@ fail: * m will always be freed. */ static int -key_spdflush(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spdflush( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_msg *newmsg; struct secpolicy *sp; @@ -2660,10 +2769,10 @@ key_spdflush(so, m, mhp) */ static int -key_spddump(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_spddump( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct secpolicy *sp, **spbuf = NULL, **sp_ptr; int cnt = 0, bufcount; @@ -2732,10 +2841,11 @@ end: } static struct mbuf * -key_setdumpsp(sp, type, seq, pid) - struct secpolicy *sp; - u_int8_t type; - u_int32_t seq, pid; +key_setdumpsp( + struct secpolicy *sp, + u_int8_t type, + u_int32_t seq, + u_int32_t pid) { struct mbuf *result = NULL, *m; @@ -2790,8 +2900,8 @@ fail: * get PFKEY message length for security policy and request. */ static u_int -key_getspreqmsglen(sp) - struct secpolicy *sp; +key_getspreqmsglen( + struct secpolicy *sp) { u_int tlen; @@ -2828,12 +2938,12 @@ key_getspreqmsglen(sp) * others : error number */ static int -key_spdexpire(sp) - struct secpolicy *sp; +key_spdexpire( + struct secpolicy *sp) { struct mbuf *result = NULL, *m; int len; - int error = -1; + int error = EINVAL; struct sadb_lifetime *lt; lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED); @@ -2867,7 +2977,7 @@ key_spdexpire(sp) lt->sadb_lifetime_bytes = 0; lt->sadb_lifetime_addtime = sp->created; lt->sadb_lifetime_usetime = sp->lastused; - lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); + lt = (struct sadb_lifetime *)(void *)(mtod(m, caddr_t) + len / 2); lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; lt->sadb_lifetime_allocations = 0; @@ -2939,8 +3049,9 @@ key_spdexpire(sp) * others : pointer to new SA head. */ static struct secashead * -key_newsah(saidx) - struct secasindex *saidx; +key_newsah( + struct secasindex *saidx, + u_int8_t dir) { struct secashead *newsah; @@ -2976,6 +3087,7 @@ key_newsah(saidx) break; } + newsah->dir = dir; /* add to saidxtree */ newsah->state = SADB_SASTATE_MATURE; LIST_INSERT_HEAD(&sahtree, newsah, chain); @@ -2986,9 +3098,9 @@ key_newsah(saidx) /* * delete SA index and all SA registerd. */ -static void -key_delsah(sah) - struct secashead *sah; +void +key_delsah( + struct secashead *sah) { struct secasvar *sav, *nextsav; u_int stateidx, state; @@ -3060,11 +3172,11 @@ key_delsah(sah) * does not modify mbuf. does not free mbuf on error. */ static struct secasvar * -key_newsav(m, mhp, sah, errp) - struct mbuf *m; - const struct sadb_msghdr *mhp; - struct secashead *sah; - int *errp; +key_newsav( + struct mbuf *m, + const struct sadb_msghdr *mhp, + struct secashead *sah, + int *errp) { struct secasvar *newsav; const struct sadb_sa *xsa; @@ -3110,7 +3222,7 @@ key_newsav(m, mhp, sah, errp) *errp = EINVAL; return NULL; } - xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + xsa = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; key_setspi(newsav, xsa->sadb_sa_spi); newsav->seq = mhp->msg->sadb_msg_seq; break; @@ -3151,12 +3263,117 @@ key_newsav(m, mhp, sah, errp) return newsav; } +/* + * allocating a new SA with LARVAL state. key_add() and key_getspi() call, + * and copy the values passed into new buffer. + * When SAD message type is GETSPI: + * to set sequence number from acq_seq++, + * to set zero to SPI. + * not to call key_setsava(). + * OUT: NULL : fail + * others : pointer to new secasvar. + */ +struct secasvar * +key_newsav2(struct secashead *sah, + u_int8_t satype, + u_int8_t alg_auth, + u_int8_t alg_enc, + u_int32_t flags, + u_int8_t replay, + struct sadb_key *key_auth, + u_int16_t key_auth_len, + struct sadb_key *key_enc, + u_int16_t key_enc_len, + u_int16_t natt_port, + u_int32_t seq, + u_int32_t spi, + u_int32_t pid, + struct sadb_lifetime *lifetime_hard, + struct sadb_lifetime *lifetime_soft) +{ + struct secasvar *newsav; + + lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); + + /* sanity check */ + if (sah == NULL) + panic("key_newsa: NULL pointer is passed.\n"); + + KMALLOC_NOWAIT(newsav, struct secasvar *, sizeof(struct secasvar)); + if (newsav == NULL) { + lck_mtx_unlock(sadb_mutex); + KMALLOC_WAIT(newsav, struct secasvar *, sizeof(struct secasvar)); + lck_mtx_lock(sadb_mutex); + if (newsav == NULL) { + ipseclog((LOG_DEBUG, "key_newsa: No more memory.\n")); + return NULL; + } + } + bzero((caddr_t)newsav, sizeof(struct secasvar)); + +#if IPSEC_DOSEQCHECK + /* sync sequence number */ + if (seq == 0) + newsav->seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); + else +#endif + newsav->seq = seq; + key_setspi(newsav, spi); + + if (key_setsaval2(newsav, + satype, + alg_auth, + alg_enc, + flags, + replay, + key_auth, + key_auth_len, + key_enc, + key_enc_len, + natt_port, + seq, + spi, + pid, + lifetime_hard, + lifetime_soft)) { + if (newsav->spihash.le_prev || newsav->spihash.le_next) + LIST_REMOVE(newsav, spihash); + KFREE(newsav); + return NULL; + } + + /* reset created */ + { + struct timeval tv; + microtime(&tv); + newsav->created = tv.tv_sec; + } + + newsav->pid = pid; + + /* add to satree */ + newsav->sah = sah; + newsav->refcnt = 1; + if (spi && key_auth && key_auth_len && key_enc && key_enc_len) { + newsav->state = SADB_SASTATE_MATURE; + LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_MATURE], newsav, + secasvar, chain); + } else { + newsav->state = SADB_SASTATE_LARVAL; + LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav, + secasvar, chain); + } + ipsec_sav_count++; + + return newsav; +} + /* * free() SA variable entry. */ -static void -key_delsav(sav) - struct secasvar *sav; +void +key_delsav( + struct secasvar *sav) { lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); @@ -3224,8 +3441,8 @@ key_delsav(sav) * others : found, pointer to a SA. */ static struct secashead * -key_getsah(saidx) - struct secasindex *saidx; +key_getsah( + struct secasindex *saidx) { struct secashead *sah; @@ -3241,6 +3458,21 @@ key_getsah(saidx) return NULL; } +struct secashead * +key_newsah2 (struct secasindex *saidx, + u_int8_t dir) +{ + struct secashead *sah; + + lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); + + sah = key_getsah(saidx); + if (!sah) { + return(key_newsah(saidx, dir)); + } + return sah; +} + /* * check not to be duplicated SPI. * NOTE: this function is too slow due to searching all SAD. @@ -3249,9 +3481,9 @@ key_getsah(saidx) * others : found, pointer to a SA. */ static struct secasvar * -key_checkspidup(saidx, spi) - struct secasindex *saidx; - u_int32_t spi; +key_checkspidup( + struct secasindex *saidx, + u_int32_t spi) { struct secasvar *sav; u_int stateidx, state; @@ -3282,9 +3514,9 @@ key_checkspidup(saidx, spi) } static void -key_setspi(sav, spi) - struct secasvar *sav; - u_int32_t spi; +key_setspi( + struct secasvar *sav, + u_int32_t spi) { lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); sav->spi = spi; @@ -3301,9 +3533,9 @@ key_setspi(sav, spi) * others : found, pointer to a SA. */ static struct secasvar * -key_getsavbyspi(sah, spi) - struct secashead *sah; - u_int32_t spi; +key_getsavbyspi( + struct secashead *sah, + u_int32_t spi) { struct secasvar *sav, *match; u_int stateidx, state, matchidx; @@ -3338,10 +3570,10 @@ key_getsavbyspi(sah, spi) * does not modify mbuf. does not free mbuf on error. */ static int -key_setsaval(sav, m, mhp) - struct secasvar *sav; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_setsaval( + struct secasvar *sav, + struct mbuf *m, + const struct sadb_msghdr *mhp) { #if IPSEC_ESP const struct esp_algorithm *algo; @@ -3373,7 +3605,7 @@ key_setsaval(sav, m, mhp) if (mhp->ext[SADB_EXT_SA] != NULL) { const struct sadb_sa *sa0; - sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) { ipseclog((LOG_DEBUG, "key_setsaval: invalid message size.\n")); error = EINVAL; @@ -3390,12 +3622,12 @@ key_setsaval(sav, m, mhp) */ if ((sav->flags & SADB_X_EXT_NATT) != 0) { if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa_2) || - ((struct sadb_sa_2*)(sa0))->sadb_sa_natt_port == 0) { + ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_port == 0) { ipseclog((LOG_DEBUG, "key_setsaval: natt port not set.\n")); error = EINVAL; goto fail; } - sav->remote_ike_port = ((struct sadb_sa_2*)(sa0))->sadb_sa_natt_port; + sav->remote_ike_port = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_port; } /* @@ -3572,7 +3804,8 @@ key_setsaval(sav, m, mhp) { const struct sadb_lifetime *lft0; - lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; + lft0 = (struct sadb_lifetime *) + (void *)mhp->ext[SADB_EXT_LIFETIME_HARD]; if (lft0 != NULL) { if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) { ipseclog((LOG_DEBUG, "key_setsaval: invalid hard lifetime ext len.\n")); @@ -3589,7 +3822,8 @@ key_setsaval(sav, m, mhp) /* to be initialize ? */ } - lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT]; + lft0 = (struct sadb_lifetime *) + (void *)mhp->ext[SADB_EXT_LIFETIME_SOFT]; if (lft0 != NULL) { if (mhp->extlen[SADB_EXT_LIFETIME_SOFT] < sizeof(*lft0)) { ipseclog((LOG_DEBUG, "key_setsaval: invalid soft lifetime ext len.\n")); @@ -3651,62 +3885,282 @@ key_setsaval(sav, m, mhp) } /* - * validation with a secasvar entry, and set SADB_SATYPE_MATURE. - * OUT: 0: valid - * other: errno + * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*. + * You must update these if need. + * OUT: 0: success. + * !0: failure. + * + * does not modify mbuf. does not free mbuf on error. */ -static int -key_mature(sav) - struct secasvar *sav; +int +key_setsaval2(struct secasvar *sav, + u_int8_t satype, + u_int8_t alg_auth, + u_int8_t alg_enc, + u_int32_t flags, + u_int8_t replay, + struct sadb_key *key_auth, + u_int16_t key_auth_len, + struct sadb_key *key_enc, + u_int16_t key_enc_len, + u_int16_t natt_port, + u_int32_t seq, + u_int32_t spi, + u_int32_t pid, + struct sadb_lifetime *lifetime_hard, + struct sadb_lifetime *lifetime_soft) { - int mature; - int checkmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ - int mustmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ - - mature = 0; +#if IPSEC_ESP + const struct esp_algorithm *algo; +#endif + int error = 0; + struct timeval tv; lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); - /* check SPI value */ - switch (sav->sah->saidx.proto) { - case IPPROTO_ESP: - case IPPROTO_AH: - if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) { - ipseclog((LOG_DEBUG, - "key_mature: illegal range of SPI %u.\n", - (u_int32_t)ntohl(sav->spi))); - return EINVAL; + /* initialization */ + sav->replay = NULL; + sav->key_auth = NULL; + sav->key_enc = NULL; + sav->sched = NULL; + sav->schedlen = 0; + sav->iv = NULL; + sav->lft_c = NULL; + sav->lft_h = NULL; + sav->lft_s = NULL; + sav->remote_ike_port = 0; + sav->natt_last_activity = natt_now; + sav->natt_encapsulated_src_port = 0; + + sav->alg_auth = alg_auth; + sav->alg_enc = alg_enc; + sav->flags = flags; + sav->pid = pid; + sav->seq = seq; + key_setspi(sav, htonl(spi)); + + /* + * Verify that a nat-traversal port was specified if + * the nat-traversal flag is set. + */ + if ((sav->flags & SADB_X_EXT_NATT) != 0) { + if (natt_port == 0) { + ipseclog((LOG_DEBUG, "key_setsaval2: natt port not set.\n")); + error = EINVAL; + goto fail; } - break; + sav->remote_ike_port = natt_port; } - /* check satype */ - switch (sav->sah->saidx.proto) { - case IPPROTO_ESP: - /* check flags */ - if ((sav->flags & SADB_X_EXT_OLD) - && (sav->flags & SADB_X_EXT_DERIV)) { - ipseclog((LOG_DEBUG, "key_mature: " - "invalid flag (derived) given to old-esp.\n")); - return EINVAL; - } - if (sav->alg_auth == SADB_AALG_NONE) - checkmask = 1; - else - checkmask = 3; - mustmask = 1; - break; - case IPPROTO_AH: - /* check flags */ - if (sav->flags & SADB_X_EXT_DERIV) { - ipseclog((LOG_DEBUG, "key_mature: " - "invalid flag (derived) given to AH SA.\n")); - return EINVAL; - } - if (sav->alg_enc != SADB_EALG_NONE) { - ipseclog((LOG_DEBUG, "key_mature: " - "protocol and algorithm mismated.\n")); - return(EINVAL); + /* + * Verify if SADB_X_EXT_NATT_MULTIPLEUSERS flag is set that + * SADB_X_EXT_NATT is set and SADB_X_EXT_NATT_KEEPALIVE is not + * set (we're not behind nat) - otherwise clear it. + */ + if ((sav->flags & SADB_X_EXT_NATT_MULTIPLEUSERS) != 0) + if ((sav->flags & SADB_X_EXT_NATT) == 0 || + (sav->flags & SADB_X_EXT_NATT_KEEPALIVE) != 0) + sav->flags &= ~SADB_X_EXT_NATT_MULTIPLEUSERS; + + /* replay window */ + if ((flags & SADB_X_EXT_OLD) == 0) { + sav->replay = keydb_newsecreplay(replay); + if (sav->replay == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + } + + /* Authentication keys */ + sav->key_auth = (__typeof__(sav->key_auth))key_newbuf(key_auth, key_auth_len); + if (sav->key_auth == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + + /* Encryption key */ + sav->key_enc = (__typeof__(sav->key_enc))key_newbuf(key_enc, key_enc_len); + if (sav->key_enc == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + + /* set iv */ + sav->ivlen = 0; + + if (satype == SADB_SATYPE_ESP) { +#if IPSEC_ESP + algo = esp_algorithm_lookup(sav->alg_enc); + if (algo && algo->ivlen) + sav->ivlen = (*algo->ivlen)(algo, sav); + if (sav->ivlen != 0) { + KMALLOC_NOWAIT(sav->iv, caddr_t, sav->ivlen); + if (sav->iv == 0) { + lck_mtx_unlock(sadb_mutex); + KMALLOC_WAIT(sav->iv, caddr_t, sav->ivlen); + lck_mtx_lock(sadb_mutex); + if (sav->iv == 0) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + } + /* initialize */ + key_randomfill(sav->iv, sav->ivlen); + } +#endif + } + + /* reset created */ + microtime(&tv); + sav->created = tv.tv_sec; + + /* make lifetime for CURRENT */ + KMALLOC_NOWAIT(sav->lft_c, struct sadb_lifetime *, + sizeof(struct sadb_lifetime)); + if (sav->lft_c == NULL) { + lck_mtx_unlock(sadb_mutex); + KMALLOC_WAIT(sav->lft_c, struct sadb_lifetime *, + sizeof(struct sadb_lifetime)); + lck_mtx_lock(sadb_mutex); + if (sav->lft_c == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + } + + microtime(&tv); + + sav->lft_c->sadb_lifetime_len = + PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + sav->lft_c->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + sav->lft_c->sadb_lifetime_allocations = 0; + sav->lft_c->sadb_lifetime_bytes = 0; + sav->lft_c->sadb_lifetime_addtime = tv.tv_sec; + sav->lft_c->sadb_lifetime_usetime = 0; + + /* lifetimes for HARD and SOFT */ + sav->lft_h = (__typeof__(sav->lft_h))key_newbuf(lifetime_hard, + sizeof(*lifetime_hard)); + if (sav->lft_h == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + sav->lft_s = (__typeof__(sav->lft_s))key_newbuf(lifetime_soft, + sizeof(*lifetime_soft)); + if (sav->lft_s == NULL) { + ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n")); + error = ENOBUFS; + goto fail; + } + + return 0; + +fail: + /* initialization */ + if (sav->replay != NULL) { + keydb_delsecreplay(sav->replay); + sav->replay = NULL; + } + if (sav->key_auth != NULL) { + bzero(_KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth)); + KFREE(sav->key_auth); + sav->key_auth = NULL; + } + if (sav->key_enc != NULL) { + bzero(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc)); + KFREE(sav->key_enc); + sav->key_enc = NULL; + } + if (sav->sched) { + bzero(sav->sched, sav->schedlen); + KFREE(sav->sched); + sav->sched = NULL; + } + if (sav->iv != NULL) { + KFREE(sav->iv); + sav->iv = NULL; + } + if (sav->lft_c != NULL) { + KFREE(sav->lft_c); + sav->lft_c = NULL; + } + if (sav->lft_h != NULL) { + KFREE(sav->lft_h); + sav->lft_h = NULL; + } + if (sav->lft_s != NULL) { + KFREE(sav->lft_s); + sav->lft_s = NULL; + } + + return error; +} + +/* + * validation with a secasvar entry, and set SADB_SATYPE_MATURE. + * OUT: 0: valid + * other: errno + */ +static int +key_mature( + struct secasvar *sav) +{ + int mature; + int checkmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ + int mustmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ + + mature = 0; + + lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); + + /* check SPI value */ + switch (sav->sah->saidx.proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + + /* No reason to test if this is >= 0, because ntohl(sav->spi) is unsigned. */ + if (ntohl(sav->spi) <= 255) { + ipseclog((LOG_DEBUG, + "key_mature: illegal range of SPI %u.\n", + (u_int32_t)ntohl(sav->spi))); + return EINVAL; + } + break; + } + + /* check satype */ + switch (sav->sah->saidx.proto) { + case IPPROTO_ESP: + /* check flags */ + if ((sav->flags & SADB_X_EXT_OLD) + && (sav->flags & SADB_X_EXT_DERIV)) { + ipseclog((LOG_DEBUG, "key_mature: " + "invalid flag (derived) given to old-esp.\n")); + return EINVAL; + } + if (sav->alg_auth == SADB_AALG_NONE) + checkmask = 1; + else + checkmask = 3; + mustmask = 1; + break; + case IPPROTO_AH: + /* check flags */ + if (sav->flags & SADB_X_EXT_DERIV) { + ipseclog((LOG_DEBUG, "key_mature: " + "invalid flag (derived) given to AH SA.\n")); + return EINVAL; + } + if (sav->alg_enc != SADB_EALG_NONE) { + ipseclog((LOG_DEBUG, "key_mature: " + "protocol and algorithm mismated.\n")); + return(EINVAL); } checkmask = 2; mustmask = 2; @@ -3833,10 +4287,12 @@ key_mature(sav) * subroutine for SADB_GET and SADB_DUMP. */ static struct mbuf * -key_setdumpsa(sav, type, satype, seq, pid) - struct secasvar *sav; - u_int8_t type, satype; - u_int32_t seq, pid; +key_setdumpsa( + struct secasvar *sav, + u_int8_t type, + u_int8_t satype, + u_int32_t seq, + u_int32_t pid) { struct mbuf *result = NULL, *tres = NULL, *m; int l = 0; @@ -3937,7 +4393,7 @@ key_setdumpsa(sav, type, satype, seq, pid) if ((!m && !p) || (m && p)) goto fail; if (p && tres) { - M_PREPEND(tres, l, M_DONTWAIT); + M_PREPEND(tres, l, M_WAITOK); if (!tres) goto fail; bcopy(p, mtod(tres, caddr_t), l); @@ -3982,12 +4438,13 @@ fail: * set data into sadb_msg. */ static struct mbuf * -key_setsadbmsg(type, tlen, satype, seq, pid, reserved) - u_int8_t type, satype; - u_int16_t tlen; - u_int32_t seq; - pid_t pid; - u_int16_t reserved; +key_setsadbmsg( + u_int8_t type, + u_int16_t tlen, + u_int8_t satype, + u_int32_t seq, + pid_t pid, + u_int16_t reserved) { struct mbuf *m; struct sadb_msg *p; @@ -4028,8 +4485,8 @@ key_setsadbmsg(type, tlen, satype, seq, pid, reserved) * copy secasvar data into sadb_address. */ static struct mbuf * -key_setsadbsa(sav) - struct secasvar *sav; +key_setsadbsa( + struct secasvar *sav) { struct mbuf *m; struct sadb_sa *p; @@ -4062,11 +4519,11 @@ key_setsadbsa(sav) * set data into sadb_address. */ static struct mbuf * -key_setsadbaddr(exttype, saddr, prefixlen, ul_proto) - u_int16_t exttype; - struct sockaddr *saddr; - u_int8_t prefixlen; - u_int16_t ul_proto; +key_setsadbaddr( + u_int16_t exttype, + struct sockaddr *saddr, + u_int8_t prefixlen, + u_int16_t ul_proto) { struct mbuf *m; struct sadb_address *p; @@ -4109,16 +4566,87 @@ key_setsadbaddr(exttype, saddr, prefixlen, ul_proto) return m; } +/* + * set data into sadb_session_id + */ +static struct mbuf * +key_setsadbsession_id (u_int64_t session_ids[]) +{ + struct mbuf *m; + struct sadb_session_id *p; + size_t len; + + len = PFKEY_ALIGN8(sizeof(*p)); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + + p = mtod(m, __typeof__(p)); + + bzero(p, len); + p->sadb_session_id_len = PFKEY_UNIT64(len); + p->sadb_session_id_exttype = SADB_EXT_SESSION_ID; + p->sadb_session_id_v[0] = session_ids[0]; + p->sadb_session_id_v[1] = session_ids[1]; + + return m; +} + +/* + * copy stats data into sadb_sastat type. + */ +static struct mbuf * +key_setsadbsastat (u_int32_t dir, + struct sastat *stats, + u_int32_t max_stats) +{ + struct mbuf *m; + struct sadb_sastat *p; + int list_len, len; + + if (!stats) { + return NULL; + } + + list_len = sizeof(*stats) * max_stats; + len = PFKEY_ALIGN8(sizeof(*p)) + PFKEY_ALIGN8(list_len); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + + p = mtod(m, __typeof__(p)); + + bzero(p, len); + p->sadb_sastat_len = PFKEY_UNIT64(len); + p->sadb_sastat_exttype = SADB_EXT_SASTAT; + p->sadb_sastat_dir = dir; + p->sadb_sastat_list_len = max_stats; + if (list_len) { + bcopy(stats, + mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(*p)), + list_len); + } + + return m; +} + #if 0 /* * set data into sadb_ident. */ static struct mbuf * -key_setsadbident(exttype, idtype, string, stringlen, id) - u_int16_t exttype, idtype; - caddr_t string; - int stringlen; - u_int64_t id; +key_setsadbident( + u_int16_t exttype, + u_int16_t idtype, + caddr_t string, + int stringlen, + u_int64_t id) { struct mbuf *m; struct sadb_ident *p; @@ -4153,9 +4681,10 @@ key_setsadbident(exttype, idtype, string, stringlen, id) * set data into sadb_x_sa2. */ static struct mbuf * -key_setsadbxsa2(mode, seq, reqid) - u_int8_t mode; - u_int32_t seq, reqid; +key_setsadbxsa2( + u_int8_t mode, + u_int32_t seq, + u_int32_t reqid) { struct mbuf *m; struct sadb_x_sa2 *p; @@ -4187,10 +4716,10 @@ key_setsadbxsa2(mode, seq, reqid) * set data into sadb_x_policy */ static struct mbuf * -key_setsadbxpolicy(type, dir, id) - u_int16_t type; - u_int8_t dir; - u_int32_t id; +key_setsadbxpolicy( + u_int16_t type, + u_int8_t dir, + u_int32_t id) { struct mbuf *m; struct sadb_x_policy *p; @@ -4221,9 +4750,9 @@ key_setsadbxpolicy(type, dir, id) * copy a buffer into the new buffer allocated. */ static void * -key_newbuf(src, len) - const void *src; - u_int len; +key_newbuf( + const void *src, + u_int len) { caddr_t new; @@ -4248,8 +4777,8 @@ key_newbuf(src, len) * 0: false */ int -key_ismyaddr(sa) - struct sockaddr *sa; +key_ismyaddr( + struct sockaddr *sa) { #if INET struct sockaddr_in *sin; @@ -4263,25 +4792,27 @@ key_ismyaddr(sa) switch (sa->sa_family) { #if INET case AF_INET: - lck_mtx_lock(rt_mtx); - sin = (struct sockaddr_in *)sa; + lck_rw_lock_shared(in_ifaddr_rwlock); + sin = (struct sockaddr_in *)(void *)sa; for (ia = in_ifaddrhead.tqh_first; ia; - ia = ia->ia_link.tqe_next) - { + ia = ia->ia_link.tqe_next) { + IFA_LOCK_SPIN(&ia->ia_ifa); if (sin->sin_family == ia->ia_addr.sin_family && sin->sin_len == ia->ia_addr.sin_len && sin->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) { - lck_mtx_unlock(rt_mtx); + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(in_ifaddr_rwlock); return 1; } + IFA_UNLOCK(&ia->ia_ifa); } - lck_mtx_unlock(rt_mtx); + lck_rw_done(in_ifaddr_rwlock); break; #endif #if INET6 case AF_INET6: - return key_ismyaddr6((struct sockaddr_in6 *)sa); + return key_ismyaddr6((struct sockaddr_in6 *)(void *)sa); #endif } @@ -4298,19 +4829,22 @@ key_ismyaddr(sa) #include static int -key_ismyaddr6(sin6) - struct sockaddr_in6 *sin6; +key_ismyaddr6( + struct sockaddr_in6 *sin6) { struct in6_ifaddr *ia; struct in6_multi *in6m; - lck_mtx_lock(nd6_mutex); + lck_rw_lock_shared(&in6_ifaddr_rwlock); for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + IFA_LOCK(&ia->ia_ifa); if (key_sockaddrcmp((struct sockaddr *)&sin6, (struct sockaddr *)&ia->ia_addr, 0) == 0) { - lck_mtx_unlock(nd6_mutex); + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(&in6_ifaddr_rwlock); return 1; } + IFA_UNLOCK(&ia->ia_ifa); /* * XXX Multicast @@ -4319,13 +4853,16 @@ key_ismyaddr6(sin6) * XXX scope */ in6m = NULL; - IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m); - if (in6m) { - lck_mtx_unlock(nd6_mutex); + in6_multihead_lock_shared(); + IN6_LOOKUP_MULTI(&sin6->sin6_addr, ia->ia_ifp, in6m); + in6_multihead_lock_done(); + if (in6m != NULL) { + lck_rw_done(&in6_ifaddr_rwlock); + IN6M_REMREF(in6m); return 1; } } - lck_mtx_unlock(nd6_mutex); + lck_rw_done(&in6_ifaddr_rwlock); /* loopback, just for safety */ if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) @@ -4348,9 +4885,10 @@ key_ismyaddr6(sin6) * 0 : not equal */ static int -key_cmpsaidx(saidx0, saidx1, flag) - struct secasindex *saidx0, *saidx1; - int flag; +key_cmpsaidx( + struct secasindex *saidx0, + struct secasindex *saidx1, + int flag) { /* sanity */ if (saidx0 == NULL && saidx1 == NULL) @@ -4411,8 +4949,9 @@ key_cmpsaidx(saidx0, saidx1, flag) * 0 : not equal */ static int -key_cmpspidx_exactly(spidx0, spidx1) - struct secpolicyindex *spidx0, *spidx1; +key_cmpspidx_exactly( + struct secpolicyindex *spidx0, + struct secpolicyindex *spidx1) { /* sanity */ if (spidx0 == NULL && spidx1 == NULL) @@ -4448,8 +4987,9 @@ key_cmpspidx_exactly(spidx0, spidx1) * 0 : not equal */ static int -key_cmpspidx_withmask(spidx0, spidx1) - struct secpolicyindex *spidx0, *spidx1; +key_cmpspidx_withmask( + struct secpolicyindex *spidx0, + struct secpolicyindex *spidx1) { /* sanity */ if (spidx0 == NULL && spidx1 == NULL) @@ -4546,10 +5086,10 @@ key_cmpspidx_withmask(spidx0, spidx1) /* returns 0 on match */ static int -key_sockaddrcmp(sa1, sa2, port) - struct sockaddr *sa1; - struct sockaddr *sa2; - int port; +key_sockaddrcmp( + struct sockaddr *sa1, + struct sockaddr *sa2, + int port) { if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len) return 1; @@ -4601,9 +5141,10 @@ key_sockaddrcmp(sa1, sa2, port) * 0 : not equal */ static int -key_bbcmp(p1, p2, bits) - caddr_t p1, p2; - u_int bits; +key_bbcmp( + caddr_t p1, + caddr_t p2, + u_int bits) { u_int8_t mask; @@ -4731,7 +5272,7 @@ key_timehandler(void) empty_sah_count++; continue; } - + /* if LARVAL entry doesn't become MATURE, delete it. */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_LARVAL]); sav != NULL; @@ -4758,9 +5299,8 @@ key_timehandler(void) */ if (savkabuf && savkacount < savbufcount) { sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_MATURE]); //%%% should we check dying list if this is empty??? - if (natt_keepalive_interval && sav && (sav->flags & SADB_X_EXT_NATT_KEEPALIVE) != 0 && - (natt_now - sav->natt_last_activity) >= natt_keepalive_interval) { - //ipsec_send_natt_keepalive(sav); + if (natt_keepalive_interval && sav && + (sav->flags & (SADB_X_EXT_NATT_KEEPALIVE | SADB_X_EXT_ESP_KEEPALIVE)) != 0) { sav->refcnt++; *savkaptr++ = sav; savkacount++; @@ -4832,7 +5372,7 @@ key_timehandler(void) savexcount++; } } - + /* check DYING entry to change status to DEAD. */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DYING]); sav != NULL; @@ -4997,9 +5537,20 @@ key_timehandler(void) key_spdexpire(*(--spptr)); } if (savkabuf && savkacount > 0) { - cnt = savkacount; - while (cnt--) - ipsec_send_natt_keepalive(*(--savkaptr)); + struct secasvar **savkaptr_sav = savkaptr; + int cnt_send = savkacount; + + while (cnt_send--) { + if (ipsec_send_natt_keepalive(*(--savkaptr))) { + // iterate (all over again) and update timestamps + struct secasvar **savkaptr_update = savkaptr_sav; + int cnt_update = savkacount; + while (cnt_update--) { + key_update_natt_keepalive_timestamp(*savkaptr, + *(--savkaptr_update)); + } + } + } } if (savexbuf && savexcount > 0) { cnt = savexcount; @@ -5039,7 +5590,7 @@ key_timehandler(void) * to initialize a seed for random() */ static void -key_srandom() +key_srandom(void) { #ifdef __APPLE__ /* Our PRNG is based on Yarrow and doesn't need to be seeded */ @@ -5055,26 +5606,26 @@ key_srandom() return; } -u_long -key_random() +u_int32_t +key_random(void) { - u_long value; + u_int32_t value; key_randomfill(&value, sizeof(value)); return value; } void -key_randomfill(p, l) - void *p; - size_t l; +key_randomfill( + void *p, + size_t l) { #ifdef __APPLE__ read_random(p, (u_int)l); #else size_t n; - u_long v; + u_int32_t v; static int warn = 1; n = 0; @@ -5102,8 +5653,8 @@ key_randomfill(p, l) * 0: invalid satype. */ static u_int16_t -key_satype2proto(satype) - u_int8_t satype; +key_satype2proto( + u_int8_t satype) { switch (satype) { case SADB_SATYPE_UNSPEC: @@ -5127,8 +5678,8 @@ key_satype2proto(satype) * 0: invalid protocol type. */ static u_int8_t -key_proto2satype(proto) - u_int16_t proto; +key_proto2satype( + u_int16_t proto) { switch (proto) { case IPPROTO_AH: @@ -5158,10 +5709,10 @@ key_proto2satype(proto) * other if success, return pointer to the message to send. */ static int -key_getspi(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_getspi( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_address *src0, *dst0; struct secasindex saidx; @@ -5190,8 +5741,10 @@ key_getspi(so, m, mhp) return key_senderror(so, m, EINVAL); } if (mhp->ext[SADB_X_EXT_SA2] != NULL) { - mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; - reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + mode = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; } else { mode = IPSEC_MODE_ANY; reqid = 0; @@ -5212,13 +5765,13 @@ key_getspi(so, m, mhp) if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); - ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; + ((struct sockaddr_in *)(void *)(src0 + 1))->sin_port = 0; break; case AF_INET6: if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); - ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; + ((struct sockaddr_in6 *)(void *)(src0 + 1))->sin6_port = 0; break; default: ; /*???*/ @@ -5228,13 +5781,13 @@ key_getspi(so, m, mhp) if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); - ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; + ((struct sockaddr_in *)(void *)(dst0 + 1))->sin_port = 0; break; case AF_INET6: if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); - ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; + ((struct sockaddr_in6 *)(void *)(dst0 + 1))->sin6_port = 0; break; default: ; /*???*/ @@ -5244,10 +5797,10 @@ key_getspi(so, m, mhp) KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); lck_mtx_lock(sadb_mutex); - + /* SPI allocation */ - spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], - &saidx); + spi = key_do_getnewspi((struct sadb_spirange *) + (void *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); if (spi == 0) { lck_mtx_unlock(sadb_mutex); return key_senderror(so, m, EINVAL); @@ -5255,8 +5808,8 @@ key_getspi(so, m, mhp) /* get a SA index */ if ((newsah = key_getsah(&saidx)) == NULL) { - /* create a new SA index */ - if ((newsah = key_newsah(&saidx)) == NULL) { + /* create a new SA index: key_addspi is always used for inbound spi */ + if ((newsah = key_newsah(&saidx, IPSEC_DIR_INBOUND)) == NULL) { lck_mtx_unlock(sadb_mutex); ipseclog((LOG_DEBUG, "key_getspi: No more memory.\n")); return key_senderror(so, m, ENOBUFS); @@ -5303,9 +5856,9 @@ key_getspi(so, m, mhp) if (len > MCLBYTES) return key_senderror(so, m, ENOBUFS); - MGETHDR(n, M_DONTWAIT, MT_DATA); - if (len > MHLEN) { - MCLGET(n, M_DONTWAIT); + MGETHDR(n, M_WAITOK, MT_DATA); + if (n && len > MHLEN) { + MCLGET(n, M_WAITOK); if ((n->m_flags & M_EXT) == 0) { m_freem(n); n = NULL; @@ -5321,7 +5874,7 @@ key_getspi(so, m, mhp) m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); - m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off); + m_sa = (struct sadb_sa *)(void *)(mtod(n, caddr_t) + off); m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa)); m_sa->sadb_sa_exttype = SADB_EXT_SA; m_sa->sadb_sa_spi = htonl(spi); @@ -5360,17 +5913,73 @@ key_getspi(so, m, mhp) } } +u_int32_t +key_getspi2(struct sockaddr *src, + struct sockaddr *dst, + u_int8_t proto, + u_int8_t mode, + u_int32_t reqid, + struct sadb_spirange *spirange) +{ + u_int32_t spi; + struct secasindex saidx; + + lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED); + + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, mode, reqid, src, dst, &saidx); + + /* make sure if port number is zero. */ + switch (((struct sockaddr *)&saidx.src)->sa_family) { + case AF_INET: + if (((struct sockaddr *)&saidx.src)->sa_len != sizeof(struct sockaddr_in)) + return 0; + ((struct sockaddr_in *)&saidx.src)->sin_port = 0; + break; + case AF_INET6: + if (((struct sockaddr *)&saidx.src)->sa_len != sizeof(struct sockaddr_in6)) + return 0; + ((struct sockaddr_in6 *)&saidx.src)->sin6_port = 0; + break; + default: + ; /*???*/ + } + switch (((struct sockaddr *)&saidx.dst)->sa_family) { + case AF_INET: + if (((struct sockaddr *)&saidx.dst)->sa_len != sizeof(struct sockaddr_in)) + return 0; + ((struct sockaddr_in *)&saidx.dst)->sin_port = 0; + break; + case AF_INET6: + if (((struct sockaddr *)&saidx.dst)->sa_len != sizeof(struct sockaddr_in6)) + return 0; + ((struct sockaddr_in6 *)&saidx.dst)->sin6_port = 0; + break; + default: + ; /*???*/ + } + + lck_mtx_lock(sadb_mutex); + + /* SPI allocation */ + spi = key_do_getnewspi(spirange, &saidx); + + lck_mtx_unlock(sadb_mutex); + + return spi; +} + /* * allocating new SPI - * called by key_getspi(). + * called by key_getspi() and key_getspi2(). * OUT: * 0: failure. * others: success. */ static u_int32_t -key_do_getnewspi(spirange, saidx) - struct sadb_spirange *spirange; - struct secasindex *saidx; +key_do_getnewspi( + struct sadb_spirange *spirange, + struct secasindex *saidx) { u_int32_t newspi; u_int32_t keymin, keymax; @@ -5409,14 +6018,14 @@ key_do_getnewspi(spirange, saidx) } else { - u_long range = keymax - keymin + 1; /* overflow value of zero means full range */ + u_int32_t range = keymax - keymin + 1; /* overflow value of zero means full range */ /* init SPI */ newspi = 0; /* when requesting to allocate spi ranged */ while (count--) { - u_long rand_val = key_random(); + u_int32_t rand_val = key_random(); /* generate pseudo-random SPI value ranged. */ newspi = (range == 0 ? rand_val : keymin + (rand_val % range)); @@ -5452,10 +6061,10 @@ key_do_getnewspi(spirange, saidx) * m will always be freed. */ static int -key_update(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_update( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_sa *sa0; struct sadb_address *src0, *dst0; @@ -5500,15 +6109,17 @@ key_update(so, m, mhp) return key_senderror(so, m, EINVAL); } if (mhp->ext[SADB_X_EXT_SA2] != NULL) { - mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; - reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + mode = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; } else { mode = IPSEC_MODE_ANY; reqid = 0; } /* XXX boundary checking for other extensions */ - sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); @@ -5516,7 +6127,7 @@ key_update(so, m, mhp) KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); lck_mtx_lock(sadb_mutex); - + /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { lck_mtx_unlock(sadb_mutex); @@ -5628,9 +6239,9 @@ key_update(so, m, mhp) */ #if IPSEC_DOSEQCHECK static struct secasvar * -key_getsavbyseq(sah, seq) - struct secashead *sah; - u_int32_t seq; +key_getsavbyseq( + struct secashead *sah, + u_int32_t seq) { struct secasvar *sav; u_int state; @@ -5674,10 +6285,10 @@ key_getsavbyseq(sah, seq) * m will always be freed. */ static int -key_add(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_add( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_sa *sa0; struct sadb_address *src0, *dst0; @@ -5723,14 +6334,16 @@ key_add(so, m, mhp) return key_senderror(so, m, EINVAL); } if (mhp->ext[SADB_X_EXT_SA2] != NULL) { - mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; - reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + mode = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *) + (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; } else { mode = IPSEC_MODE_ANY; reqid = 0; } - sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; @@ -5738,11 +6351,11 @@ key_add(so, m, mhp) KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); lck_mtx_lock(sadb_mutex); - + /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { - /* create a new SA header */ - if ((newsah = key_newsah(&saidx)) == NULL) { + /* create a new SA header: key_addspi is always used for outbound spi */ + if ((newsah = key_newsah(&saidx, IPSEC_DIR_OUTBOUND)) == NULL) { lck_mtx_unlock(sadb_mutex); ipseclog((LOG_DEBUG, "key_add: No more memory.\n")); return key_senderror(so, m, ENOBUFS); @@ -5810,10 +6423,10 @@ key_add(so, m, mhp) /* m is retained */ static int -key_setident(sah, m, mhp) - struct secashead *sah; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_setident( + struct secashead *sah, + struct mbuf *m, + const struct sadb_msghdr *mhp) { const struct sadb_ident *idsrc, *iddst; int idsrclen, iddstlen; @@ -5838,8 +6451,10 @@ key_setident(sah, m, mhp) return EINVAL; } - idsrc = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_SRC]; - iddst = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_DST]; + idsrc = (const struct sadb_ident *) + (void *)mhp->ext[SADB_EXT_IDENTITY_SRC]; + iddst = (const struct sadb_ident *) + (void *)mhp->ext[SADB_EXT_IDENTITY_DST]; idsrclen = mhp->extlen[SADB_EXT_IDENTITY_SRC]; iddstlen = mhp->extlen[SADB_EXT_IDENTITY_DST]; @@ -5894,9 +6509,9 @@ key_setident(sah, m, mhp) * it is caller's responsibility to free the result. */ static struct mbuf * -key_getmsgbuf_x1(m, mhp) - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_getmsgbuf_x1( + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct mbuf *n; int mbufItems[] = {SADB_EXT_RESERVED, SADB_EXT_SA, @@ -5941,10 +6556,10 @@ static int key_delete_all(struct socket *, struct mbuf *, * m will always be freed. */ static int -key_delete(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_delete( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_sa *sa0; struct sadb_address *src0, *dst0; @@ -5994,7 +6609,7 @@ key_delete(so, m, mhp) return key_senderror(so, m, EINVAL); } - sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); @@ -6054,11 +6669,11 @@ key_delete(so, m, mhp) * delete all SAs for src/dst. Called from key_delete(). */ static int -key_delete_all(so, m, mhp, proto) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; - u_int16_t proto; +key_delete_all( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp, + u_int16_t proto) { struct sadb_address *src0, *dst0; struct secasindex saidx; @@ -6144,10 +6759,10 @@ key_delete_all(so, m, mhp, proto) * m will always be freed. */ static int -key_get(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_get( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_sa *sa0; struct sadb_address *src0, *dst0; @@ -6181,7 +6796,7 @@ key_get(so, m, mhp) return key_senderror(so, m, EINVAL); } - sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA]; src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; @@ -6234,10 +6849,86 @@ key_get(so, m, mhp) } } +/* + * get SA stats by spi. + * OUT: -1 : not found + * 0 : found, arg pointer to a SA stats is updated. + */ +static int +key_getsastatbyspi_one (u_int32_t spi, + struct sastat *stat) +{ + struct secashead *sah; + struct secasvar *sav = NULL; + + if ((void *)stat == NULL) { + return -1; + } + + lck_mtx_lock(sadb_mutex); + + /* get a SA header */ + LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + + /* get a SA with SPI. */ + sav = key_getsavbyspi(sah, spi); + if (sav) { + stat->spi = sav->spi; + stat->created = sav->created; + if (sav->lft_c) { + bcopy(sav->lft_c,&stat->lft_c, sizeof(stat->lft_c)); + } else { + bzero(&stat->lft_c, sizeof(stat->lft_c)); + } + lck_mtx_unlock(sadb_mutex); + return 0; + } + } + + lck_mtx_unlock(sadb_mutex); + + return -1; +} + +/* + * get SA stats collection by indices. + * OUT: -1 : not found + * 0 : found, arg pointers to a SA stats and 'maximum stats' are updated. + */ +static int +key_getsastatbyspi (struct sastat *stat_arg, + u_int32_t max_stat_arg, + struct sastat *stat_res, + u_int32_t *max_stat_res) +{ + int cur, found = 0; + + if (stat_arg == NULL || + stat_res == NULL || + max_stat_res == NULL) { + return -1; + } + + for (cur = 0; cur < max_stat_arg; cur++) { + if (key_getsastatbyspi_one(stat_arg[cur].spi, + &stat_res[found]) == 0) { + found++; + } + } + *max_stat_res = found; + + if (found) { + return 0; + } + return -1; +} + /* XXX make it sysctl-configurable? */ static void -key_getcomb_setlifetime(comb) - struct sadb_comb *comb; +key_getcomb_setlifetime( + struct sadb_comb *comb) { comb->sadb_comb_soft_allocations = 1; @@ -6256,7 +6947,7 @@ key_getcomb_setlifetime(comb) * XXX no idea if the user wants ESP authentication or not */ static struct mbuf * -key_getcomb_esp() +key_getcomb_esp(void) { struct sadb_comb *comb; const struct esp_algorithm *algo; @@ -6286,7 +6977,7 @@ key_getcomb_esp() if (l > MLEN) panic("assumption failed in key_getcomb_esp"); #endif - MGET(m, M_DONTWAIT, MT_DATA); + MGET(m, M_WAITOK, MT_DATA); if (m) { M_ALIGN(m, l); m->m_len = l; @@ -6311,7 +7002,8 @@ key_getcomb_esp() /* m is already freed */ goto fail; } - comb = (struct sadb_comb *)(mtod(n, caddr_t) + o); + comb = (struct sadb_comb *) + (void *)(mtod(n, caddr_t) + o); bzero(comb, sizeof(*comb)); key_getcomb_setlifetime(comb); comb->sadb_comb_encrypt = i; @@ -6338,7 +7030,7 @@ key_getcomb_esp() * XXX reorder combinations by preference */ static struct mbuf * -key_getcomb_ah() +key_getcomb_ah(void) { struct sadb_comb *comb; const struct ah_algorithm *algo; @@ -6370,14 +7062,14 @@ key_getcomb_ah() if (l > MLEN) panic("assumption failed in key_getcomb_ah"); #endif - MGET(m, M_DONTWAIT, MT_DATA); + MGET(m, M_WAITOK, MT_DATA); if (m) { M_ALIGN(m, l); m->m_len = l; m->m_next = NULL; } } else - M_PREPEND(m, l, M_DONTWAIT); + M_PREPEND(m, l, M_WAITOK); if (!m) return NULL; @@ -6397,7 +7089,7 @@ key_getcomb_ah() * XXX reorder combinations by preference */ static struct mbuf * -key_getcomb_ipcomp() +key_getcomb_ipcomp(void) { struct sadb_comb *comb; const struct ipcomp_algorithm *algo; @@ -6416,14 +7108,14 @@ key_getcomb_ipcomp() if (l > MLEN) panic("assumption failed in key_getcomb_ipcomp"); #endif - MGET(m, M_DONTWAIT, MT_DATA); + MGET(m, M_WAITOK, MT_DATA); if (m) { M_ALIGN(m, l); m->m_len = l; m->m_next = NULL; } } else - M_PREPEND(m, l, M_DONTWAIT); + M_PREPEND(m, l, M_WAITOK); if (!m) return NULL; @@ -6443,8 +7135,8 @@ key_getcomb_ipcomp() * XXX sysctl interface to ipsec_{ah,esp}_keymin */ static struct mbuf * -key_getprop(saidx) - const struct secasindex *saidx; +key_getprop( + const struct secasindex *saidx) { struct sadb_prop *prop; struct mbuf *m, *n; @@ -6469,7 +7161,7 @@ key_getprop(saidx) if (!m) return NULL; - M_PREPEND(m, l, M_DONTWAIT); + M_PREPEND(m, l, M_WAITOK); if (!m) return NULL; @@ -6507,9 +7199,9 @@ key_getprop(saidx) * others: error number */ static int -key_acquire(saidx, sp) - struct secasindex *saidx; - struct secpolicy *sp; +key_acquire( + struct secasindex *saidx, + struct secpolicy *sp) { struct mbuf *result = NULL, *m; #ifndef IPSEC_NONBLOCK_ACQUIRE @@ -6597,7 +7289,7 @@ key_acquire(saidx, sp) } m_cat(result, m); } - + /* XXX identity (optional) */ #if 0 if (idexttype && fqdn) { @@ -6692,8 +7384,8 @@ key_acquire(saidx, sp) #ifndef IPSEC_NONBLOCK_ACQUIRE static struct secacq * -key_newacq(saidx) - struct secasindex *saidx; +key_newacq( + struct secasindex *saidx) { struct secacq *newacq; struct timeval tv; @@ -6722,8 +7414,8 @@ key_newacq(saidx) } static struct secacq * -key_getacq(saidx) - struct secasindex *saidx; +key_getacq( + struct secasindex *saidx) { struct secacq *acq; @@ -6738,8 +7430,8 @@ key_getacq(saidx) } static struct secacq * -key_getacqbyseq(seq) - u_int32_t seq; +key_getacqbyseq( + u_int32_t seq) { struct secacq *acq; @@ -6755,8 +7447,8 @@ key_getacqbyseq(seq) #endif static struct secspacq * -key_newspacq(spidx) - struct secpolicyindex *spidx; +key_newspacq( + struct secpolicyindex *spidx) { struct secspacq *acq; struct timeval tv; @@ -6784,8 +7476,8 @@ key_newspacq(spidx) } static struct secspacq * -key_getspacq(spidx) - struct secpolicyindex *spidx; +key_getspacq( + struct secpolicyindex *spidx) { struct secspacq *acq; @@ -6814,10 +7506,10 @@ key_getspacq(spidx) * m will always be freed. */ static int -key_acquire2(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_acquire2( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { const struct sadb_address *src0, *dst0; struct secasindex saidx; @@ -6899,10 +7591,11 @@ key_acquire2(so, m, mhp) return key_senderror(so, m, EINVAL); } - src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; - dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + src0 = (const struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (const struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; /* XXX boundary check against sa_len */ + /* cast warnings */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); /* get a SA index */ @@ -6942,10 +7635,10 @@ key_acquire2(so, m, mhp) * m will always be freed. */ static int -key_register(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_register( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct secreg *reg, *newreg = 0; @@ -7021,9 +7714,9 @@ key_register(so, m, mhp) if (len > MCLBYTES) return key_senderror(so, m, ENOBUFS); - MGETHDR(n, M_DONTWAIT, MT_DATA); - if (len > MHLEN) { - MCLGET(n, M_DONTWAIT); + MGETHDR(n, M_WAITOK, MT_DATA); + if (n && len > MHLEN) { + MCLGET(n, M_WAITOK); if ((n->m_flags & M_EXT) == 0) { m_freem(n); n = NULL; @@ -7044,7 +7737,7 @@ key_register(so, m, mhp) /* for authentication algorithm */ if (alen) { - sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); + sup = (struct sadb_supported *)(void *)(mtod(n, caddr_t) + off); sup->sadb_supported_len = PFKEY_UNIT64(alen); sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; off += PFKEY_ALIGN8(sizeof(*sup)); @@ -7055,7 +7748,8 @@ key_register(so, m, mhp) aalgo = ah_algorithm_lookup(i); if (!aalgo) continue; - alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); + alg = (struct sadb_alg *) + (void *)(mtod(n, caddr_t) + off); alg->sadb_alg_id = i; alg->sadb_alg_ivlen = 0; alg->sadb_alg_minbits = aalgo->keymin; @@ -7067,7 +7761,7 @@ key_register(so, m, mhp) #if IPSEC_ESP /* for encryption algorithm */ if (elen) { - sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); + sup = (struct sadb_supported *)(void *)(mtod(n, caddr_t) + off); sup->sadb_supported_len = PFKEY_UNIT64(elen); sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; off += PFKEY_ALIGN8(sizeof(*sup)); @@ -7078,7 +7772,8 @@ key_register(so, m, mhp) ealgo = esp_algorithm_lookup(i); if (!ealgo) continue; - alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); + alg = (struct sadb_alg *) + (void *)(mtod(n, caddr_t) + off); alg->sadb_alg_id = i; if (ealgo && ealgo->ivlen) { /* @@ -7111,8 +7806,8 @@ key_register(so, m, mhp) * XXX: I want to do free a socket marked done SADB_RESIGER to socket. */ void -key_freereg(so) - struct socket *so; +key_freereg( + struct socket *so) { struct secreg *reg; int i; @@ -7152,8 +7847,8 @@ key_freereg(so) * others : error number */ static int -key_expire(sav) - struct secasvar *sav; +key_expire( + struct secasvar *sav) { int satype; struct mbuf *result = NULL, *m; @@ -7214,7 +7909,7 @@ key_expire(sav) lt->sadb_lifetime_bytes = sav->lft_c->sadb_lifetime_bytes; lt->sadb_lifetime_addtime = sav->lft_c->sadb_lifetime_addtime; lt->sadb_lifetime_usetime = sav->lft_c->sadb_lifetime_usetime; - lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); + lt = (struct sadb_lifetime *)(void *)(mtod(m, caddr_t) + len / 2); bcopy(sav->lft_s, lt, sizeof(*lt)); m_cat(result, m); @@ -7279,10 +7974,10 @@ key_expire(sav) * m will always be freed. */ static int -key_flush(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_flush( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct sadb_msg *newmsg; struct secashead *sah, *nextsah; @@ -7368,10 +8063,10 @@ struct sav_dump_elem { }; static int -key_dump(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_dump( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { struct secashead *sah; struct secasvar *sav; @@ -7487,10 +8182,10 @@ end: * m will always be freed. */ static int -key_promisc(so, m, mhp) - struct socket *so; - struct mbuf *m; - const struct sadb_msghdr *mhp; +key_promisc( + struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) { int olen; @@ -7564,6 +8259,7 @@ static int (*key_typesw[])(struct socket *, struct mbuf *, key_spdadd, /* SADB_X_SPDSETIDX */ NULL, /* SADB_X_SPDEXPIRE */ key_spddelete2, /* SADB_X_SPDDELETE2 */ + key_getsastat, /* SADB_GETSASTAT */ }; /* @@ -7578,9 +8274,9 @@ static int (*key_typesw[])(struct socket *, struct mbuf *, * length for buffer to send to user process. */ int -key_parse(m, so) - struct mbuf *m; - struct socket *so; +key_parse( + struct mbuf *m, + struct socket *so) { struct sadb_msg *msg; struct sadb_msghdr mh; @@ -7642,9 +8338,9 @@ key_parse(m, so) if (m->m_next) { struct mbuf *n; - MGETHDR(n, M_DONTWAIT, MT_DATA); + MGETHDR(n, M_WAITOK, MT_DATA); if (n && m->m_pkthdr.len > MHLEN) { - MCLGET(n, M_DONTWAIT); + MCLGET(n, M_WAITOK); if ((n->m_flags & M_EXT) == 0) { m_free(n); n = NULL; @@ -7833,10 +8529,10 @@ senderror: } static int -key_senderror(so, m, code) - struct socket *so; - struct mbuf *m; - int code; +key_senderror( + struct socket *so, + struct mbuf *m, + int code) { struct sadb_msg *msg; @@ -7856,9 +8552,9 @@ key_senderror(so, m, code) * XXX larger-than-MCLBYTES extension? */ static int -key_align(m, mhp) - struct mbuf *m; - struct sadb_msghdr *mhp; +key_align( + struct mbuf *m, + struct sadb_msghdr *mhp) { struct mbuf *n; struct sadb_ext *ext; @@ -7886,7 +8582,7 @@ key_align(m, mhp) /* m is already freed */ return ENOBUFS; } - ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); + ext = (struct sadb_ext *)(void *)(mtod(n, caddr_t) + toff); /* set pointer */ switch (ext->sadb_ext_type) { @@ -7908,6 +8604,8 @@ key_align(m, mhp) case SADB_EXT_SPIRANGE: case SADB_X_EXT_POLICY: case SADB_X_EXT_SA2: + case SADB_EXT_SESSION_ID: + case SADB_EXT_SASTAT: /* duplicate check */ /* * XXX Are there duplication payloads of either @@ -7944,7 +8642,7 @@ key_align(m, mhp) /* m is already freed */ return ENOBUFS; } - ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); + ext = (struct sadb_ext *)(void *)(mtod(n, caddr_t) + toff); mhp->ext[ext->sadb_ext_type] = ext; mhp->extoff[ext->sadb_ext_type] = off; @@ -7961,9 +8659,9 @@ key_align(m, mhp) } static int -key_validate_ext(ext, len) - const struct sadb_ext *ext; - int len; +key_validate_ext( + const struct sadb_ext *ext, + int len) { struct sockaddr *sa; enum { NONE, ADDR } checktype = NONE; @@ -7992,8 +8690,8 @@ key_validate_ext(ext, len) break; case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: - if (((struct sadb_ident *)ext)->sadb_ident_type == - SADB_X_IDENTTYPE_ADDR) { + if (((struct sadb_ident *)(uintptr_t)(size_t)ext)-> + sadb_ident_type == SADB_X_IDENTTYPE_ADDR) { baselen = PFKEY_ALIGN8(sizeof(struct sadb_ident)); checktype = ADDR; } else @@ -8008,7 +8706,8 @@ key_validate_ext(ext, len) case NONE: break; case ADDR: - sa = (struct sockaddr *)((caddr_t)ext + baselen); + sa = (struct sockaddr *)((caddr_t)(uintptr_t)ext + baselen); + if (len < baselen + sal) return EINVAL; if (baselen + PFKEY_ALIGN8(sa->sa_len) != len) @@ -8020,50 +8719,8 @@ key_validate_ext(ext, len) } void -key_domain_init() +key_domain_init(void) { - int i; - - bzero((caddr_t)&key_cb, sizeof(key_cb)); - - for (i = 0; i < IPSEC_DIR_MAX; i++) { - LIST_INIT(&sptree[i]); - } - ipsec_policy_count = 0; - - LIST_INIT(&sahtree); - - for (i = 0; i <= SADB_SATYPE_MAX; i++) { - LIST_INIT(®tree[i]); - } - ipsec_sav_count = 0; - -#ifndef IPSEC_NONBLOCK_ACQUIRE - LIST_INIT(&acqtree); -#endif - LIST_INIT(&spacqtree); - - /* system default */ -#if INET - ip4_def_policy.policy = IPSEC_POLICY_NONE; - ip4_def_policy.refcnt++; /*never reclaim this*/ -#endif -#if INET6 - ip6_def_policy.policy = IPSEC_POLICY_NONE; - ip6_def_policy.refcnt++; /*never reclaim this*/ -#endif - -#ifndef IPSEC_DEBUG2 - timeout((void *)key_timehandler, (void *)0, hz); -#endif /*IPSEC_DEBUG2*/ - - /* initialize key statistics */ - keystat.getspi_count = 1; - -#ifndef __APPLE__ - printf("IPsec: Initialized Security Association Processing.\n"); -#endif - return; } @@ -8094,9 +8751,9 @@ key_checktunnelsanity( /* record data transfer on SA, and update timestamps */ void -key_sa_recordxfer(sav, m) - struct secasvar *sav; - struct mbuf *m; +key_sa_recordxfer( + struct secasvar *sav, + struct mbuf *m) { @@ -8147,8 +8804,8 @@ key_sa_recordxfer(sav, m) /* dumb version */ void -key_sa_routechange(dst) - struct sockaddr *dst; +key_sa_routechange( + struct sockaddr *dst) { struct secashead *sah; struct route *ro; @@ -8167,10 +8824,10 @@ key_sa_routechange(dst) return; } -static void -key_sa_chgstate(sav, state) - struct secasvar *sav; - u_int8_t state; +void +key_sa_chgstate( + struct secasvar *sav, + u_int8_t state) { if (sav == NULL) @@ -8190,8 +8847,8 @@ key_sa_chgstate(sav, state) } void -key_sa_stir_iv(sav) - struct secasvar *sav; +key_sa_stir_iv( + struct secasvar *sav) { lck_mtx_lock(sadb_mutex); if (!sav->iv) @@ -8202,8 +8859,8 @@ key_sa_stir_iv(sav) /* XXX too much? */ static struct mbuf * -key_alloc_mbuf(l) - int l; +key_alloc_mbuf( + int l) { struct mbuf *m = NULL, *n; int len, t; @@ -8239,654 +8896,199 @@ key_alloc_mbuf(l) return m; } +static struct mbuf * +key_setdumpsastats (u_int32_t dir, + struct sastat *stats, + u_int32_t max_stats, + u_int64_t session_ids[], + u_int32_t seq, + u_int32_t pid) +{ + struct mbuf *result = NULL, *m = NULL; -/* ---------------------------------------------------------------------------------- -Application of kernel control for interface creation + m = key_setsadbmsg(SADB_GETSASTAT, 0, 0, seq, pid, 0); + if (!m) { + goto fail; + } + result = m; -Theory of operation: -ipsecif acts as glue between kernel control sockets and ipsec network interfaces. This -kernel control will register an interface for every client that connects. -ipsec interface do not send or receive packets, an they are intercepted by ipsec before -they reach the interface. ipsec needs interface to attach tunnel ip addresses. -In the future, we may want to change the control mechanism to use PF_KEY to create -interfaces for ipsec ----------------------------------------------------------------------------------- */ + m = key_setsadbsession_id(session_ids); + if (!m) { + goto fail; + } + m_cat(result, m); -#include -//#include "if_ip.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include /* Until leopard, our ugly bpf protocol prepend will need this */ -#include -#include -#include + m = key_setsadbsastat(dir, + stats, + max_stats); + if (!m) { + goto fail; + } + m_cat(result, m); -/* -*/ - -#define IPSECIF_CONTROL_NAME "com.apple.net.ipsecif_control" - -/* Kernel Control functions */ -static errno_t ipsecif_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, - void **unitinfo); -static errno_t ipsecif_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t unit, - void *unitinfo); -static errno_t ipsecif_ctl_send(kern_ctl_ref kctlref, u_int32_t unit, - void *unitinfo, mbuf_t m, int flags); - -/* Network Interface functions */ -static errno_t ipsecif_output(ifnet_t interface, mbuf_t data); -static errno_t ipsecif_demux(ifnet_t interface, mbuf_t data, char *frame_header, - protocol_family_t *protocol); -static errno_t ipsecif_add_proto(ifnet_t interface, protocol_family_t protocol, - const struct ifnet_demux_desc *demux_array, - u_int32_t demux_count); -static errno_t ipsecif_del_proto(ifnet_t interface, protocol_family_t protocol); -static errno_t ipsecif_ioctl(ifnet_t interface, u_int32_t cmd, void *data); -static errno_t ipsecif_settap(ifnet_t interface, bpf_tap_mode mode, - bpf_packet_func callback); -static void ipsecif_detached(ifnet_t interface); - -/* Protocol handlers */ -static errno_t ipsecif_attach_proto(ifnet_t interface, protocol_family_t proto); -static errno_t ipsecif_proto_input(ifnet_t interface, protocol_family_t protocol, - mbuf_t m, char *frame_header); - -/* Control block allocated for each kernel control connection */ -struct ipsecif_pcb { - kern_ctl_ref ctlref; - u_int32_t unit; - ifnet_t ifp; - bpf_tap_mode mode; - bpf_packet_func tap; -}; + if ((result->m_flags & M_PKTHDR) == 0) { + goto fail; + } -static kern_ctl_ref ipsecif_kctlref; -static u_int32_t ipsecif_family; -static OSMallocTag ipsecif_malloc_tag; -static SInt32 ipsecif_ifcount = 0; + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) { + goto fail; + } + } -/* Prepend length */ -static void* -ipsecif_alloc(size_t size) -{ - size_t *mem = OSMalloc(size + sizeof(size_t), ipsecif_malloc_tag); - - if (mem) { - *mem = size + sizeof(size_t); - mem++; + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) { + result->m_pkthdr.len += m->m_len; } - - return (void*)mem; -} -static void -ipsecif_free(void *ptr) -{ - size_t *size = ptr; - size--; - OSFree(size, *size, ipsecif_malloc_tag); -} - -static errno_t -ipsecif_register_control(void) -{ - struct kern_ctl_reg kern_ctl; - errno_t result = 0; - - /* Create a tag to allocate memory */ - ipsecif_malloc_tag = OSMalloc_Tagalloc(IPSECIF_CONTROL_NAME, OSMT_DEFAULT); - - /* Find a unique value for our interface family */ - result = mbuf_tag_id_find(IPSECIF_CONTROL_NAME, &ipsecif_family); - if (result != 0) { - printf("ipsecif_register_control - mbuf_tag_id_find_internal failed: %d\n", result); - return result; - } - - bzero(&kern_ctl, sizeof(kern_ctl)); - strncpy(kern_ctl.ctl_name, IPSECIF_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_connect = ipsecif_ctl_connect; - kern_ctl.ctl_disconnect = ipsecif_ctl_disconnect; - kern_ctl.ctl_send = ipsecif_ctl_send; - - result = ctl_register(&kern_ctl, &ipsecif_kctlref); - if (result != 0) { - printf("ipsecif_register_control - ctl_register failed: %d\n", result); - return result; - } - - /* Register the protocol plumbers */ - if ((result = proto_register_plumber(PF_INET, ipsecif_family, - ipsecif_attach_proto, NULL)) != 0) { - printf("ipsecif_register_control - proto_register_plumber(PF_INET, %d) failed: %d\n", - ipsecif_family, result); - ctl_deregister(ipsecif_kctlref); - return result; - } - - /* Register the protocol plumbers */ - if ((result = proto_register_plumber(PF_INET6, ipsecif_family, - ipsecif_attach_proto, NULL)) != 0) { - proto_unregister_plumber(PF_INET, ipsecif_family); - ctl_deregister(ipsecif_kctlref); - printf("ipsecif_register_control - proto_register_plumber(PF_INET6, %d) failed: %d\n", - ipsecif_family, result); - return result; - } - - return 0; -} + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); -/* Kernel control functions */ + return result; -static errno_t -ipsecif_ctl_connect( - kern_ctl_ref kctlref, - struct sockaddr_ctl *sac, - void **unitinfo) -{ - struct ifnet_init_params ipsecif_init; - struct ipsecif_pcb *pcb; - errno_t result; - - /* kernel control allocates, interface frees */ - pcb = ipsecif_alloc(sizeof(*pcb)); - if (pcb == NULL) - return ENOMEM; - - /* Setup the protocol control block */ - bzero(pcb, sizeof(*pcb)); - *unitinfo = pcb; - pcb->ctlref = kctlref; - pcb->unit = sac->sc_unit; - printf("ipsecif_ctl_connect: creating unit ip%d\n", pcb->unit); - - /* Create the interface */ - bzero(&ipsecif_init, sizeof(ipsecif_init)); - ipsecif_init.name = "ipsec"; - ipsecif_init.unit = pcb->unit; - ipsecif_init.family = ipsecif_family; - ipsecif_init.type = IFT_OTHER; - ipsecif_init.output = ipsecif_output; - ipsecif_init.demux = ipsecif_demux; - ipsecif_init.add_proto = ipsecif_add_proto; - ipsecif_init.del_proto = ipsecif_del_proto; - ipsecif_init.softc = pcb; - ipsecif_init.ioctl = ipsecif_ioctl; - ipsecif_init.set_bpf_tap = ipsecif_settap; - ipsecif_init.detach = ipsecif_detached; - - result = ifnet_allocate(&ipsecif_init, &pcb->ifp); - if (result != 0) { - printf("ipsecif_ctl_connect - ifnet_allocate failed: %d\n", result); - ipsecif_free(pcb); - return result; - } - OSIncrementAtomic(&ipsecif_ifcount); - - /* Set flags and additional information. */ - ifnet_set_mtu(pcb->ifp, 1280); - ifnet_set_flags(pcb->ifp, IFF_UP | IFF_MULTICAST | IFF_BROADCAST, 0xffff); -// ifnet_set_flags(pcb->ifp, IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT, 0xffff); - - /* Attach the interface */ - result = ifnet_attach(pcb->ifp, NULL); - if (result != 0) { - printf("ipsecif_ctl_connect - ifnet_allocate failed: %d\n", result); - ifnet_release(pcb->ifp); - ipsecif_free(pcb); + fail: + if (result) { + m_freem(result); } - - /* Attach to bpf */ - if (result == 0) - bpfattach(pcb->ifp, DLT_NULL, 4); - - return result; + return NULL; } /* - * These defines are marked private but it's impossible to remove an interface - * without them. + * SADB_GETSASTAT processing + * dump all stats for matching entries in SAD. + * + * m will always be freed. */ -#ifndef SIOCPROTODETACH -#define SIOCPROTODETACH _IOWR('i', 81, struct ifreq) /* detach proto from interface */ -#endif /* SIOCPROTODETACH */ - -#ifndef SIOCPROTODETACH_IN6 -#define SIOCPROTODETACH_IN6 _IOWR('i', 111, struct in6_ifreq) /* detach proto from interface */ -#endif /* SIOCPROTODETACH */ + +static int +key_getsastat (struct socket *so, + struct mbuf *m, + const struct sadb_msghdr *mhp) +{ + struct sadb_session_id *session_id; + u_int32_t bufsize, arg_count, res_count; + struct sadb_sastat *sa_stats_arg; + struct sastat *sa_stats_sav = NULL; + struct mbuf *n; + int error = 0; + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("%s: NULL pointer is passed.\n", __FUNCTION__); -static errno_t -ipsecif_detach_ip( - ifnet_t interface, - protocol_family_t protocol, - socket_t pf_socket) -{ - errno_t result = EPROTONOSUPPORT; - - /* Attempt a detach */ - if (protocol == PF_INET) { - struct ifreq ifr; - - bzero(&ifr, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", - ifnet_name(interface), ifnet_unit(interface)); - - result = sock_ioctl(pf_socket, SIOCPROTODETACH, &ifr); - } - else if (protocol == PF_INET6) { - struct in6_ifreq ifr6; - - bzero(&ifr6, sizeof(ifr6)); - snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d", - ifnet_name(interface), ifnet_unit(interface)); - - result = sock_ioctl(pf_socket, SIOCPROTODETACH_IN6, &ifr6); - } - - return result; -} + if (mhp->ext[SADB_EXT_SESSION_ID] == NULL) { + printf("%s: invalid message is passed. missing session-id.\n", __FUNCTION__); + return key_senderror(so, m, EINVAL); + } + if (mhp->extlen[SADB_EXT_SESSION_ID] < sizeof(struct sadb_session_id)) { + printf("%s: invalid message is passed. short session-id.\n", __FUNCTION__); + return key_senderror(so, m, EINVAL); + } + if (mhp->ext[SADB_EXT_SASTAT] == NULL) { + printf("%s: invalid message is passed. missing stat args.\n", __FUNCTION__); + return key_senderror(so, m, EINVAL); + } + if (mhp->extlen[SADB_EXT_SASTAT] < sizeof(*sa_stats_arg)) { + printf("%s: invalid message is passed. short stat args.\n", __FUNCTION__); + return key_senderror(so, m, EINVAL); + } -static void -ipsecif_remove_address( - ifnet_t interface, - protocol_family_t protocol, - ifaddr_t address, - socket_t pf_socket) -{ - errno_t result = 0; - - /* Attempt a detach */ - if (protocol == PF_INET) { - struct ifreq ifr; - - bzero(&ifr, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", - ifnet_name(interface), ifnet_unit(interface)); - result = ifaddr_address(address, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); - if (result != 0) { - printf("ipsecif_remove_address - ifaddr_address failed: %d", result); - } - else { - result = sock_ioctl(pf_socket, SIOCDIFADDR, &ifr); - if (result != 0) { - printf("ipsecif_remove_address - SIOCDIFADDR failed: %d", result); - } - } - } - else if (protocol == PF_INET6) { - struct in6_ifreq ifr6; - - bzero(&ifr6, sizeof(ifr6)); - snprintf(ifr6.ifr_name, sizeof(ifr6.ifr_name), "%s%d", - ifnet_name(interface), ifnet_unit(interface)); - result = ifaddr_address(address, (struct sockaddr*)&ifr6.ifr_addr, - sizeof(ifr6.ifr_addr)); - if (result != 0) { - printf("ipsecif_remove_address - ifaddr_address failed (v6): %d", - result); - } - else { - result = sock_ioctl(pf_socket, SIOCDIFADDR_IN6, &ifr6); - if (result != 0) { - printf("ipsecif_remove_address - SIOCDIFADDR_IN6 failed: %d", - result); - } - } - } -} + lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED); -static void -ipsecif_cleanup_family( - ifnet_t interface, - protocol_family_t protocol) -{ - errno_t result = 0; - socket_t pf_socket = NULL; - ifaddr_t *addresses = NULL; - int i; - - if (protocol != PF_INET && protocol != PF_INET6) { - printf("ipsecif_cleanup_family - invalid protocol family %d\n", protocol); - return; - } - - /* Create a socket for removing addresses and detaching the protocol */ - result = sock_socket(protocol, SOCK_DGRAM, 0, NULL, NULL, &pf_socket); - if (result != 0) { - if (result != EAFNOSUPPORT) - printf("ipsecif_cleanup_family - failed to create %s socket: %d\n", - protocol == PF_INET ? "IP" : "IPv6", result); - goto cleanup; - } - - result = ipsecif_detach_ip(interface, protocol, pf_socket); - if (result == 0 || result == ENXIO) { - /* We are done! We either detached or weren't attached. */ - goto cleanup; - } - else if (result != EBUSY) { - /* Uh, not really sure what happened here... */ - printf("ipsecif_cleanup_family - ipsecif_detach_ip failed: %d\n", result); - goto cleanup; - } - - /* - * At this point, we received an EBUSY error. This means there are - * addresses attached. We should detach them and then try again. - */ - result = ifnet_get_address_list_family(interface, &addresses, protocol); - if (result != 0) { - printf("fnet_get_address_list_family(%s%d, 0xblah, %s) - failed: %d\n", - ifnet_name(interface), ifnet_unit(interface), - protocol == PF_INET ? "PF_INET" : "PF_INET6", result); - goto cleanup; - } - - for (i = 0; addresses[i] != 0; i++) { - ipsecif_remove_address(interface, protocol, addresses[i], pf_socket); - } - ifnet_free_address_list(addresses); - addresses = NULL; - - /* - * The addresses should be gone, we should try the remove again. - */ - result = ipsecif_detach_ip(interface, protocol, pf_socket); - if (result != 0 && result != ENXIO) { - printf("ipsecif_cleanup_family - ipsecif_detach_ip failed: %d\n", result); + // exit early if there are no active SAs + if (ipsec_sav_count <= 0) { + printf("%s: No active SAs.\n", __FUNCTION__); + error = ENOENT; + goto end; } - -cleanup: - if (pf_socket != NULL) - sock_close(pf_socket); - - if (addresses != NULL) - ifnet_free_address_list(addresses); -} + bufsize = (ipsec_sav_count + 1) * sizeof(*sa_stats_sav); -static errno_t -ipsecif_ctl_disconnect( - __unused kern_ctl_ref kctlref, - __unused u_int32_t unit, - void *unitinfo) -{ - struct ipsecif_pcb *pcb = unitinfo; - ifnet_t ifp = pcb->ifp; - errno_t result = 0; - - pcb->ctlref = NULL; - pcb->unit = 0; - - /* - * We want to do everything in our power to ensure that the interface - * really goes away when the socket is closed. We must remove IP/IPv6 - * addresses and detach the protocols. Finally, we can remove and - * release the interface. - */ - ipsecif_cleanup_family(ifp, AF_INET); - ipsecif_cleanup_family(ifp, AF_INET6); - - if ((result = ifnet_detach(ifp)) != 0) { - printf("ipsecif_ctl_disconnect - ifnet_detach failed: %d\n", result); - } - - if ((result = ifnet_release(ifp)) != 0) { - printf("ipsecif_ctl_disconnect - ifnet_release failed: %d\n", result); + KMALLOC_WAIT(sa_stats_sav, __typeof__(sa_stats_sav), bufsize); + if (sa_stats_sav == NULL) { + printf("%s: No more memory.\n", __FUNCTION__); + error = ENOMEM; + goto end; } - - return 0; -} + bzero(sa_stats_sav, bufsize); -static inline void -call_bpf_tap( - ifnet_t ifp, - bpf_packet_func tap, - mbuf_t m) -{ - struct m_hdr hack_hdr; - struct mbuf *n; - int af; - - if (!tap) - return; - - af = (((*(char*)(mbuf_data(m))) & 0xf0) >> 4); // 4 or 6 - if(af == 4) { - af = AF_INET; - } - else if (af == 6) { - af = AF_INET6; - } - else { - /* Uh...this ain't right */ - af = 0; + sa_stats_arg = (__typeof__(sa_stats_arg)) + (void *)mhp->ext[SADB_EXT_SASTAT]; + arg_count = sa_stats_arg->sadb_sastat_list_len; + // exit early if there are no requested SAs + if (arg_count == 0) { + printf("%s: No SAs requested.\n", __FUNCTION__); + error = ENOENT; + goto end; } - - hack_hdr.mh_next = (struct mbuf*)m; - hack_hdr.mh_nextpkt = NULL; - hack_hdr.mh_len = 4; - hack_hdr.mh_data = (char *)⁡ - hack_hdr.mh_type = ((struct mbuf*)m)->m_type; - hack_hdr.mh_flags = 0; - - n = (struct mbuf*)&hack_hdr; - - tap(ifp, (mbuf_t)n); -} - + res_count = 0; -static errno_t -ipsecif_ctl_send( - __unused kern_ctl_ref kctlref, - __unused u_int32_t unit, - void *unitinfo, - mbuf_t m, - __unused int flags) -{ - struct ipsecif_pcb *pcb = unitinfo; - struct ifnet_stat_increment_param incs; - errno_t result; - - bzero(&incs, sizeof(incs)); - - mbuf_pkthdr_setrcvif(m, pcb->ifp); - - if (pcb->mode & BPF_MODE_INPUT) { - call_bpf_tap(pcb->ifp, pcb->tap, m); + if (key_getsastatbyspi((struct sastat *)(sa_stats_arg + 1), + arg_count, + sa_stats_sav, + &res_count)) { + printf("%s: Error finding SAs.\n", __FUNCTION__); + error = ENOENT; + goto end; } - - incs.packets_in = 1; - incs.bytes_in = mbuf_pkthdr_len(m); - result = ifnet_input(pcb->ifp, m, &incs); - if (result != 0) { - ifnet_stat_increment_in(pcb->ifp, 0, 0, 1); - printf("ipsecif_ctl_send - ifnet_input failed: %d\n", result); - mbuf_freem(m); + if (!res_count) { + printf("%s: No SAs found.\n", __FUNCTION__); + error = ENOENT; + goto end; } - - return 0; -} -/* Network Interface functions */ -static errno_t -ipsecif_output( - ifnet_t interface, - mbuf_t data) -{ - struct ipsecif_pcb *pcb = ifnet_softc(interface); - errno_t result; - - if (pcb->mode & BPF_MODE_OUTPUT) { - call_bpf_tap(interface, pcb->tap, data); - } - - // no packet should go to the ipsec interface - mbuf_freem(data); - -#if 0 - if (pcb->ctlref) { - int length = mbuf_pkthdr_len(data); - result = ctl_enqueuembuf(pcb->ctlref, pcb->unit, data, CTL_DATA_EOR); - if (result != 0) { - mbuf_freem(data); - printf("ipsecif_output - ctl_enqueuembuf failed: %d\n", result); - ifnet_stat_increment_out(interface, 0, 0, 1); - } - else { - ifnet_stat_increment_out(interface, 1, length, 0); - } - } - else - mbuf_freem(data); -#endif - - return 0; -} + session_id = (__typeof__(session_id)) + (void *)mhp->ext[SADB_EXT_SESSION_ID]; -/* Network Interface functions */ -static errno_t -ipsecif_demux( - __unused ifnet_t interface, - mbuf_t data, - __unused char *frame_header, - protocol_family_t *protocol) -{ - u_int8_t *vers; - - while (data != NULL && mbuf_len(data) < 1) { - data = mbuf_next(data); - } - - if (data != NULL) { - vers = mbuf_data(data); - switch(((*vers) & 0xf0) >> 4) { - case 4: - *protocol = PF_INET; - return 0; - - case 6: - *protocol = PF_INET6; - return 0; - } + /* send this to the userland. */ + n = key_setdumpsastats(sa_stats_arg->sadb_sastat_dir, + sa_stats_sav, + res_count, + session_id->sadb_session_id_v, + mhp->msg->sadb_msg_seq, + mhp->msg->sadb_msg_pid); + if (!n) { + printf("%s: No bufs to dump stats.\n", __FUNCTION__); + error = ENOBUFS; + goto end; } - - return ENOENT; -} -static errno_t -ipsecif_add_proto( - __unused ifnet_t interface, - protocol_family_t protocol, - __unused const struct ifnet_demux_desc *demux_array, - __unused u_int32_t demux_count) -{ - switch(protocol) { - case PF_INET: - return 0; - case PF_INET6: - return 0; - default: - break; + key_sendup_mbuf(so, n, KEY_SENDUP_ALL); +end: + if (sa_stats_sav) { + KFREE(sa_stats_sav); } - - return ENOPROTOOPT; -} - -static errno_t -ipsecif_del_proto( - __unused ifnet_t interface, - __unused protocol_family_t protocol) -{ - return 0; -} -static errno_t -ipsecif_ioctl( - __unused ifnet_t interface, - __unused u_int32_t command, - __unused void *data) -{ - errno_t result = 0; - - switch(command) { - case SIOCSIFMTU: - ifnet_set_mtu(interface, ((struct ifreq*)data)->ifr_mtu); - break; - - default: - result = EOPNOTSUPP; - } - - return result; -} + if (error) + return key_senderror(so, m, error); -static errno_t -ipsecif_settap( - ifnet_t interface, - bpf_tap_mode mode, - bpf_packet_func callback) -{ - struct ipsecif_pcb *pcb = ifnet_softc(interface); - - pcb->mode = mode; - pcb->tap = callback; - + m_freem(m); return 0; } static void -ipsecif_detached( - ifnet_t interface) +key_update_natt_keepalive_timestamp (struct secasvar *sav_sent, + struct secasvar *sav_update) { - struct ipsecif_pcb *pcb = ifnet_softc(interface); - - ipsecif_free(pcb); - - OSDecrementAtomic(&ipsecif_ifcount); -} + struct secasindex saidx_swap_sent_addr; -/* Protocol Handlers */ + // exit early if two SAs are identical, or if sav_update is current + if (sav_sent == sav_update || + sav_update->natt_last_activity == natt_now) { + return; + } -static errno_t -ipsecif_proto_input( - __unused ifnet_t interface, - protocol_family_t protocol, - mbuf_t m, - __unused char *frame_header) -{ - proto_input(protocol, m); - - return 0; -} + // assuming that (sav_update->remote_ike_port != 0 && (esp_udp_encap_port & 0xFFFF) != 0) -static errno_t -ipsecif_attach_proto( - ifnet_t interface, - protocol_family_t protocol) -{ - struct ifnet_attach_proto_param proto; - errno_t result; - - bzero(&proto, sizeof(proto)); - proto.input = ipsecif_proto_input; - - result = ifnet_attach_protocol(interface, protocol, &proto); - if (result != 0 && result != EEXIST) { - printf("ipsecif_attach_inet - ifnet_attach_protocol %d failed: %d\n", - protocol, result); + bzero(&saidx_swap_sent_addr, sizeof(saidx_swap_sent_addr)); + memcpy(&saidx_swap_sent_addr.src, &sav_sent->sah->saidx.dst, sizeof(saidx_swap_sent_addr.src)); + memcpy(&saidx_swap_sent_addr.dst, &sav_sent->sah->saidx.src, sizeof(saidx_swap_sent_addr.dst)); + saidx_swap_sent_addr.proto = sav_sent->sah->saidx.proto; + saidx_swap_sent_addr.mode = sav_sent->sah->saidx.mode; + // we ignore reqid for split-tunnel setups + + if (key_cmpsaidx(&sav_sent->sah->saidx, &sav_update->sah->saidx, CMP_MODE | CMP_PORT) || + key_cmpsaidx(&saidx_swap_sent_addr, &sav_update->sah->saidx, CMP_MODE | CMP_PORT)) { + sav_update->natt_last_activity = natt_now; } - - return result; } -