diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2015-05-23 12:38:54 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2015-05-23 12:38:54 +0000 |
commit | c5c3b04eba7e7ace6db10b6bb91112148249a324 (patch) | |
tree | 28ffcd28d613c934819c6e7f89c5d14a7a92d6fa /sys | |
parent | f358761cb8f1672763a3a2d0491b4d613db30afa (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.c | 119 | ||||
-rw-r--r-- | sys/net/pfkeyv2.h | 7 | ||||
-rw-r--r-- | sys/net/pfkeyv2_convert.c | 68 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 175 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 42 | ||||
-rw-r--r-- | sys/netinet/ip_spd.c | 53 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 6 |
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; |