summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2021-11-25 13:46:03 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2021-11-25 13:46:03 +0000
commite8a983f68fa2f49fead18f84db69a87c02eb6746 (patch)
treed1f96609775698f0a110ff2ebdd94b0c815a834a
parent90e12edf80cdcf876423da2d6d69d69128ffccd5 (diff)
Implement reference counting for IPsec tdbs. Not all cases are
covered yet, more ref counts to come. The timeouts are protected, so the racy tdb_reaper() gets retired. The tdb_policy_head, onext and inext lists are protected. All gettdb...() functions return a tdb that is ref counted and has to be unrefed later. A flag ensures that tdb_delete() is called only once. Tested by Hrvoje Popovski; OK sthen@ mvs@ tobhe@
-rw-r--r--sys/net/if_bridge.c22
-rw-r--r--sys/net/if_pfsync.c4
-rw-r--r--sys/net/pfkeyv2.c51
-rw-r--r--sys/net/pfkeyv2_convert.c12
-rw-r--r--sys/netinet/ip_ipsp.c122
-rw-r--r--sys/netinet/ip_ipsp.h11
-rw-r--r--sys/netinet/ip_spd.c57
-rw-r--r--sys/netinet/ipsec_input.c31
-rw-r--r--sys/netinet/ipsec_output.c32
-rw-r--r--sys/netinet/tcp_input.c38
-rw-r--r--sys/netinet/tcp_output.c4
-rw-r--r--sys/netinet/udp_usrreq.c4
12 files changed, 271 insertions, 117 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index ceb93e70149..bfc4cd2f1b3 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bridge.c,v 1.358 2021/11/11 18:08:17 bluhm Exp $ */
+/* $OpenBSD: if_bridge.c,v 1.359 2021/11/25 13:46:02 bluhm Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -1567,20 +1567,28 @@ bridge_ipsec(struct ifnet *ifp, struct ether_header *eh, int hassnap,
tdb->tdb_xform != NULL) {
if (tdb->tdb_first_use == 0) {
tdb->tdb_first_use = gettime();
- if (tdb->tdb_flags & TDBF_FIRSTUSE)
- timeout_add_sec(&tdb->tdb_first_tmo,
- tdb->tdb_exp_first_use);
- if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE)
- timeout_add_sec(&tdb->tdb_sfirst_tmo,
- tdb->tdb_soft_first_use);
+ if (tdb->tdb_flags & TDBF_FIRSTUSE) {
+ if (timeout_add_sec(
+ &tdb->tdb_first_tmo,
+ tdb->tdb_exp_first_use))
+ tdb_ref(tdb);
+ }
+ if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
+ if (timeout_add_sec(
+ &tdb->tdb_sfirst_tmo,
+ tdb->tdb_soft_first_use))
+ tdb_ref(tdb);
+ }
}
prot = (*(tdb->tdb_xform->xf_input))(&m, tdb, hlen,
off);
+ tdb_unref(tdb);
if (prot != IPPROTO_DONE)
ip_deliver(&m, &hlen, prot, af);
return (1);
} else {
+ tdb_unref(tdb);
skiplookup:
/* XXX do an input policy lookup */
return (0);
diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c
index 906afa0e3b3..4c538ca7280 100644
--- a/sys/net/if_pfsync.c
+++ b/sys/net/if_pfsync.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_pfsync.c,v 1.298 2021/11/11 12:35:01 sashan Exp $ */
+/* $OpenBSD: if_pfsync.c,v 1.299 2021/11/25 13:46:02 bluhm Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff
@@ -1325,11 +1325,13 @@ pfsync_update_net_tdb(struct pfsync_tdb *pt)
/* Neither replay nor byte counter should ever decrease. */
if (pt->rpl < tdb->tdb_rpl ||
pt->cur_bytes < tdb->tdb_cur_bytes) {
+ tdb_unref(tdb);
goto bad;
}
tdb->tdb_rpl = pt->rpl;
tdb->tdb_cur_bytes = pt->cur_bytes;
+ tdb_unref(tdb);
}
return;
diff --git a/sys/net/pfkeyv2.c b/sys/net/pfkeyv2.c
index f34bd29a7f5..ac1d4376135 100644
--- a/sys/net/pfkeyv2.c
+++ b/sys/net/pfkeyv2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2.c,v 1.221 2021/10/25 18:25:01 bluhm Exp $ */
+/* $OpenBSD: pfkeyv2.c,v 1.222 2021/11/25 13:46:02 bluhm Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
@@ -1043,8 +1043,17 @@ pfkeyv2_sa_flush(struct tdb *tdb, void *satype_vp, int last)
{
if (!(*((u_int8_t *) satype_vp)) ||
tdb->tdb_satype == *((u_int8_t *) satype_vp)) {
+ /* keep in sync with tdb_delete() */
+ NET_ASSERT_LOCKED();
+
+ if (tdb->tdb_flags & TDBF_DELETED)
+ return (0);
+ tdb->tdb_flags |= TDBF_DELETED;
+
tdb_unlink_locked(tdb);
- tdb_free(tdb);
+ tdb_unbundle(tdb);
+ tdb_deltimeouts(tdb);
+ tdb_unref(tdb);
}
return (0);
}
@@ -1327,7 +1336,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
if ((rval = pfkeyv2_get_proto_alg(newsa->tdb_satype,
&newsa->tdb_sproto, &alg))) {
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1363,7 +1372,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
headers[SADB_X_EXT_DST_MASK],
headers[SADB_X_EXT_PROTOCOL],
headers[SADB_X_EXT_FLOW_TYPE]))) {
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1386,7 +1395,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
rval = tdb_init(newsa, alg, &ii);
if (rval) {
rval = EINVAL;
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1397,7 +1406,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
/* Delete old version of the SA, insert new one */
tdb_delete(sa2);
puttdb((struct tdb *) freeme);
- sa2 = freeme = NULL;
+ freeme = NULL;
} else {
/*
* The SA is already initialized, so we're only allowed to
@@ -1503,7 +1512,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
newsa->tdb_satype = smsg->sadb_msg_satype;
if ((rval = pfkeyv2_get_proto_alg(newsa->tdb_satype,
&newsa->tdb_sproto, &alg))) {
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1541,7 +1550,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
headers[SADB_X_EXT_DST_MASK],
headers[SADB_X_EXT_PROTOCOL],
headers[SADB_X_EXT_FLOW_TYPE]))) {
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1564,7 +1573,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
rval = tdb_init(newsa, alg, &ii);
if (rval) {
rval = EINVAL;
- tdb_free(freeme);
+ tdb_unref(freeme);
freeme = NULL;
NET_UNLOCK();
goto ret;
@@ -1596,7 +1605,6 @@ pfkeyv2_send(struct socket *so, void *message, int len)
tdb_delete(sa2);
NET_UNLOCK();
- sa2 = NULL;
break;
case SADB_X_ASKPOLICY:
@@ -1786,6 +1794,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(sa_proto->sadb_protocol_proto));
if (tdb2 == NULL) {
+ tdb_unref(tdb1);
rval = ESRCH;
NET_UNLOCK();
goto ret;
@@ -1794,6 +1803,8 @@ pfkeyv2_send(struct socket *so, void *message, int len)
/* Detect cycles */
for (tdb3 = tdb2; tdb3; tdb3 = tdb3->tdb_onext)
if (tdb3 == tdb1) {
+ tdb_unref(tdb1);
+ tdb_unref(tdb2);
rval = ESRCH;
NET_UNLOCK();
goto ret;
@@ -1801,12 +1812,16 @@ pfkeyv2_send(struct socket *so, void *message, int len)
/* Maintenance */
if ((tdb1->tdb_onext) &&
- (tdb1->tdb_onext->tdb_inext == tdb1))
+ (tdb1->tdb_onext->tdb_inext == tdb1)) {
+ tdb_unref(tdb1->tdb_onext->tdb_inext);
tdb1->tdb_onext->tdb_inext = NULL;
+ }
if ((tdb2->tdb_inext) &&
- (tdb2->tdb_inext->tdb_onext == tdb2))
+ (tdb2->tdb_inext->tdb_onext == tdb2)) {
+ tdb_unref(tdb2->tdb_inext->tdb_onext);
tdb2->tdb_inext->tdb_onext = NULL;
+ }
/* Link them */
tdb1->tdb_onext = tdb2;
@@ -2008,10 +2023,12 @@ pfkeyv2_send(struct socket *so, void *message, int len)
(caddr_t)&ipo->ipo_mask, rnh,
ipo->ipo_nodes, 0)) == NULL) {
/* Remove from linked list of policies on TDB */
- if (ipo->ipo_tdb)
- TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
+ if (ipo->ipo_tdb != NULL) {
+ TAILQ_REMOVE(
+ &ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
-
+ tdb_unref(ipo->ipo_tdb);
+ }
if (ipo->ipo_ids)
ipsp_ids_free(ipo->ipo_ids);
pool_put(&ipsec_policy_pool, ipo);
@@ -2128,6 +2145,10 @@ realret:
free(sa1, M_PFKEY, sizeof(*sa1));
+ NET_LOCK();
+ tdb_unref(sa2);
+ NET_UNLOCK();
+
return (rval);
}
diff --git a/sys/net/pfkeyv2_convert.c b/sys/net/pfkeyv2_convert.c
index cf5c1b3e325..0cd94552d53 100644
--- a/sys/net/pfkeyv2_convert.c
+++ b/sys/net/pfkeyv2_convert.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2_convert.c,v 1.75 2021/10/22 12:30:53 bluhm Exp $ */
+/* $OpenBSD: pfkeyv2_convert.c,v 1.76 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@keromytis.org)
*
@@ -299,8 +299,9 @@ import_lifetime(struct tdb *tdb, struct sadb_lifetime *sadb_lifetime, int type)
if ((tdb->tdb_exp_timeout =
sadb_lifetime->sadb_lifetime_addtime) != 0) {
tdb->tdb_flags |= TDBF_TIMER;
- timeout_add_sec(&tdb->tdb_timer_tmo,
- tdb->tdb_exp_timeout);
+ if (timeout_add_sec(&tdb->tdb_timer_tmo,
+ tdb->tdb_exp_timeout))
+ tdb_ref(tdb);
} else
tdb->tdb_flags &= ~TDBF_TIMER;
@@ -327,8 +328,9 @@ import_lifetime(struct tdb *tdb, struct sadb_lifetime *sadb_lifetime, int type)
if ((tdb->tdb_soft_timeout =
sadb_lifetime->sadb_lifetime_addtime) != 0) {
tdb->tdb_flags |= TDBF_SOFT_TIMER;
- timeout_add_sec(&tdb->tdb_stimer_tmo,
- tdb->tdb_soft_timeout);
+ if (timeout_add_sec(&tdb->tdb_stimer_tmo,
+ tdb->tdb_soft_timeout))
+ tdb_ref(tdb);
} else
tdb->tdb_flags &= ~TDBF_SOFT_TIMER;
diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c
index 4e45911f96a..1304a7ce255 100644
--- a/sys/netinet/ip_ipsp.c
+++ b/sys/netinet/ip_ipsp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.c,v 1.253 2021/11/21 16:17:48 mvs Exp $ */
+/* $OpenBSD: ip_ipsp.c,v 1.254 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -85,7 +85,6 @@ void tdb_hashstats(void);
#endif
int tdb_rehash(void);
-void tdb_reaper(void *);
void tdb_timeout(void *);
void tdb_firstuse(void *);
void tdb_soft_timeout(void *);
@@ -297,9 +296,10 @@ reserve_spi(u_int rdomain, u_int32_t sspi, u_int32_t tspi,
/* Check whether we're using this SPI already. */
exists = gettdb(rdomain, spi, dst, sproto);
- if (exists)
+ if (exists != NULL) {
+ tdb_unref(exists);
continue;
-
+ }
tdbp->tdb_spi = spi;
memcpy(&tdbp->tdb_dst.sa, &dst->sa, dst->sa.sa_len);
@@ -314,8 +314,9 @@ reserve_spi(u_int rdomain, u_int32_t sspi, u_int32_t tspi,
if (ipsec_keep_invalid > 0) {
tdbp->tdb_flags |= TDBF_TIMER;
tdbp->tdb_exp_timeout = ipsec_keep_invalid;
- timeout_add_sec(&tdbp->tdb_timer_tmo,
- ipsec_keep_invalid);
+ if (timeout_add_sec(&tdbp->tdb_timer_tmo,
+ ipsec_keep_invalid))
+ tdb_ref(tdbp);
}
#endif
@@ -351,6 +352,7 @@ gettdb_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *dst,
!memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len))
break;
+ tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
@@ -383,6 +385,7 @@ gettdbbysrcdst_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
break;
if (tdbp != NULL) {
+ tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
@@ -402,6 +405,7 @@ gettdbbysrcdst_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
tdbp->tdb_src.sa.sa_family == AF_UNSPEC)
break;
+ tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
@@ -469,6 +473,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
break;
}
+ tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
@@ -499,6 +504,7 @@ gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
break;
}
+ tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
@@ -548,6 +554,7 @@ tdb_printit(void *addr, int full, int (*pr)(const char *, ...))
DUMP(inext, "%p");
DUMP(onext, "%p");
DUMP(xform, "%p");
+ pr("%18s: %d\n", "refcnt", tdb->tdb_refcnt.refs);
DUMP(encalgxform, "%p");
DUMP(authalgxform, "%p");
DUMP(compalgxform, "%p");
@@ -607,6 +614,7 @@ tdb_printit(void *addr, int full, int (*pr)(const char *, ...))
pr(" %s", ipsp_address(&tdb->tdb_src, buf, sizeof(buf)));
pr("->%s", ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)));
pr(":%d", tdb->tdb_sproto);
+ pr(" #%d", tdb->tdb_refcnt.refs);
pr(" %08x\n", tdb->tdb_flags);
}
}
@@ -658,6 +666,8 @@ tdb_timeout(void *v)
}
tdb_delete(tdb);
}
+ /* decrement refcount of the timeout argument */
+ tdb_unref(tdb);
NET_UNLOCK();
}
@@ -675,6 +685,8 @@ tdb_firstuse(void *v)
}
tdb_delete(tdb);
}
+ /* decrement refcount of the timeout argument */
+ tdb_unref(tdb);
NET_UNLOCK();
}
@@ -689,6 +701,8 @@ tdb_soft_timeout(void *v)
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
tdb->tdb_flags &= ~TDBF_SOFT_TIMER;
}
+ /* decrement refcount of the timeout argument */
+ tdb_unref(tdb);
NET_UNLOCK();
}
@@ -704,6 +718,8 @@ tdb_soft_firstuse(void *v)
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE;
}
+ /* decrement refcount of the timeout argument */
+ tdb_unref(tdb);
NET_UNLOCK();
}
@@ -894,12 +910,75 @@ tdb_unlink_locked(struct tdb *tdbp)
}
void
+tdb_unbundle(struct tdb *tdbp)
+{
+ if (tdbp->tdb_onext != NULL) {
+ if (tdbp->tdb_onext->tdb_inext == tdbp) {
+ tdb_unref(tdbp); /* to us */
+ tdbp->tdb_onext->tdb_inext = NULL;
+ }
+ tdb_unref(tdbp->tdb_onext); /* to other */
+ tdbp->tdb_onext = NULL;
+ }
+ if (tdbp->tdb_inext != NULL) {
+ if (tdbp->tdb_inext->tdb_onext == tdbp) {
+ tdb_unref(tdbp); /* to us */
+ tdbp->tdb_inext->tdb_onext = NULL;
+ }
+ tdb_unref(tdbp->tdb_inext); /* to other */
+ tdbp->tdb_inext = NULL;
+ }
+}
+
+void
+tdb_deltimeouts(struct tdb *tdbp)
+{
+ if (timeout_del(&tdbp->tdb_timer_tmo))
+ tdb_unref(tdbp);
+ if (timeout_del(&tdbp->tdb_first_tmo))
+ tdb_unref(tdbp);
+ if (timeout_del(&tdbp->tdb_stimer_tmo))
+ tdb_unref(tdbp);
+ if (timeout_del(&tdbp->tdb_sfirst_tmo))
+ tdb_unref(tdbp);
+}
+
+struct tdb *
+tdb_ref(struct tdb *tdb)
+{
+ if (tdb == NULL)
+ return NULL;
+ refcnt_take(&tdb->tdb_refcnt);
+ return tdb;
+}
+
+void
+tdb_unref(struct tdb *tdb)
+{
+ if (tdb == NULL)
+ return;
+ if (refcnt_rele(&tdb->tdb_refcnt) == 0)
+ return;
+ tdb_free(tdb);
+}
+
+void
tdb_delete(struct tdb *tdbp)
{
+ /* keep in sync with pfkeyv2_sa_flush() */
NET_ASSERT_LOCKED();
+ if (tdbp->tdb_flags & TDBF_DELETED)
+ return;
+ tdbp->tdb_flags |= TDBF_DELETED;
+
tdb_unlink(tdbp);
- tdb_free(tdbp);
+ /* release tdb_onext/tdb_inext references */
+ tdb_unbundle(tdbp);
+ /* delete timeouts and release references */
+ tdb_deltimeouts(tdbp);
+ /* release the reference for tdb_unlink() */
+ tdb_unref(tdbp);
}
/*
@@ -914,6 +993,7 @@ tdb_alloc(u_int rdomain)
tdbp = pool_get(&tdb_pool, PR_WAITOK | PR_ZERO);
+ refcnt_init(&tdbp->tdb_refcnt);
TAILQ_INIT(&tdbp->tdb_policy_head);
/* Record establishment time. */
@@ -950,9 +1030,9 @@ tdb_free(struct tdb *tdbp)
#endif
/* Cleanup SPD references. */
- for (ipo = TAILQ_FIRST(&tdbp->tdb_policy_head); ipo;
- ipo = TAILQ_FIRST(&tdbp->tdb_policy_head)) {
+ while ((ipo = TAILQ_FIRST(&tdbp->tdb_policy_head)) != NULL) {
TAILQ_REMOVE(&tdbp->tdb_policy_head, ipo, ipo_tdb_next);
+ tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0; /* Force a re-search. */
}
@@ -969,28 +1049,16 @@ tdb_free(struct tdb *tdbp)
}
#endif
- if ((tdbp->tdb_onext) && (tdbp->tdb_onext->tdb_inext == tdbp))
- tdbp->tdb_onext->tdb_inext = NULL;
-
- if ((tdbp->tdb_inext) && (tdbp->tdb_inext->tdb_onext == tdbp))
- tdbp->tdb_inext->tdb_onext = NULL;
+ KASSERT(tdbp->tdb_onext == NULL);
+ KASSERT(tdbp->tdb_inext == NULL);
/* Remove expiration timeouts. */
tdbp->tdb_flags &= ~(TDBF_FIRSTUSE | TDBF_SOFT_FIRSTUSE | TDBF_TIMER |
TDBF_SOFT_TIMER);
- timeout_del(&tdbp->tdb_timer_tmo);
- timeout_del(&tdbp->tdb_first_tmo);
- timeout_del(&tdbp->tdb_stimer_tmo);
- timeout_del(&tdbp->tdb_sfirst_tmo);
-
- timeout_set_proc(&tdbp->tdb_timer_tmo, tdb_reaper, tdbp);
- timeout_add(&tdbp->tdb_timer_tmo, 0);
-}
-
-void
-tdb_reaper(void *xtdbp)
-{
- struct tdb *tdbp = xtdbp;
+ KASSERT(timeout_pending(&tdbp->tdb_timer_tmo) == 0);
+ KASSERT(timeout_pending(&tdbp->tdb_first_tmo) == 0);
+ KASSERT(timeout_pending(&tdbp->tdb_stimer_tmo) == 0);
+ KASSERT(timeout_pending(&tdbp->tdb_sfirst_tmo) == 0);
pool_put(&tdb_pool, tdbp);
}
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index 65bcf73eb3b..46fd13b610b 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.h,v 1.221 2021/11/21 16:17:48 mvs Exp $ */
+/* $OpenBSD: ip_ipsp.h,v 1.222 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -324,6 +324,8 @@ struct tdb { /* tunnel descriptor block */
struct tdb *tdb_inext;
struct tdb *tdb_onext;
+ struct refcnt tdb_refcnt;
+
const struct xformsw *tdb_xform; /* Transform to use */
const struct enc_xform *tdb_encalgxform; /* Enc algorithm */
const struct auth_hash *tdb_authalgxform; /* Auth algorithm */
@@ -335,6 +337,7 @@ struct tdb { /* tunnel descriptor block */
#define TDBF_ALLOCATIONS 0x00008 /* Check the flows counters */
#define TDBF_INVALID 0x00010 /* This SPI is not valid yet/anymore */
#define TDBF_FIRSTUSE 0x00020 /* Expire after first use */
+#define TDBF_DELETED 0x00040 /* This TDB has already been deleted */
#define TDBF_SOFT_TIMER 0x00080 /* Soft expiration */
#define TDBF_SOFT_BYTES 0x00100 /* Soft expiration */
#define TDBF_SOFT_ALLOCATIONS 0x00200 /* Soft expiration */
@@ -349,7 +352,7 @@ struct tdb { /* tunnel descriptor block */
#define TDBF_BITS ("\20" \
"\1UNIQUE\2TIMER\3BYTES\4ALLOCATIONS" \
- "\5INVALID\6FIRSTUSE\10SOFT_TIMER" \
+ "\5INVALID\6FIRSTUSE\7DELETED\10SOFT_TIMER" \
"\11SOFT_BYTES\12SOFT_ALLOCATIONS\13SOFT_FIRSTUSE\14PFS" \
"\15TUNNELING" \
"\21USEDTUNNEL\22UDPENCAP\23PFSYNC\24PFSYNC_RPL" \
@@ -564,10 +567,14 @@ struct tdb *gettdbbysrcdst_dir(u_int, u_int32_t, union sockaddr_union *,
void puttdb(struct tdb *);
void tdb_delete(struct tdb *);
struct tdb *tdb_alloc(u_int);
+struct tdb *tdb_ref(struct tdb *);
+void tdb_unref(struct tdb *);
void tdb_free(struct tdb *);
int tdb_init(struct tdb *, u_int16_t, struct ipsecinit *);
void tdb_unlink(struct tdb *);
void tdb_unlink_locked(struct tdb *);
+void tdb_unbundle(struct tdb *);
+void tdb_deltimeouts(struct tdb *);
int tdb_walk(u_int, int (*)(struct tdb *, void *, int), void *);
void tdb_printit(void *, int, int (*)(const char *, ...));
diff --git a/sys/netinet/ip_spd.c b/sys/netinet/ip_spd.c
index afd7586cb1e..a213f0ebe57 100644
--- a/sys/netinet/ip_spd.c
+++ b/sys/netinet/ip_spd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_spd.c,v 1.104 2021/07/08 16:39:55 mvs Exp $ */
+/* $OpenBSD: ip_spd.c,v 1.105 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
*
@@ -368,9 +368,11 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
}
/* Do we have a cached entry ? If so, check if it's still valid. */
- if ((ipo->ipo_tdb) && (ipo->ipo_tdb->tdb_flags & TDBF_INVALID)) {
+ 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;
}
@@ -398,7 +400,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
ids = ipsp_ids_lookup(ipsecflowinfo);
/* Check that the cached TDB (if present), is appropriate. */
- if (ipo->ipo_tdb) {
+ 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,
@@ -420,6 +422,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
/* Cached TDB was not good. */
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;
}
@@ -439,14 +442,14 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
ipo->ipo_last_searched = getuptime();
/* Find an appropriate SA from the existing ones. */
- ipo->ipo_tdb =
- gettdbbydst(rdomain,
- dignore ? &sdst : &ipo->ipo_dst,
- ipo->ipo_sproto,
- ids ? ids: 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 = gettdbbydst(rdomain,
+ dignore ? &sdst : &ipo->ipo_dst,
+ ipo->ipo_sproto, ids ? ids: ipo->ipo_ids,
+ &ipo->ipo_addr, &ipo->ipo_mask);
+ if (ipo->ipo_tdb != NULL) {
+ /* gettdbbydst() has already refcounted tdb */
+ TAILQ_INSERT_TAIL(
+ &ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
*error = 0;
return ipsp_spd_inp(m, af, hlen, error,
@@ -520,10 +523,12 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
goto nomatchin;
/* Add it to the cache. */
- if (ipo->ipo_tdb)
+ if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
- ipo->ipo_tdb = tdbp;
+ tdb_unref(ipo->ipo_tdb);
+ }
+ ipo->ipo_tdb = tdb_ref(tdbp);
TAILQ_INSERT_TAIL(&tdbp->tdb_policy_head, ipo,
ipo_tdb_next);
*error = 0;
@@ -535,7 +540,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
}
/* Check whether cached entry applies. */
- if (ipo->ipo_tdb) {
+ if (ipo->ipo_tdb != NULL) {
/*
* We only need to check that the correct
* security protocol and security gateway are
@@ -551,8 +556,9 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
/* Not applicable, unlink. */
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
- ipo->ipo_last_searched = 0;
+ tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
+ ipo->ipo_last_searched = 0;
}
/* Find whether there exists an appropriate SA. */
@@ -560,14 +566,16 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
if (dignore == 0)
ipo->ipo_last_searched = getuptime();
- ipo->ipo_tdb =
- gettdbbysrc(rdomain,
- dignore ? &ssrc : &ipo->ipo_dst,
- 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 = gettdbbysrc(rdomain,
+ dignore ? &ssrc : &ipo->ipo_dst,
+ ipo->ipo_sproto, ipo->ipo_ids,
+ &ipo->ipo_addr, &ipo->ipo_mask);
+ if (ipo->ipo_tdb != NULL) {
+ /* gettdbbysrc() has already refcounted tdb */
+ TAILQ_INSERT_TAIL(
+ &ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
+ }
}
skipinputsearch:
@@ -637,9 +645,12 @@ ipsec_delete_policy(struct ipsec_policy *ipo)
rn_delete(&ipo->ipo_addr, &ipo->ipo_mask, rnh, rn) == NULL)
return (ESRCH);
- if (ipo->ipo_tdb != 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 = NULL;
+ }
while ((ipa = TAILQ_FIRST(&ipo->ipo_acquires)) != NULL)
ipsp_delete_acquire(ipa);
diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c
index 0ca5cf07f52..9bc0d37d455 100644
--- a/sys/netinet/ipsec_input.c
+++ b/sys/netinet/ipsec_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec_input.c,v 1.192 2021/11/21 02:54:56 bluhm Exp $ */
+/* $OpenBSD: ipsec_input.c,v 1.193 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
@@ -328,12 +328,16 @@ ipsec_common_input(struct mbuf **mp, int skip, int protoff, int af, int sproto,
/* Register first use, setup expiration timer. */
if (tdbp->tdb_first_use == 0) {
tdbp->tdb_first_use = gettime();
- if (tdbp->tdb_flags & TDBF_FIRSTUSE)
- timeout_add_sec(&tdbp->tdb_first_tmo,
- tdbp->tdb_exp_first_use);
- if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE)
- timeout_add_sec(&tdbp->tdb_sfirst_tmo,
- tdbp->tdb_soft_first_use);
+ if (tdbp->tdb_flags & TDBF_FIRSTUSE) {
+ if (timeout_add_sec(&tdbp->tdb_first_tmo,
+ tdbp->tdb_exp_first_use))
+ tdb_ref(tdbp);
+ }
+ if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE) {
+ if (timeout_add_sec(&tdbp->tdb_sfirst_tmo,
+ tdbp->tdb_soft_first_use))
+ tdb_ref(tdbp);
+ }
}
tdbp->tdb_ipackets++;
@@ -348,6 +352,7 @@ ipsec_common_input(struct mbuf **mp, int skip, int protoff, int af, int sproto,
ipsecstat_inc(ipsec_idrops);
tdbp->tdb_idrops++;
}
+ tdb_unref(tdbp);
return prot;
drop:
@@ -355,6 +360,7 @@ ipsec_common_input(struct mbuf **mp, int skip, int protoff, int af, int sproto,
ipsecstat_inc(ipsec_idrops);
if (tdbp != NULL)
tdbp->tdb_idrops++;
+ tdb_unref(tdbp);
return IPPROTO_DONE;
}
@@ -938,6 +944,7 @@ ipsec_common_ctlinput(u_int rdomain, int cmd, struct sockaddr *sa,
tdbp = gettdb_rev(rdomain, spi, (union sockaddr_union *)&dst,
proto);
ipsec_set_mtu(tdbp, mtu);
+ tdb_unref(tdbp);
}
}
@@ -945,7 +952,7 @@ void
udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
struct ip *ip = v;
- struct tdb *tdbp;
+ struct tdb *tdbp, *first;
struct icmp *icp;
u_int32_t mtu;
struct sockaddr_in dst, src;
@@ -974,10 +981,9 @@ udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
src.sin_addr.s_addr = ip->ip_src.s_addr;
su_src = (union sockaddr_union *)&src;
- tdbp = gettdbbysrcdst_rev(rdomain, 0, su_src, su_dst,
- IPPROTO_ESP);
+ first = gettdbbysrcdst_rev(rdomain, 0, su_src, su_dst, IPPROTO_ESP);
- for (; tdbp != NULL; tdbp = tdbp->tdb_snext) {
+ for (tdbp = first; tdbp != NULL; tdbp = tdbp->tdb_snext) {
if (tdbp->tdb_sproto == IPPROTO_ESP &&
((tdbp->tdb_flags & (TDBF_INVALID|TDBF_UDPENCAP)) ==
TDBF_UDPENCAP) &&
@@ -986,6 +992,7 @@ udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
ipsec_set_mtu(tdbp, mtu);
}
}
+ tdb_unref(first);
}
void
@@ -1071,6 +1078,7 @@ ipsec_forward_check(struct mbuf *m, int hlen, int af)
} else
tdb = NULL;
ipsp_spd_lookup(m, af, hlen, &error, IPSP_DIRECTION_IN, tdb, NULL, 0);
+ tdb_unref(tdb);
return error;
}
@@ -1143,6 +1151,7 @@ ipsec_local_check(struct mbuf *m, int hlen, int proto, int af)
tdb = NULL;
ipsp_spd_lookup(m, af, hlen, &error, IPSP_DIRECTION_IN,
tdb, NULL, 0);
+ tdb_unref(tdb);
return error;
}
diff --git a/sys/netinet/ipsec_output.c b/sys/netinet/ipsec_output.c
index 43b94b843db..7c8064a240b 100644
--- a/sys/netinet/ipsec_output.c
+++ b/sys/netinet/ipsec_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec_output.c,v 1.91 2021/10/23 15:42:35 tobhe Exp $ */
+/* $OpenBSD: ipsec_output.c,v 1.92 2021/11/25 13:46:02 bluhm Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
*
@@ -139,12 +139,16 @@ ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
*/
if (tdb->tdb_first_use == 0) {
tdb->tdb_first_use = gettime();
- if (tdb->tdb_flags & TDBF_FIRSTUSE)
- timeout_add_sec(&tdb->tdb_first_tmo,
- tdb->tdb_exp_first_use);
- if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE)
- timeout_add_sec(&tdb->tdb_sfirst_tmo,
- tdb->tdb_soft_first_use);
+ if (tdb->tdb_flags & TDBF_FIRSTUSE) {
+ if (timeout_add_sec(&tdb->tdb_first_tmo,
+ tdb->tdb_exp_first_use))
+ tdb_ref(tdb);
+ }
+ if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
+ if (timeout_add_sec(&tdb->tdb_sfirst_tmo,
+ tdb->tdb_soft_first_use))
+ tdb_ref(tdb);
+ }
}
/*
@@ -388,6 +392,7 @@ ipsp_process_done(struct mbuf *m, struct tdb *tdb)
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
+ struct tdb *tdbo;
struct tdb_ident *tdbi;
struct m_tag *mtag;
int roff, error;
@@ -501,9 +506,13 @@ ipsp_process_done(struct mbuf *m, struct tdb *tdb)
tdb->tdb_obytes += m->m_pkthdr.len;
/* If there's another (bundled) TDB to apply, do so. */
- if (tdb->tdb_onext)
- return ipsp_process_packet(m, tdb->tdb_onext,
+ tdbo = tdb_ref(tdb->tdb_onext);
+ if (tdbo != NULL) {
+ error = ipsp_process_packet(m, tdbo,
tdb->tdb_dst.sa.sa_family, 0);
+ tdb_unref(tdbo);
+ return error;
+ }
#if NPF > 0
/* Add pf tag if requested. */
@@ -615,13 +624,16 @@ ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
if (tdbp == NULL)
break;
- if ((adjust = ipsec_hdrsz(tdbp)) == -1)
+ if ((adjust = ipsec_hdrsz(tdbp)) == -1) {
+ tdb_unref(tdbp);
break;
+ }
mtu -= adjust;
tdbp->tdb_mtu = mtu;
tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
+ tdb_unref(tdbp);
}
}
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 288cfa87706..9b9c8390f4b 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_input.c,v 1.370 2021/08/09 17:03:08 bluhm Exp $ */
+/* $OpenBSD: tcp_input.c,v 1.371 2021/11/25 13:46:02 bluhm Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
@@ -380,12 +380,6 @@ tcp_input(struct mbuf **mp, int *offp, int proto, int af)
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
#endif /* INET6 */
-#ifdef IPSEC
- struct m_tag *mtag;
- struct tdb_ident *tdbi;
- struct tdb *tdb;
- int error;
-#endif /* IPSEC */
#ifdef TCP_ECN
u_char iptos;
#endif
@@ -571,16 +565,22 @@ findpcb:
}
#ifdef IPSEC
if (ipsec_in_use) {
+ struct m_tag *mtag;
+ struct tdb *tdb = NULL;
+ int error;
+
/* Find most recent IPsec tag */
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
if (mtag != NULL) {
+ struct tdb_ident *tdbi;
+
tdbi = (struct tdb_ident *)(mtag + 1);
tdb = gettdb(tdbi->rdomain, tdbi->spi,
&tdbi->dst, tdbi->proto);
- } else
- tdb = NULL;
+ }
ipsp_spd_lookup(m, af, iphlen, &error, IPSP_DIRECTION_IN,
tdb, inp, 0);
+ tdb_unref(tdb);
if (error) {
tcpstat_inc(tcps_rcvnosec);
goto drop;
@@ -2197,7 +2197,7 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcphdr *th,
continue;
if (sigp && timingsafe_bcmp(sigp, cp + 2, 16))
- return (-1);
+ goto bad;
sigp = cp + 2;
break;
@@ -2248,7 +2248,7 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcphdr *th,
if ((sigp ? TF_SIGNATURE : 0) ^ (tp->t_flags & TF_SIGNATURE)) {
tcpstat_inc(tcps_rcvbadsig);
- return (-1);
+ goto bad;
}
if (sigp) {
@@ -2256,22 +2256,30 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcphdr *th,
if (tdb == NULL) {
tcpstat_inc(tcps_rcvbadsig);
- return (-1);
+ goto bad;
}
if (tcp_signature(tdb, tp->pf, m, th, iphlen, 1, sig) < 0)
- return (-1);
+ goto bad;
if (timingsafe_bcmp(sig, sigp, 16)) {
tcpstat_inc(tcps_rcvbadsig);
- return (-1);
+ goto bad;
}
tcpstat_inc(tcps_rcvgoodsig);
}
+
+ tdb_unref(tdb);
#endif /* TCP_SIGNATURE */
return (0);
+
+ bad:
+#ifdef TCP_SIGNATURE
+ tdb_unref(tdb);
+#endif /* TCP_SIGNATURE */
+ return (-1);
}
u_long
@@ -4056,8 +4064,10 @@ syn_cache_respond(struct syn_cache *sc, struct mbuf *m)
if (tcp_signature(tdb, sc->sc_src.sa.sa_family, m, th,
hlen, 0, optp) < 0) {
m_freem(m);
+ tdb_unref(tdb);
return (EINVAL);
}
+ tdb_unref(tdb);
optp += 16;
/* Pad options list to the next 32 bit boundary and
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 8d603d35699..faaec77ce2f 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_output.c,v 1.130 2021/02/08 19:37:15 jan Exp $ */
+/* $OpenBSD: tcp_output.c,v 1.131 2021/11/25 13:46:02 bluhm Exp $ */
/* $NetBSD: tcp_output.c,v 1.16 1997/06/03 16:17:09 kml Exp $ */
/*
@@ -879,8 +879,10 @@ send:
if (tcp_signature(tdb, tp->pf, m, th, iphlen, 0,
mtod(m, caddr_t) + hdrlen - optlen + sigoff) < 0) {
m_freem(m);
+ tdb_unref(tdb);
return (EINVAL);
}
+ tdb_unref(tdb);
}
#endif /* TCP_SIGNATURE */
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 098ee1401b9..f3d50ae83b9 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_usrreq.c,v 1.264 2021/11/11 18:08:18 bluhm Exp $ */
+/* $OpenBSD: udp_usrreq.c,v 1.265 2021/11/25 13:46:02 bluhm Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
@@ -514,11 +514,13 @@ udp_input(struct mbuf **mp, int *offp, int proto, int af)
IPSP_DIRECTION_IN, tdb, inp, 0);
if (error) {
udpstat_inc(udps_nosec);
+ tdb_unref(tdb);
goto bad;
}
/* create ipsec options while we know that tdb cannot be modified */
if (tdb && tdb->tdb_ids)
ipsecflowinfo = tdb->tdb_ids->id_flow;
+ tdb_unref(tdb);
}
#endif /*IPSEC */