summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2015-05-23 12:38:54 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2015-05-23 12:38:54 +0000
commitc5c3b04eba7e7ace6db10b6bb91112148249a324 (patch)
tree28ffcd28d613c934819c6e7f89c5d14a7a92d6fa /sys
parentf358761cb8f1672763a3a2d0491b4d613db30afa (diff)
introduce ipsec-id bundles and use them for ipsecflowinfo,
fixes rekeying for l2tp/ipsec against multiple windows clients and saves memory (for many SAs to same peers); feedback and ok mikeb@
Diffstat (limited to 'sys')
-rw-r--r--sys/net/pfkeyv2.c119
-rw-r--r--sys/net/pfkeyv2.h7
-rw-r--r--sys/net/pfkeyv2_convert.c68
-rw-r--r--sys/netinet/ip_ipsp.c175
-rw-r--r--sys/netinet/ip_ipsp.h42
-rw-r--r--sys/netinet/ip_spd.c53
-rw-r--r--sys/netinet/udp_usrreq.c6
7 files changed, 266 insertions, 204 deletions
diff --git a/sys/net/pfkeyv2.c b/sys/net/pfkeyv2.c
index d0c95d5435e..54114e961dc 100644
--- a/sys/net/pfkeyv2.c
+++ b/sys/net/pfkeyv2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2.c,v 1.143 2015/04/16 19:24:13 markus Exp $ */
+/* $OpenBSD: pfkeyv2.c,v 1.144 2015/05/23 12:38:53 markus Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
@@ -523,11 +523,10 @@ pfkeyv2_get(struct tdb *sa, void **headers, void **buffer, int *lenp)
i += sizeof(struct sadb_address) + PADUP(SA_LEN(&sa->tdb_src.sa));
i += sizeof(struct sadb_address) + PADUP(SA_LEN(&sa->tdb_dst.sa));
- if (sa->tdb_srcid)
- i += sizeof(struct sadb_ident) + PADUP(sa->tdb_srcid->ref_len);
-
- if (sa->tdb_dstid)
- i += sizeof(struct sadb_ident) + PADUP(sa->tdb_dstid->ref_len);
+ if (sa->tdb_ids) {
+ i += sizeof(struct sadb_ident) + PADUP(sa->tdb_ids->id_local->len);
+ i += sizeof(struct sadb_ident) + PADUP(sa->tdb_ids->id_remote->len);
+ }
if (sa->tdb_amxkey)
i += sizeof(struct sadb_key) + PADUP(sa->tdb_amxkeylen);
@@ -613,17 +612,9 @@ pfkeyv2_get(struct tdb *sa, void **headers, void **buffer, int *lenp)
headers[SADB_EXT_ADDRESS_DST] = p;
export_address(&p, (struct sockaddr *) &sa->tdb_dst);
- /* Export source identity, if present */
- if (sa->tdb_srcid) {
- headers[SADB_EXT_IDENTITY_SRC] = p;
- export_identity(&p, &sa->tdb_srcid);
- }
-
- /* Export destination identity, if present */
- if (sa->tdb_dstid) {
- headers[SADB_EXT_IDENTITY_DST] = p;
- export_identity(&p, &sa->tdb_dstid);
- }
+ /* Export source/destination identities, if present */
+ if (sa->tdb_ids)
+ export_identities(&p, sa->tdb_ids, sa->tdb_ids_swapped, headers);
/* Export authentication key, if present */
if (sa->tdb_amxkey) {
@@ -809,7 +800,7 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
struct sadb_spirange *sprng;
struct sadb_sa *ssa;
struct sadb_supported *ssup;
- struct sadb_ident *sid;
+ struct sadb_ident *sid, *did;
u_int rdomain;
@@ -995,9 +986,10 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
PFKEYV2_AUTHENTICATION_KEY);
import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
PFKEYV2_ENCRYPTION_KEY);
- import_identity(&newsa->tdb_srcid,
- headers[SADB_EXT_IDENTITY_SRC]);
- import_identity(&newsa->tdb_dstid,
+ newsa->tdb_ids_swapped = 1; /* only on TDB_UPDATE */
+ import_identities(&newsa->tdb_ids,
+ newsa->tdb_ids_swapped,
+ headers[SADB_EXT_IDENTITY_SRC],
headers[SADB_EXT_IDENTITY_DST]);
import_flow(&newsa->tdb_filter, &newsa->tdb_filtermask,
headers[SADB_X_EXT_SRC_FLOW],
@@ -1151,9 +1143,9 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
PFKEYV2_ENCRYPTION_KEY);
- import_identity(&newsa->tdb_srcid,
- headers[SADB_EXT_IDENTITY_SRC]);
- import_identity(&newsa->tdb_dstid,
+ import_identities(&newsa->tdb_ids,
+ newsa->tdb_ids_swapped,
+ headers[SADB_EXT_IDENTITY_SRC],
headers[SADB_EXT_IDENTITY_DST]);
import_flow(&newsa->tdb_filter, &newsa->tdb_filtermask,
@@ -1607,19 +1599,15 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
ipo->ipo_sproto = SADB_X_GETSPROTO(smsg->sadb_msg_satype);
- if (ipo->ipo_srcid) {
- ipsp_reffree(ipo->ipo_srcid);
- ipo->ipo_srcid = NULL;
- }
-
- if (ipo->ipo_dstid) {
- ipsp_reffree(ipo->ipo_dstid);
- ipo->ipo_dstid = NULL;
+ if (ipo->ipo_ids) {
+ ipsp_ids_free(ipo->ipo_ids);
+ ipo->ipo_ids = NULL;
}
- if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL) {
- import_identity(&ipo->ipo_srcid, sid);
- if (ipo->ipo_srcid == NULL) {
+ if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL &&
+ (did = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
+ import_identities(&ipo->ipo_ids, 0, sid, did);
+ if (ipo->ipo_ids == NULL) {
if (exists)
ipsec_delete_policy(ipo);
else
@@ -1630,23 +1618,6 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
}
}
- if ((sid = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
- import_identity(&ipo->ipo_dstid, sid);
- if (ipo->ipo_dstid == NULL) {
- if (exists)
- ipsec_delete_policy(ipo);
- else {
- if (ipo->ipo_dstid)
- ipsp_reffree(ipo->ipo_dstid);
- pool_put(&ipsec_policy_pool, ipo);
- }
-
- splx(s);
- rval = ENOBUFS;
- goto ret;
- }
- }
-
/* Flow type */
if (!exists) {
/* Add SPD entry */
@@ -1666,10 +1637,8 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
- if (ipo->ipo_srcid)
- ipsp_reffree(ipo->ipo_srcid);
- if (ipo->ipo_dstid)
- ipsp_reffree(ipo->ipo_dstid);
+ if (ipo->ipo_ids)
+ ipsp_ids_free(ipo->ipo_ids);
pool_put(&ipsec_policy_pool, ipo);
splx(s);
@@ -1805,11 +1774,10 @@ pfkeyv2_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw,
sizeof(struct sadb_address) + PADUP(SA_LEN(&gw->sa)) +
sizeof(struct sadb_prop) + 1 * sizeof(struct sadb_comb);
- if (ipo->ipo_srcid)
- i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_srcid->ref_len);
-
- if (ipo->ipo_dstid)
- i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_dstid->ref_len);
+ if (ipo->ipo_ids) {
+ i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
+ i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
+ }
/* Allocate */
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
@@ -1856,15 +1824,8 @@ pfkeyv2_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw,
bcopy(gw, headers[SADB_EXT_ADDRESS_DST] + sizeof(struct sadb_address),
SA_LEN(&gw->sa));
- if (ipo->ipo_srcid) {
- headers[SADB_EXT_IDENTITY_SRC] = p;
- export_identity(&p, &ipo->ipo_srcid);
- }
-
- if (ipo->ipo_dstid) {
- headers[SADB_EXT_IDENTITY_DST] = p;
- export_identity(&p, &ipo->ipo_dstid);
- }
+ if (ipo->ipo_ids)
+ export_identities(&p, ipo->ipo_ids, 0, headers);
headers[SADB_EXT_PROPOSAL] = p;
p += sizeof(struct sadb_prop);
@@ -2200,10 +2161,10 @@ pfkeyv2_dump_policy(struct ipsec_policy *ipo, void **headers, void **buffer,
return (EINVAL);
}
- if (ipo->ipo_srcid)
- i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_srcid->ref_len);
- if (ipo->ipo_dstid)
- i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_dstid->ref_len);
+ if (ipo->ipo_ids) {
+ i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
+ i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
+ }
if (lenp)
*lenp = i;
@@ -2237,14 +2198,8 @@ pfkeyv2_dump_policy(struct ipsec_policy *ipo, void **headers, void **buffer,
/* Add ids only when we are root. */
perm = suser(curproc, 0);
- if (perm == 0 && ipo->ipo_srcid) {
- headers[SADB_EXT_IDENTITY_SRC] = p;
- export_identity(&p, &ipo->ipo_srcid);
- }
- if (perm == 0 && ipo->ipo_dstid) {
- headers[SADB_EXT_IDENTITY_DST] = p;
- export_identity(&p, &ipo->ipo_dstid);
- }
+ if (perm == 0 && ipo->ipo_ids)
+ export_identities(&p, ipo->ipo_ids, 0, headers);
rval = 0;
ret:
diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h
index 8e96f0c51b1..3d08bd3cc66 100644
--- a/sys/net/pfkeyv2.h
+++ b/sys/net/pfkeyv2.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2.h,v 1.68 2015/04/17 10:04:37 mikeb Exp $ */
+/* $OpenBSD: pfkeyv2.h,v 1.69 2015/05/23 12:38:53 markus Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) January 1998
*
@@ -424,7 +424,7 @@ int pfkeyv2_sysctl_policydumper(struct ipsec_policy *, void *);
int pfdatatopacket(void *, int, struct mbuf **);
void export_address(void **, struct sockaddr *);
-void export_identity(void **, struct ipsec_ref **);
+void export_identities(void **, struct ipsec_ids *, int, void **);
void export_lifetime(void **, struct tdb *, int);
void export_sa(void **, struct tdb *);
void export_flow(void **, u_int8_t, struct sockaddr_encap *,
@@ -435,7 +435,8 @@ void export_tag(void **, struct tdb *);
void export_tap(void **, struct tdb *);
void import_address(struct sockaddr *, struct sadb_address *);
-void import_identity(struct ipsec_ref **, struct sadb_ident *);
+void import_identities(struct ipsec_ids **, int, struct sadb_ident *,
+ struct sadb_ident *);
void import_key(struct ipsecinit *, struct sadb_key *, int);
void import_lifetime(struct tdb *, struct sadb_lifetime *, int);
void import_sa(struct tdb *, struct sadb_sa *, struct ipsecinit *);
diff --git a/sys/net/pfkeyv2_convert.c b/sys/net/pfkeyv2_convert.c
index 9fa4920c40f..946c11a545d 100644
--- a/sys/net/pfkeyv2_convert.c
+++ b/sys/net/pfkeyv2_convert.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2_convert.c,v 1.50 2015/04/17 10:04:37 mikeb Exp $ */
+/* $OpenBSD: pfkeyv2_convert.c,v 1.51 2015/05/23 12:38:53 markus Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@keromytis.org)
*
@@ -702,46 +702,64 @@ export_address(void **p, struct sockaddr *sa)
/*
* Import an identity payload into the TDB.
*/
-void
-import_identity(struct ipsec_ref **ipr, struct sadb_ident *sadb_ident)
+static void
+import_identity(struct ipsec_id **id, struct sadb_ident *sadb_ident)
{
if (!sadb_ident)
return;
- *ipr = malloc(EXTLEN(sadb_ident) - sizeof(struct sadb_ident) +
- sizeof(struct ipsec_ref), M_CREDENTIALS, M_WAITOK);
- (*ipr)->ref_len = EXTLEN(sadb_ident) - sizeof(struct sadb_ident);
+ *id = malloc(EXTLEN(sadb_ident) - sizeof(struct sadb_ident) +
+ sizeof(struct ipsec_id), M_CREDENTIALS, M_WAITOK);
+ (*id)->len = EXTLEN(sadb_ident) - sizeof(struct sadb_ident);
switch (sadb_ident->sadb_ident_type) {
case SADB_IDENTTYPE_PREFIX:
- (*ipr)->ref_type = IPSP_IDENTITY_PREFIX;
+ (*id)->type = IPSP_IDENTITY_PREFIX;
break;
case SADB_IDENTTYPE_FQDN:
- (*ipr)->ref_type = IPSP_IDENTITY_FQDN;
+ (*id)->type = IPSP_IDENTITY_FQDN;
break;
case SADB_IDENTTYPE_USERFQDN:
- (*ipr)->ref_type = IPSP_IDENTITY_USERFQDN;
+ (*id)->type = IPSP_IDENTITY_USERFQDN;
break;
default:
- free(*ipr, M_CREDENTIALS, 0);
- *ipr = NULL;
+ free(*id, M_CREDENTIALS, 0);
+ *id = NULL;
return;
}
- (*ipr)->ref_count = 1;
- (*ipr)->ref_malloctype = M_CREDENTIALS;
- bcopy((void *) sadb_ident + sizeof(struct sadb_ident), (*ipr) + 1,
- (*ipr)->ref_len);
+ bcopy((void *) sadb_ident + sizeof(struct sadb_ident), (*id) + 1,
+ (*id)->len);
}
void
-export_identity(void **p, struct ipsec_ref **ipr)
+import_identities(struct ipsec_ids **ids, int swapped,
+ struct sadb_ident *srcid, struct sadb_ident *dstid)
+{
+ struct ipsec_ids *tmp;
+
+ *ids = NULL;
+ tmp = malloc(sizeof(struct ipsec_ids), M_CREDENTIALS, M_WAITOK);
+ import_identity(&tmp->id_local, swapped ? dstid: srcid);
+ import_identity(&tmp->id_remote, swapped ? srcid: dstid);
+ if (tmp->id_local != NULL && tmp->id_remote != NULL) {
+ *ids = ipsp_ids_insert(tmp);
+ if (*ids == tmp)
+ return;
+ }
+ free(tmp->id_local, M_CREDENTIALS, 0);
+ free(tmp->id_remote, M_CREDENTIALS, 0);
+ free(tmp, M_CREDENTIALS, 0);
+}
+
+static void
+export_identity(void **p, struct ipsec_id *id)
{
struct sadb_ident *sadb_ident = (struct sadb_ident *) *p;
sadb_ident->sadb_ident_len = (sizeof(struct sadb_ident) +
- PADUP((*ipr)->ref_len)) / sizeof(uint64_t);
+ PADUP(id->len)) / sizeof(uint64_t);
- switch ((*ipr)->ref_type) {
+ switch (id->type) {
case IPSP_IDENTITY_PREFIX:
sadb_ident->sadb_ident_type = SADB_IDENTTYPE_PREFIX;
break;
@@ -753,8 +771,18 @@ export_identity(void **p, struct ipsec_ref **ipr)
break;
}
*p += sizeof(struct sadb_ident);
- bcopy((*ipr) + 1, *p, (*ipr)->ref_len);
- *p += PADUP((*ipr)->ref_len);
+ bcopy(id + 1, *p, id->len);
+ *p += PADUP(id->len);
+}
+
+void
+export_identities(void **p, struct ipsec_ids *ids, int swapped,
+ void **headers)
+{
+ headers[SADB_EXT_IDENTITY_SRC] = *p;
+ export_identity(p, swapped ? ids->id_remote : ids->id_local);
+ headers[SADB_EXT_IDENTITY_DST] = *p;
+ export_identity(p, swapped ? ids->id_local : ids->id_remote);
}
/* ... */
diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c
index c39c3372f6b..499b958811e 100644
--- a/sys/netinet/ip_ipsp.c
+++ b/sys/netinet/ip_ipsp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.c,v 1.213 2015/04/17 11:04:01 mikeb Exp $ */
+/* $OpenBSD: ip_ipsp.c,v 1.214 2015/05/23 12:38:53 markus Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -92,6 +92,19 @@ struct ipsec_policy_head ipsec_policy_head =
struct ipsec_acquire_head ipsec_acquire_head =
TAILQ_HEAD_INITIALIZER(ipsec_acquire_head);
+u_int32_t ipsec_ids_next_flow = 1; /* may not be zero */
+int ipsec_ids_idle = 100; /* keep free ids for 100s */
+struct ipsec_ids_tree ipsec_ids_tree;
+struct ipsec_ids_flows ipsec_ids_flows;
+
+void ipsp_ids_timeout(void *);
+static int ipsp_ids_cmp(struct ipsec_ids *, struct ipsec_ids *);
+static int ipsp_ids_flow_cmp(struct ipsec_ids *, struct ipsec_ids *);
+RB_PROTOTYPE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
+RB_PROTOTYPE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
+RB_GENERATE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
+RB_GENERATE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
+
/*
* This is the proper place to define the various encapsulation transforms.
*/
@@ -331,19 +344,13 @@ gettdbbysrcdst(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
*/
int
ipsp_aux_match(struct tdb *tdb,
- struct ipsec_ref *psrcid,
- struct ipsec_ref *pdstid,
+ struct ipsec_ids *ids,
struct sockaddr_encap *pfilter,
struct sockaddr_encap *pfiltermask)
{
- if (psrcid != NULL)
- if (tdb->tdb_srcid == NULL ||
- !ipsp_ref_match(tdb->tdb_srcid, psrcid))
- return 0;
-
- if (pdstid != NULL)
- if (tdb->tdb_dstid == NULL ||
- !ipsp_ref_match(tdb->tdb_dstid, pdstid))
+ if (ids != NULL)
+ if (tdb->tdb_ids == NULL ||
+ !ipsp_ids_match(tdb->tdb_ids, ids))
return 0;
/* Check for filter matches. */
@@ -372,7 +379,7 @@ ipsp_aux_match(struct tdb *tdb,
*/
struct tdb *
gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
- struct ipsec_ref *srcid, struct ipsec_ref *dstid,
+ struct ipsec_ids *ids,
struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
{
u_int32_t hashval;
@@ -389,8 +396,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(!memcmp(&tdbp->tdb_dst, dst, SA_LEN(&dst->sa)))) {
/* Do IDs match ? */
- if (!ipsp_aux_match(tdbp, srcid, dstid, filter,
- filtermask))
+ if (!ipsp_aux_match(tdbp, ids, filter, filtermask))
continue;
break;
}
@@ -404,7 +410,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
*/
struct tdb *
gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
- struct ipsec_ref *srcid, struct ipsec_ref *dstid,
+ struct ipsec_ids *ids,
struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
{
u_int32_t hashval;
@@ -421,7 +427,7 @@ gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(!memcmp(&tdbp->tdb_src, src, SA_LEN(&src->sa)))) {
/* Check whether IDs match */
- if (!ipsp_aux_match(tdbp, dstid, srcid, filter,
+ if (!ipsp_aux_match(tdbp, ids, filter,
filtermask))
continue;
break;
@@ -793,14 +799,9 @@ tdb_free(struct tdb *tdbp)
timeout_del(&tdbp->tdb_stimer_tmo);
timeout_del(&tdbp->tdb_sfirst_tmo);
- if (tdbp->tdb_srcid) {
- ipsp_reffree(tdbp->tdb_srcid);
- tdbp->tdb_srcid = NULL;
- }
-
- if (tdbp->tdb_dstid) {
- ipsp_reffree(tdbp->tdb_dstid);
- tdbp->tdb_dstid = NULL;
+ if (tdbp->tdb_ids) {
+ ipsp_ids_free(tdbp->tdb_ids);
+ tdbp->tdb_ids = NULL;
}
#if NPF > 0
@@ -892,28 +893,118 @@ ipsp_is_unspecified(union sockaddr_union addr)
}
}
-/* Free reference-counted structure. */
+int
+ipsp_ids_match(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+ return a == b;
+}
+
+struct ipsec_ids *
+ipsp_ids_insert(struct ipsec_ids *ids)
+{
+ struct ipsec_ids *found;
+ u_int32_t start_flow;
+
+ found = RB_INSERT(ipsec_ids_tree, &ipsec_ids_tree, ids);
+ if (found) {
+ /* if refcount was zero, then timeout is running */
+ if (found->id_refcount++ == 0)
+ timeout_del(&found->id_timeout);
+ DPRINTF(("%s: ids %p count %d\n", __func__,
+ found, found->id_refcount));
+ return found;
+ }
+ ids->id_flow = start_flow = ipsec_ids_next_flow;
+ if (++ipsec_ids_next_flow == 0)
+ ipsec_ids_next_flow = 1;
+ while (RB_INSERT(ipsec_ids_flows, &ipsec_ids_flows, ids) != NULL) {
+ ids->id_flow = ipsec_ids_next_flow;
+ if (++ipsec_ids_next_flow == 0)
+ ipsec_ids_next_flow = 1;
+ if (ipsec_ids_next_flow == start_flow) {
+ DPRINTF(("ipsec_ids_next_flow exhausted %u\n",
+ ipsec_ids_next_flow));
+ return NULL;
+ }
+ }
+ ids->id_refcount = 1;
+ DPRINTF(("%s: new ids %p flow %u\n", __func__, ids, ids->id_flow));
+ timeout_set(&ids->id_timeout, ipsp_ids_timeout, ids);
+ return ids;
+}
+
+struct ipsec_ids *
+ipsp_ids_lookup(u_int32_t ipsecflowinfo)
+{
+ struct ipsec_ids key;
+
+ key.id_flow = ipsecflowinfo;
+ return RB_FIND(ipsec_ids_flows, &ipsec_ids_flows, &key);
+}
+
+/* free ids only from delayed timeout */
void
-ipsp_reffree(struct ipsec_ref *ipr)
+ipsp_ids_timeout(void *arg)
{
-#ifdef DIAGNOSTIC
- if (ipr->ref_count <= 0)
- printf("ipsp_reffree: illegal reference count %d for "
- "object %p (len = %d, malloctype = %d)\n",
- ipr->ref_count, ipr, ipr->ref_len, ipr->ref_malloctype);
-#endif
- if (--ipr->ref_count <= 0)
- free(ipr, ipr->ref_malloctype, 0);
+ struct ipsec_ids *ids = arg;
+ int s;
+
+ DPRINTF(("%s: ids %p count %d\n", __func__, ids, ids->id_refcount));
+ KASSERT(ids->id_refcount == 0);
+ s = splsoftnet();
+ RB_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
+ RB_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
+ free(ids->id_local, M_CREDENTIALS, 0);
+ free(ids->id_remote, M_CREDENTIALS, 0);
+ free(ids, M_CREDENTIALS, 0);
+ splx(s);
}
-/* Return true if the two structures match. */
-int
-ipsp_ref_match(struct ipsec_ref *ref1, struct ipsec_ref *ref2)
+/* decrements refcount, actual free happens in timeout */
+void
+ipsp_ids_free(struct ipsec_ids *ids)
{
- if (ref1->ref_type != ref2->ref_type ||
- ref1->ref_len != ref2->ref_len ||
- memcmp(ref1 + 1, ref2 + 1, ref1->ref_len))
- return 0;
+ /*
+ * If the refcount becomes zero, then a timeout is started. This
+ * timeout must be cancelled if refcount is increased from zero.
+ */
+ DPRINTF(("%s: ids %p count %d\n", __func__, ids, ids->id_refcount));
+ KASSERT(ids->id_refcount > 0);
+ if (--ids->id_refcount == 0)
+ timeout_add_sec(&ids->id_timeout, ipsec_ids_idle);
+}
- return 1;
+static int
+ipsp_id_cmp(struct ipsec_id *a, struct ipsec_id *b)
+{
+ if (a->type > b->type)
+ return 1;
+ if (a->type < b->type)
+ return -1;
+ if (a->len > b->len)
+ return 1;
+ if (a->len < b->len)
+ return -1;
+ return memcmp(a + 1, b + 1, a->len);
+}
+
+static int
+ipsp_ids_cmp(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+ int ret;
+
+ ret = ipsp_id_cmp(a->id_remote, b->id_remote);
+ if (ret != 0)
+ return ret;
+ return ipsp_id_cmp(a->id_local, b->id_local);
+}
+
+static int
+ipsp_ids_flow_cmp(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+ if (a->id_flow > b->id_flow)
+ return 1;
+ if (a->id_flow < b->id_flow)
+ return -1;
+ return 0;
}
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index 365e985e3ad..3ff6c2d64a5 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.h,v 1.169 2015/04/17 11:04:01 mikeb Exp $ */
+/* $OpenBSD: ip_ipsp.h,v 1.170 2015/05/23 12:38:53 markus Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -47,6 +47,7 @@ struct m_tag;
#include <sys/types.h>
#ifdef _KERNEL
#include <sys/timeout.h>
+#include <sys/tree.h>
#endif
#include <sys/queue.h>
#include <netinet/in.h>
@@ -166,13 +167,23 @@ struct sockaddr_encap {
#define SENT_LEN sizeof(struct sockaddr_encap)
-struct ipsec_ref {
- u_int16_t ref_type; /* Subtype of data */
- int16_t ref_len; /* Length of data following */
- int ref_count; /* Reference count */
- int ref_malloctype; /* malloc(9) type, for freeing */
+struct ipsec_id {
+ u_int16_t type; /* Subtype of data */
+ int16_t len; /* Length of data following */
};
+struct ipsec_ids {
+ RB_ENTRY(ipsec_ids) id_node_id;
+ RB_ENTRY(ipsec_ids) id_node_flow;
+ struct ipsec_id *id_local;
+ struct ipsec_id *id_remote;
+ u_int32_t id_flow;
+ int id_refcount;
+ struct timeout id_timeout;
+};
+RB_HEAD(ipsec_ids_flows, ipsec_ids);
+RB_HEAD(ipsec_ids_tree, ipsec_ids);
+
struct ipsec_acquire {
union sockaddr_union ipa_addr;
u_int32_t ipa_seq;
@@ -212,8 +223,7 @@ struct ipsec_policy {
struct tdb *ipo_tdb; /* Cached entry */
- struct ipsec_ref *ipo_srcid;
- struct ipsec_ref *ipo_dstid;
+ struct ipsec_ids *ipo_ids;
TAILQ_HEAD(ipo_acquires_head, ipsec_acquire) ipo_acquires; /* List of acquires */
TAILQ_ENTRY(ipsec_policy) ipo_tdb_next; /* List TDB policies */
@@ -328,8 +338,8 @@ struct tdb { /* tunnel descriptor block */
u_int8_t tdb_iv[4]; /* Used for HALF-IV ESP */
- struct ipsec_ref *tdb_srcid; /* Source ID for this SA */
- struct ipsec_ref *tdb_dstid; /* Destination ID for this SA */
+ struct ipsec_ids *tdb_ids; /* Src/Dst ID for this SA */
+ int tdb_ids_swapped; /* XXX */
u_int32_t tdb_mtu; /* MTU at this point in the chain */
u_int64_t tdb_mtutimeout; /* When to ignore this entry */
@@ -448,10 +458,10 @@ uint32_t reserve_spi(u_int, u_int32_t, u_int32_t, union sockaddr_union *,
union sockaddr_union *, u_int8_t, int *);
struct tdb *gettdb(u_int, u_int32_t, union sockaddr_union *, u_int8_t);
struct tdb *gettdbbydst(u_int, union sockaddr_union *, u_int8_t,
- struct ipsec_ref *, struct ipsec_ref *,
+ struct ipsec_ids *,
struct sockaddr_encap *, struct sockaddr_encap *);
struct tdb *gettdbbysrc(u_int, union sockaddr_union *, u_int8_t,
- struct ipsec_ref *, struct ipsec_ref *,
+ struct ipsec_ids *,
struct sockaddr_encap *, struct sockaddr_encap *);
struct tdb *gettdbbysrcdst(u_int, u_int32_t, union sockaddr_union *,
union sockaddr_union *, u_int8_t);
@@ -541,10 +551,12 @@ struct tdb *ipsp_spd_lookup(struct mbuf *, int, int, int *, int,
struct tdb *ipsp_spd_inp(struct mbuf *, int, int, int *, int,
struct tdb *, struct inpcb *, struct ipsec_policy *);
int ipsp_is_unspecified(union sockaddr_union);
-int ipsp_ref_match(struct ipsec_ref *, struct ipsec_ref *);
-void ipsp_reffree(struct ipsec_ref *);
-int ipsp_aux_match(struct tdb *, struct ipsec_ref *, struct ipsec_ref *,
+int ipsp_aux_match(struct tdb *, struct ipsec_ids *,
struct sockaddr_encap *, struct sockaddr_encap *);
+int ipsp_ids_match(struct ipsec_ids *, struct ipsec_ids *);
+struct ipsec_ids *ipsp_ids_insert(struct ipsec_ids *);
+struct ipsec_ids *ipsp_ids_lookup(u_int32_t);
+void ipsp_ids_free(struct ipsec_ids *);
int ipsec_common_input(struct mbuf *, int, int, int, int, int);
int ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int);
diff --git a/sys/netinet/ip_spd.c b/sys/netinet/ip_spd.c
index 6ade3de7b41..1bd87d097e7 100644
--- a/sys/netinet/ip_spd.c
+++ b/sys/netinet/ip_spd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_spd.c,v 1.84 2015/04/30 20:12:33 millert Exp $ */
+/* $OpenBSD: ip_spd.c,v 1.85 2015/05/23 12:38:53 markus Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
*
@@ -84,8 +84,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
union sockaddr_union sdst, ssrc;
struct sockaddr_encap *ddst, dst;
struct ipsec_policy *ipo;
- struct ipsec_ref *dstid = NULL, *srcid = NULL;
- struct tdb *tdbin = NULL;
+ struct ipsec_ids *ids = NULL;
int signore = 0, dignore = 0;
u_int rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
@@ -343,17 +342,8 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
}
}
- /*
- * Fetch the incoming TDB based on the SPI passed
- * in ipsecflow and use it's dstid when looking
- * up the outgoing TDB.
- */
- if (ipsecflowinfo &&
- (tdbin = gettdb(rdomain, ipsecflowinfo, &ssrc,
- ipo->ipo_sproto)) != NULL) {
- srcid = tdbin->tdb_dstid;
- dstid = tdbin->tdb_srcid;
- }
+ if (ipsecflowinfo)
+ ids = ipsp_ids_lookup(ipsecflowinfo);
/* Check that the cached TDB (if present), is appropriate. */
if (ipo->ipo_tdb) {
@@ -365,8 +355,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
goto nomatchout;
if (!ipsp_aux_match(ipo->ipo_tdb,
- srcid ? srcid : ipo->ipo_srcid,
- dstid ? dstid : ipo->ipo_dstid,
+ ids ? ids : ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask))
goto nomatchout;
@@ -402,8 +391,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
gettdbbydst(rdomain,
dignore ? &sdst : &ipo->ipo_dst,
ipo->ipo_sproto,
- srcid ? srcid : ipo->ipo_srcid,
- dstid ? dstid : ipo->ipo_dstid,
+ ids ? ids: ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask);
if (ipo->ipo_tdb) {
TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head,
@@ -455,21 +443,11 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
(ipo->ipo_sproto != tdbp->tdb_sproto))
goto nomatchin;
- /* Match source ID. */
- if (ipo->ipo_srcid) {
- if (tdbp->tdb_dstid == NULL ||
- !ipsp_ref_match(ipo->ipo_srcid,
- tdbp->tdb_dstid))
- goto nomatchin;
- }
-
- /* Match destination ID. */
- if (ipo->ipo_dstid) {
- if (tdbp->tdb_srcid == NULL ||
- !ipsp_ref_match(ipo->ipo_dstid,
- tdbp->tdb_srcid))
+ /* Match source/dest IDs. */
+ if (ipo->ipo_ids)
+ if (tdbp->tdb_ids == NULL ||
+ !ipsp_ids_match(ipo->ipo_ids, tdbp->tdb_ids))
goto nomatchin;
- }
/* Add it to the cache. */
if (ipo->ipo_tdb)
@@ -515,9 +493,8 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
ipo->ipo_tdb =
gettdbbysrc(rdomain,
dignore ? &ssrc : &ipo->ipo_dst,
- ipo->ipo_sproto, ipo->ipo_srcid,
- ipo->ipo_dstid, &ipo->ipo_addr,
- &ipo->ipo_mask);
+ ipo->ipo_sproto, ipo->ipo_ids,
+ &ipo->ipo_addr, &ipo->ipo_mask);
if (ipo->ipo_tdb)
TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
@@ -600,10 +577,8 @@ ipsec_delete_policy(struct ipsec_policy *ipo)
TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);
- if (ipo->ipo_srcid)
- ipsp_reffree(ipo->ipo_srcid);
- if (ipo->ipo_dstid)
- ipsp_reffree(ipo->ipo_dstid);
+ if (ipo->ipo_ids)
+ ipsp_ids_free(ipo->ipo_ids);
ipsec_in_use--;
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index ece64632ec8..dd29e16aa73 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_usrreq.c,v 1.198 2015/04/16 19:24:13 markus Exp $ */
+/* $OpenBSD: udp_usrreq.c,v 1.199 2015/05/23 12:38:53 markus Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
@@ -615,8 +615,8 @@ udp_input(struct mbuf *m, ...)
goto bad;
}
/* create ipsec options while we know that tdb cannot be modified */
- if (tdb)
- ipsecflowinfo = tdb->tdb_spi;
+ if (tdb && tdb->tdb_ids)
+ ipsecflowinfo = tdb->tdb_ids->id_flow;
#endif /*IPSEC */
opts = NULL;