#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/pool.h>
#include <sys/timeout.h>
#include <net/rtable.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
int ipsp_spd_inp(struct mbuf *, const struct ipsec_level *,
struct ipsec_policy *, struct tdb **);
int ipsp_acquire_sa(struct ipsec_policy *, union sockaddr_union *,
union sockaddr_union *, struct sockaddr_encap *, struct mbuf *);
int ipsp_pending_acquire(struct ipsec_policy *, union sockaddr_union *);
void ipsp_delete_acquire_timer(void *);
void ipsp_delete_acquire_locked(struct ipsec_acquire *);
void ipsp_delete_acquire(struct ipsec_acquire *);
void ipsp_unref_acquire_locked(struct ipsec_acquire *);
struct pool ipsec_policy_pool;
struct pool ipsec_acquire_pool;
struct mutex ipo_tdb_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct radix_node_head **spd_tables;
unsigned int spd_table_max;
struct mutex ipsec_acquire_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct ipsec_acquire_head ipsec_acquire_head =
TAILQ_HEAD_INITIALIZER(ipsec_acquire_head);
struct radix_node_head *
spd_table_get(unsigned int rtableid)
{
unsigned int rdomain;
NET_ASSERT_LOCKED();
if (spd_tables == NULL)
return (NULL);
rdomain = rtable_l2(rtableid);
if (rdomain > spd_table_max)
return (NULL);
return (spd_tables[rdomain]);
}
struct radix_node_head *
spd_table_add(unsigned int rtableid)
{
struct radix_node_head *rnh = NULL;
unsigned int rdomain;
void *p;
NET_ASSERT_LOCKED_EXCLUSIVE();
rdomain = rtable_l2(rtableid);
if (spd_tables == NULL || rdomain > spd_table_max) {
if ((p = mallocarray(rdomain + 1, sizeof(*rnh),
M_RTABLE, M_NOWAIT|M_ZERO)) == NULL)
return (NULL);
if (spd_tables != NULL) {
memcpy(p, spd_tables, sizeof(*rnh) * (spd_table_max+1));
free(spd_tables, M_RTABLE,
sizeof(*rnh) * (spd_table_max+1));
}
spd_tables = p;
spd_table_max = rdomain;
}
if (spd_tables[rdomain] == NULL) {
if (rn_inithead((void **)&rnh,
offsetof(struct sockaddr_encap, sen_type)) == 0)
rnh = NULL;
spd_tables[rdomain] = rnh;
}
return (spd_tables[rdomain]);
}
int
spd_table_walk(unsigned int rtableid,
int (*func)(struct ipsec_policy *, void *, unsigned int), void *arg)
{
struct radix_node_head *rnh;
int (*walker)(struct radix_node *, void *, u_int) = (void *)func;
int error;
rnh = spd_table_get(rtableid);
if (rnh == NULL)
return (0);
while ((error = rn_walktree(rnh, walker, arg)) == EAGAIN)
continue;
return (error);
}
int
ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int direction,
struct tdb *tdbin, const struct ipsec_level *seclevel, struct tdb **tdbout,
struct ipsec_ids *ipsecflowinfo_ids)
{
struct radix_node_head *rnh;
struct radix_node *rn;
union sockaddr_union sdst, ssrc;
struct sockaddr_encap *ddst, dst;
struct ipsec_policy *ipo;
struct ipsec_ids *ids = NULL;
int error, signore = 0, dignore = 0;
u_int rdomain;
NET_ASSERT_LOCKED();
if (!ipsec_in_use)
return ipsp_spd_inp(m, seclevel, NULL, tdbout);
if ((seclevel != NULL) && (direction == IPSP_DIRECTION_IN) &&
(seclevel->sl_esp_trans == IPSEC_LEVEL_BYPASS) &&
(seclevel->sl_esp_network == IPSEC_LEVEL_BYPASS) &&
(seclevel->sl_auth == IPSEC_LEVEL_BYPASS)) {
if (tdbout != NULL)
*tdbout = NULL;
return 0;
}
memset(&dst, 0, sizeof(dst));
memset(&sdst, 0, sizeof(union sockaddr_union));
memset(&ssrc, 0, sizeof(union sockaddr_union));
ddst = (struct sockaddr_encap *)&dst;
ddst->sen_family = PF_KEY;
ddst->sen_len = SENT_LEN;
switch (af) {
case AF_INET:
if (hlen < sizeof (struct ip) || m->m_pkthdr.len < hlen)
return EINVAL;
ddst->sen_direction = direction;
ddst->sen_type = SENT_IP4;
m_copydata(m, offsetof(struct ip, ip_src),
sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_src));
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_dst));
m_copydata(m, offsetof(struct ip, ip_p), sizeof(u_int8_t),
(caddr_t) &(ddst->sen_proto));
sdst.sin.sin_family = ssrc.sin.sin_family = AF_INET;
sdst.sin.sin_len = ssrc.sin.sin_len =
sizeof(struct sockaddr_in);
ssrc.sin.sin_addr = ddst->sen_ip_src;
sdst.sin.sin_addr = ddst->sen_ip_dst;
switch (ddst->sen_proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
if (m->m_pkthdr.len < hlen + 2 * sizeof(u_int16_t))
return EINVAL;
m_copydata(m, hlen, sizeof(u_int16_t),
(caddr_t) &(ddst->sen_sport));
m_copydata(m, hlen + sizeof(u_int16_t),
sizeof(u_int16_t),
(caddr_t) &(ddst->sen_dport));
break;
default:
ddst->sen_sport = 0;
ddst->sen_dport = 0;
}
break;
#ifdef INET6
case AF_INET6:
if (hlen < sizeof (struct ip6_hdr) || m->m_pkthdr.len < hlen)
return EINVAL;
ddst->sen_type = SENT_IP6;
ddst->sen_ip6_direction = direction;
m_copydata(m, offsetof(struct ip6_hdr, ip6_src),
sizeof(struct in6_addr),
(caddr_t) &(ddst->sen_ip6_src));
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
sizeof(struct in6_addr),
(caddr_t) &(ddst->sen_ip6_dst));
m_copydata(m, offsetof(struct ip6_hdr, ip6_nxt),
sizeof(u_int8_t),
(caddr_t) &(ddst->sen_ip6_proto));
sdst.sin6.sin6_family = ssrc.sin6.sin6_family = AF_INET6;
sdst.sin6.sin6_len = ssrc.sin6.sin6_len =
sizeof(struct sockaddr_in6);
in6_recoverscope(&ssrc.sin6, &ddst->sen_ip6_src);
in6_recoverscope(&sdst.sin6, &ddst->sen_ip6_dst);
switch (ddst->sen_ip6_proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
if (m->m_pkthdr.len < hlen + 2 * sizeof(u_int16_t))
return EINVAL;
m_copydata(m, hlen, sizeof(u_int16_t),
(caddr_t) &(ddst->sen_ip6_sport));
m_copydata(m, hlen + sizeof(u_int16_t),
sizeof(u_int16_t),
(caddr_t) &(ddst->sen_ip6_dport));
break;
default:
ddst->sen_ip6_sport = 0;
ddst->sen_ip6_dport = 0;
}
break;
#endif
default:
return EAFNOSUPPORT;
}
rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
if ((rnh = spd_table_get(rdomain)) == NULL ||
(rn = rn_match((caddr_t)&dst, rnh)) == NULL) {
return ipsp_spd_inp(m, seclevel, NULL, tdbout);
}
ipo = (struct ipsec_policy *)rn;
switch (ipo->ipo_type) {
case IPSP_PERMIT:
return ipsp_spd_inp(m, seclevel, ipo, tdbout);
case IPSP_DENY:
return EHOSTUNREACH;
case IPSP_IPSEC_USE:
case IPSP_IPSEC_ACQUIRE:
case IPSP_IPSEC_REQUIRE:
case IPSP_IPSEC_DONTACQ:
break;
default:
return EINVAL;
}
switch (ipo->ipo_dst.sa.sa_family) {
case AF_INET:
if ((ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_ANY) ||
(ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_BROADCAST))
dignore = 1;
break;
#ifdef INET6
case AF_INET6:
if ((IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_dst.sin6.sin6_addr)) ||
(memcmp(&ipo->ipo_dst.sin6.sin6_addr, &in6mask128,
sizeof(in6mask128)) == 0))
dignore = 1;
break;
#endif
}
switch (ipo->ipo_src.sa.sa_family) {
case AF_INET:
if (ipo->ipo_src.sin.sin_addr.s_addr == INADDR_ANY)
signore = 1;
break;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_src.sin6.sin6_addr))
signore = 1;
break;
#endif
}
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL &&
(ipo->ipo_tdb->tdb_flags & TDBF_INVALID)) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
}
mtx_leave(&ipo_tdb_mtx);
if (direction == IPSP_DIRECTION_OUT) {
if ((seclevel != NULL) &&
(seclevel->sl_esp_trans == IPSEC_LEVEL_BYPASS) &&
(seclevel->sl_esp_network == IPSEC_LEVEL_BYPASS) &&
(seclevel->sl_auth == IPSEC_LEVEL_BYPASS)) {
if (dignore ||
!memcmp(&sdst, &ipo->ipo_dst, sdst.sa.sa_len)) {
if (tdbout != NULL)
*tdbout = NULL;
return 0;
}
}
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
if ((ipo->ipo_last_searched <= ipsec_last_added) ||
(ipo->ipo_sproto != ipo->ipo_tdb->tdb_sproto) ||
memcmp(dignore ? &sdst : &ipo->ipo_dst,
&ipo->ipo_tdb->tdb_dst,
ipo->ipo_tdb->tdb_dst.sa.sa_len))
goto nomatchout;
if (!ipsp_aux_match(ipo->ipo_tdb,
ipsecflowinfo_ids? ipsecflowinfo_ids: ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask))
goto nomatchout;
error = ipsp_spd_inp(m, seclevel, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
nomatchout:
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0;
}
if (ipo->ipo_last_searched <= ipsec_last_added) {
struct tdb *tdbp_new;
if (dignore == 0)
ipo->ipo_last_searched = getuptime();
mtx_leave(&ipo_tdb_mtx);
tdbp_new = gettdbbydst(rdomain,
dignore ? &sdst : &ipo->ipo_dst,
ipo->ipo_sproto,
ipsecflowinfo_ids? ipsecflowinfo_ids: ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask);
ids = NULL;
mtx_enter(&ipo_tdb_mtx);
if ((tdbp_new != NULL) &&
(tdbp_new->tdb_flags & TDBF_DELETED)) {
tdb_unref(tdbp_new);
tdbp_new = NULL;
}
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdbp_new;
if (ipo->ipo_tdb != NULL) {
TAILQ_INSERT_TAIL(
&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
error = ipsp_spd_inp(m, seclevel, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
}
}
mtx_leave(&ipo_tdb_mtx);
switch (ipo->ipo_type) {
case IPSP_IPSEC_REQUIRE:
if (ipsp_acquire_sa(ipo,
dignore ? &sdst : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, m) != 0) {
return EACCES;
}
case IPSP_IPSEC_DONTACQ:
return -EINVAL;
case IPSP_IPSEC_ACQUIRE:
ipsp_acquire_sa(ipo, dignore ? &sdst : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, NULL);
case IPSP_IPSEC_USE:
return ipsp_spd_inp(m, seclevel, ipo, tdbout);
}
} else {
if (tdbin != NULL) {
if (ipo->ipo_sproto == IPPROTO_IPCOMP &&
tdbin->tdb_sproto == IPPROTO_ESP &&
tdbin->tdb_inext != NULL &&
tdbin->tdb_inext->tdb_sproto == IPPROTO_IPCOMP)
tdbin = tdbin->tdb_inext;
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb == tdbin) {
error = ipsp_spd_inp(m, seclevel, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
}
mtx_leave(&ipo_tdb_mtx);
if (memcmp(dignore ? &ssrc : &ipo->ipo_dst,
&tdbin->tdb_src, tdbin->tdb_src.sa.sa_len) ||
(ipo->ipo_sproto != tdbin->tdb_sproto))
goto nomatchin;
if (ipo->ipo_ids)
if (tdbin->tdb_ids == NULL ||
!ipsp_ids_match(ipo->ipo_ids,
tdbin->tdb_ids))
goto nomatchin;
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdb_ref(tdbin);
TAILQ_INSERT_TAIL(&tdbin->tdb_policy_head, ipo,
ipo_tdb_next);
error = ipsp_spd_inp(m, seclevel, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
nomatchin:
;
}
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
if (ipo->ipo_sproto == ipo->ipo_tdb->tdb_sproto &&
!memcmp(&ipo->ipo_tdb->tdb_src,
dignore ? &ssrc : &ipo->ipo_dst,
ipo->ipo_tdb->tdb_src.sa.sa_len))
goto skipinputsearch;
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0;
}
if (ipo->ipo_last_searched <= ipsec_last_added) {
struct tdb *tdbp_new;
if (dignore == 0)
ipo->ipo_last_searched = getuptime();
mtx_leave(&ipo_tdb_mtx);
tdbp_new = gettdbbysrc(rdomain,
dignore ? &ssrc : &ipo->ipo_dst,
ipo->ipo_sproto, ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask);
mtx_enter(&ipo_tdb_mtx);
if ((tdbp_new != NULL) &&
(tdbp_new->tdb_flags & TDBF_DELETED)) {
tdb_unref(tdbp_new);
tdbp_new = NULL;
}
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdbp_new;
if (ipo->ipo_tdb != NULL) {
TAILQ_INSERT_TAIL(
&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
}
}
skipinputsearch:
mtx_leave(&ipo_tdb_mtx);
switch (ipo->ipo_type) {
case IPSP_IPSEC_REQUIRE:
if (ipo->ipo_tdb != NULL)
return -EINVAL;
if ((error = ipsp_acquire_sa(ipo,
dignore ? &ssrc : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, m)) != 0)
return error;
case IPSP_IPSEC_DONTACQ:
return -EINVAL;
case IPSP_IPSEC_ACQUIRE:
if (ipo->ipo_tdb != NULL)
return ipsp_spd_inp(m, seclevel, ipo, tdbout);
ipsp_acquire_sa(ipo, dignore ? &ssrc : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, NULL);
case IPSP_IPSEC_USE:
return ipsp_spd_inp(m, seclevel, ipo, tdbout);
}
}
return EINVAL;
}
int
ipsec_delete_policy(struct ipsec_policy *ipo)
{
struct ipsec_acquire *ipa;
struct radix_node_head *rnh;
struct radix_node *rn = (struct radix_node *)ipo;
NET_ASSERT_LOCKED_EXCLUSIVE();
if (refcnt_rele(&ipo->ipo_refcnt) == 0)
return 0;
if ((rnh = spd_table_get(ipo->ipo_rdomain)) == NULL ||
rn_delete(&ipo->ipo_addr, &ipo->ipo_mask, rnh, rn) == NULL)
return (ESRCH);
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
}
mtx_leave(&ipo_tdb_mtx);
mtx_enter(&ipsec_acquire_mtx);
while ((ipa = TAILQ_FIRST(&ipo->ipo_acquires)) != NULL)
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);
if (ipo->ipo_ids)
ipsp_ids_free(ipo->ipo_ids);
ipsec_in_use--;
pool_put(&ipsec_policy_pool, ipo);
return 0;
}
void
ipsp_delete_acquire_timer(void *v)
{
struct ipsec_acquire *ipa = v;
mtx_enter(&ipsec_acquire_mtx);
refcnt_rele(&ipa->ipa_refcnt);
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
void
ipsp_delete_acquire(struct ipsec_acquire *ipa)
{
mtx_enter(&ipsec_acquire_mtx);
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
void
ipsp_delete_acquire_locked(struct ipsec_acquire *ipa)
{
if (timeout_del(&ipa->ipa_timeout) == 1)
refcnt_rele(&ipa->ipa_refcnt);
ipsp_unref_acquire_locked(ipa);
}
void
ipsec_unref_acquire(struct ipsec_acquire *ipa)
{
mtx_enter(&ipsec_acquire_mtx);
ipsp_unref_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
void
ipsp_unref_acquire_locked(struct ipsec_acquire *ipa)
{
MUTEX_ASSERT_LOCKED(&ipsec_acquire_mtx);
if (refcnt_rele(&ipa->ipa_refcnt) == 0)
return;
TAILQ_REMOVE(&ipsec_acquire_head, ipa, ipa_next);
TAILQ_REMOVE(&ipa->ipa_policy->ipo_acquires, ipa, ipa_ipo_next);
ipa->ipa_policy = NULL;
pool_put(&ipsec_acquire_pool, ipa);
}
int
ipsp_pending_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
mtx_enter(&ipsec_acquire_mtx);
TAILQ_FOREACH(ipa, &ipo->ipo_acquires, ipa_ipo_next) {
if (!memcmp(gw, &ipa->ipa_addr, gw->sa.sa_len))
break;
}
mtx_leave(&ipsec_acquire_mtx);
return (ipa != NULL);
}
int
ipsp_acquire_sa(struct ipsec_policy *ipo, union sockaddr_union *gw,
union sockaddr_union *laddr, struct sockaddr_encap *ddst, struct mbuf *m)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
if (ipsp_pending_acquire(ipo, gw))
return 0;
ipa = pool_get(&ipsec_acquire_pool, PR_NOWAIT|PR_ZERO);
if (ipa == NULL)
return ENOMEM;
ipa->ipa_addr = *gw;
refcnt_init(&ipa->ipa_refcnt);
timeout_set(&ipa->ipa_timeout, ipsp_delete_acquire_timer, ipa);
ipa->ipa_info.sen_len = ipa->ipa_mask.sen_len = SENT_LEN;
ipa->ipa_info.sen_family = ipa->ipa_mask.sen_family = PF_KEY;
switch (ipo->ipo_addr.sen_type) {
case SENT_IP4:
ipa->ipa_info.sen_type = ipa->ipa_mask.sen_type = SENT_IP4;
ipa->ipa_info.sen_direction = ipo->ipo_addr.sen_direction;
ipa->ipa_mask.sen_direction = ipo->ipo_mask.sen_direction;
if (ipsp_is_unspecified(ipo->ipo_dst)) {
ipa->ipa_info.sen_ip_src = ddst->sen_ip_src;
ipa->ipa_mask.sen_ip_src.s_addr = INADDR_BROADCAST;
ipa->ipa_info.sen_ip_dst = ddst->sen_ip_dst;
ipa->ipa_mask.sen_ip_dst.s_addr = INADDR_BROADCAST;
} else {
ipa->ipa_info.sen_ip_src = ipo->ipo_addr.sen_ip_src;
ipa->ipa_mask.sen_ip_src = ipo->ipo_mask.sen_ip_src;
ipa->ipa_info.sen_ip_dst = ipo->ipo_addr.sen_ip_dst;
ipa->ipa_mask.sen_ip_dst = ipo->ipo_mask.sen_ip_dst;
}
ipa->ipa_info.sen_proto = ipo->ipo_addr.sen_proto;
ipa->ipa_mask.sen_proto = ipo->ipo_mask.sen_proto;
if (ipo->ipo_addr.sen_proto) {
ipa->ipa_info.sen_sport = ipo->ipo_addr.sen_sport;
ipa->ipa_mask.sen_sport = ipo->ipo_mask.sen_sport;
ipa->ipa_info.sen_dport = ipo->ipo_addr.sen_dport;
ipa->ipa_mask.sen_dport = ipo->ipo_mask.sen_dport;
}
break;
#ifdef INET6
case SENT_IP6:
ipa->ipa_info.sen_type = ipa->ipa_mask.sen_type = SENT_IP6;
ipa->ipa_info.sen_ip6_direction =
ipo->ipo_addr.sen_ip6_direction;
ipa->ipa_mask.sen_ip6_direction =
ipo->ipo_mask.sen_ip6_direction;
if (ipsp_is_unspecified(ipo->ipo_dst)) {
ipa->ipa_info.sen_ip6_src = ddst->sen_ip6_src;
ipa->ipa_mask.sen_ip6_src = in6mask128;
ipa->ipa_info.sen_ip6_dst = ddst->sen_ip6_dst;
ipa->ipa_mask.sen_ip6_dst = in6mask128;
} else {
ipa->ipa_info.sen_ip6_src = ipo->ipo_addr.sen_ip6_src;
ipa->ipa_mask.sen_ip6_src = ipo->ipo_mask.sen_ip6_src;
ipa->ipa_info.sen_ip6_dst = ipo->ipo_addr.sen_ip6_dst;
ipa->ipa_mask.sen_ip6_dst = ipo->ipo_mask.sen_ip6_dst;
}
ipa->ipa_info.sen_ip6_proto = ipo->ipo_addr.sen_ip6_proto;
ipa->ipa_mask.sen_ip6_proto = ipo->ipo_mask.sen_ip6_proto;
if (ipo->ipo_mask.sen_ip6_proto) {
ipa->ipa_info.sen_ip6_sport =
ipo->ipo_addr.sen_ip6_sport;
ipa->ipa_mask.sen_ip6_sport =
ipo->ipo_mask.sen_ip6_sport;
ipa->ipa_info.sen_ip6_dport =
ipo->ipo_addr.sen_ip6_dport;
ipa->ipa_mask.sen_ip6_dport =
ipo->ipo_mask.sen_ip6_dport;
}
break;
#endif
default:
pool_put(&ipsec_acquire_pool, ipa);
return 0;
}
mtx_enter(&ipsec_acquire_mtx);
#ifdef IPSEC
if (timeout_add_sec(&ipa->ipa_timeout,
atomic_load_int(&ipsec_expire_acquire)) == 1)
refcnt_take(&ipa->ipa_refcnt);
#endif
TAILQ_INSERT_TAIL(&ipsec_acquire_head, ipa, ipa_next);
TAILQ_INSERT_TAIL(&ipo->ipo_acquires, ipa, ipa_ipo_next);
ipa->ipa_policy = ipo;
mtx_leave(&ipsec_acquire_mtx);
return pfkeyv2_acquire(ipo, gw, laddr, &ipa->ipa_seq, ddst);
}
int
ipsp_spd_inp(struct mbuf *m, const struct ipsec_level *seclevel,
struct ipsec_policy *ipo, struct tdb **tdbout)
{
if (seclevel == NULL)
goto justreturn;
if (seclevel->sl_esp_trans == IPSEC_LEVEL_BYPASS &&
seclevel->sl_esp_network == IPSEC_LEVEL_BYPASS &&
seclevel->sl_auth == IPSEC_LEVEL_BYPASS)
goto justreturn;
if (seclevel->sl_esp_trans == IPSEC_LEVEL_AVAIL &&
seclevel->sl_esp_network == IPSEC_LEVEL_AVAIL &&
seclevel->sl_auth == IPSEC_LEVEL_AVAIL)
goto justreturn;
return -EINVAL;
justreturn:
if (tdbout != NULL) {
if (ipo != NULL)
*tdbout = tdb_ref(ipo->ipo_tdb);
else
*tdbout = NULL;
}
return 0;
}
struct ipsec_acquire *
ipsec_get_acquire(u_int32_t seq)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
mtx_enter(&ipsec_acquire_mtx);
TAILQ_FOREACH(ipa, &ipsec_acquire_head, ipa_next) {
if (ipa->ipa_seq == seq) {
refcnt_take(&ipa->ipa_refcnt);
break;
}
}
mtx_leave(&ipsec_acquire_mtx);
return ipa;
}