diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2021-11-25 13:46:03 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2021-11-25 13:46:03 +0000 |
commit | e8a983f68fa2f49fead18f84db69a87c02eb6746 (patch) | |
tree | d1f96609775698f0a110ff2ebdd94b0c815a834a | |
parent | 90e12edf80cdcf876423da2d6d69d69128ffccd5 (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.c | 22 | ||||
-rw-r--r-- | sys/net/if_pfsync.c | 4 | ||||
-rw-r--r-- | sys/net/pfkeyv2.c | 51 | ||||
-rw-r--r-- | sys/net/pfkeyv2_convert.c | 12 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 122 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 11 | ||||
-rw-r--r-- | sys/netinet/ip_spd.c | 57 | ||||
-rw-r--r-- | sys/netinet/ipsec_input.c | 31 | ||||
-rw-r--r-- | sys/netinet/ipsec_output.c | 32 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 38 | ||||
-rw-r--r-- | sys/netinet/tcp_output.c | 4 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 4 |
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 */ |