Each tdb (SA) bundle will receive a "flow" identificator that will
be reassigned to the newly established SAs upon rekeying. Later
this will be passed as IP_IPSECFLOWINFO control message to userland.
Discussed with with Markus and Bret Lambert. OK?
---
sys/netinet/ip_ipsp.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++-
sys/netinet/ip_ipsp.h | 3 ++
2 files changed, 103 insertions(+), 1 deletion(-)
diff --git sys/netinet/ip_ipsp.c sys/netinet/ip_ipsp.c
index 184c28f..55c3871 100644
--- sys/netinet/ip_ipsp.c
+++ sys/netinet/ip_ipsp.c
@@ -118,24 +118,26 @@ struct xformsw xformsw[] = {
tcp_signature_tdb_zeroize, tcp_signature_tdb_input,
tcp_signature_tdb_output, }
#endif /* TCP_SIGNATURE */
};
struct xformsw *xformswNXFORMSW = &xformsw[nitems(xformsw)];
#define TDB_HASHSIZE_INIT 32
static struct tdb **tdbh = NULL;
+static struct tdb **tdbflow = NULL;
static struct tdb **tdbdst = NULL;
static struct tdb **tdbsrc = NULL;
static u_int tdb_hashmask = TDB_HASHSIZE_INIT - 1;
static int tdb_count;
+static u_int tdb_flow;
/*
* Our hashing function needs to stir things with a non-zero random multiplier
* so we cannot be DoS-attacked via choosing of the data to hash.
*/
int
tdb_hash(u_int rdomain, u_int32_t spi, union sockaddr_union *dst,
u_int8_t proto)
{
static u_int32_t mult1 = 0, mult2 = 0;
@@ -286,20 +288,41 @@ gettdb(u_int rdomain, u_int32_t spi, union sockaddr_union
*dst, u_int8_t proto)
for (tdbp = tdbh[hashval]; tdbp != NULL; tdbp = tdbp->tdb_hnext)
if ((tdbp->tdb_spi == spi) && (tdbp->tdb_sproto == proto) &&
(tdbp->tdb_rdomain == rdomain) &&
!memcmp(&tdbp->tdb_dst, dst, SA_LEN(&dst->sa)))
break;
return tdbp;
}
+struct tdb *
+gettdbbyflow(u_int rdomain, u_int32_t flow, union sockaddr_union *dst,
+ u_int8_t proto)
+{
+ u_int32_t hashval;
+ struct tdb *tdbp;
+
+ if (tdbflow == NULL)
+ return (NULL);
+
+ hashval = tdb_hash(rdomain, flow, dst, proto);
+
+ for (tdbp = tdbflow[hashval]; tdbp != NULL; tdbp = tdbp->tdb_hnext)
+ if ((tdbp->tdb_flow == flow) && (tdbp->tdb_sproto == proto) &&
+ (tdbp->tdb_rdomain == rdomain) &&
+ !memcmp(&tdbp->tdb_dst, dst, SA_LEN(&dst->sa)))
+ break;
+
+ return (tdbp);
+}
+
/*
* Same as gettdb() but compare SRC as well, so we
* use the tdbsrc[] hash table. Setting spi to 0
* matches all SPIs.
*/
struct tdb *
gettdbbysrcdst(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
union sockaddr_union *dst, u_int8_t proto)
{
u_int32_t hashval;
@@ -577,43 +600,55 @@ tdb_soft_firstuse(void *v)
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE;
}
/*
* Caller is responsible for splsoftnet().
*/
void
tdb_rehash(void)
{
- struct tdb **new_tdbh, **new_tdbdst, **new_srcaddr, *tdbp, *tdbnp;
+ struct tdb **new_tdbh, **new_tdbflow, **new_tdbdst, **new_srcaddr;
+ struct tdb *tdbp, *tdbnp;
u_int i, old_hashmask = tdb_hashmask;
u_int32_t hashval;
tdb_hashmask = (tdb_hashmask << 1) | 1;
new_tdbh = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
+ new_tdbflow = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
+ M_WAITOK | M_ZERO);
new_tdbdst = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
new_srcaddr = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
for (i = 0; i <= old_hashmask; i++) {
for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbnp) {
tdbnp = tdbp->tdb_hnext;
hashval = tdb_hash(tdbp->tdb_rdomain,
tdbp->tdb_spi, &tdbp->tdb_dst,
tdbp->tdb_sproto);
tdbp->tdb_hnext = new_tdbh[hashval];
new_tdbh[hashval] = tdbp;
}
+ for (tdbp = tdbflow[i]; tdbp != NULL; tdbp = tdbnp) {
+ tdbnp = tdbp->tdb_fnext;
+ hashval = tdb_hash(tdbp->tdb_rdomain,
+ tdbp->tdb_flow, &tdbp->tdb_dst,
+ tdbp->tdb_sproto);
+ tdbp->tdb_fnext = new_tdbh[hashval];
+ new_tdbflow[hashval] = tdbp;
+ }
+
for (tdbp = tdbdst[i]; tdbp != NULL; tdbp = tdbnp) {
tdbnp = tdbp->tdb_dnext;
hashval = tdb_hash(tdbp->tdb_rdomain,
0, &tdbp->tdb_dst,
tdbp->tdb_sproto);
tdbp->tdb_dnext = new_tdbdst[hashval];
new_tdbdst[hashval] = tdbp;
}
for (tdbp = tdbsrc[i]; tdbp != NULL; tdbp = tdbnp) {
@@ -622,39 +657,45 @@ tdb_rehash(void)
0, &tdbp->tdb_src,
tdbp->tdb_sproto);
tdbp->tdb_snext = new_srcaddr[hashval];
new_srcaddr[hashval] = tdbp;
}
}
free(tdbh, M_TDB, 0);
tdbh = new_tdbh;
+ free(tdbflow, M_TDB, 0);
+ tdbflow = new_tdbflow;
+
free(tdbdst, M_TDB, 0);
tdbdst = new_tdbdst;
free(tdbsrc, M_TDB, 0);
tdbsrc = new_srcaddr;
}
/*
* Add TDB in the hash table.
*/
void
puttdb(struct tdb *tdbp)
{
+ struct tdb *tdbpp;
u_int32_t hashval;
int s = splsoftnet();
if (tdbh == NULL) {
tdbh = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *),
M_TDB, M_WAITOK | M_ZERO);
+ tdbflow = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *),
+ M_TDB, M_WAITOK | M_ZERO);
tdbdst = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *),
M_TDB, M_WAITOK | M_ZERO);
tdbsrc = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *),
M_TDB, M_WAITOK | M_ZERO);
}
hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_spi,
&tdbp->tdb_dst, tdbp->tdb_sproto);
/*
@@ -668,20 +709,61 @@ puttdb(struct tdb *tdbp)
if (tdbh[hashval] != NULL && tdbh[hashval]->tdb_hnext != NULL &&
tdb_count * 10 > tdb_hashmask + 1) {
tdb_rehash();
hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_spi,
&tdbp->tdb_dst, tdbp->tdb_sproto);
}
tdbp->tdb_hnext = tdbh[hashval];
tdbh[hashval] = tdbp;
+ if (ISSET(tdbp->tdb_flags, TDBF_INVALID))
+ goto noflow; /* not ready yet */
+ /*
+ * Try to figure out whether we're rekeying an existing SA
+ * by getting an exact match based on our credentials.
+ */
+ tdbpp = gettdbbydst(tdbp->tdb_rdomain, &tdbp->tdb_dst,
+ tdbp->tdb_sproto, tdbp->tdb_srcid, tdbp->tdb_dstid,
+ tdbp->tdb_local_cred, NULL, NULL);
+ /*
+ * If that fails try to find an SA set up for the opposite
+ * direction; allocate a new flow ID otherwise.
+ */
+ if (tdbpp == NULL || tdbpp->tdb_flow == 0)
+ tdbpp = gettdbbysrc(tdbp->tdb_rdomain, &tdbp->tdb_dst,
+ tdbp->tdb_sproto, tdbp->tdb_srcid, tdbp->tdb_dstid,
+ NULL, NULL);
+ if (tdbpp != NULL && tdbpp->tdb_flow > 0) {
+ tdbp->tdb_flow = tdbpp->tdb_flow;
+ } else {
+ u_int start = tdb_flow;
+
+ while (++tdb_flow != start) {
+ if (tdb_flow == 0)
+ tdb_flow++;
+ if (gettdbbyflow(tdbp->tdb_rdomain, tdb_flow,
+ &tdbp->tdb_dst, tdbp->tdb_sproto) == NULL)
+ break;
+ }
+ if (tdb_flow == start) {
+ printf("puttdb: too many flows\n");
+ goto noflow;
+ }
+ tdbp->tdb_flow = tdb_flow;
+ }
+ hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_flow,
+ &tdbp->tdb_dst, tdbp->tdb_sproto);
+ tdbp->tdb_fnext = tdbflow[hashval];
+ tdbflow[hashval] = tdbp;
+ noflow:
+
hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_dst,
tdbp->tdb_sproto);
tdbp->tdb_dnext = tdbdst[hashval];
tdbdst[hashval] = tdbp;
hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_src,
tdbp->tdb_sproto);
tdbp->tdb_snext = tdbsrc[hashval];
tdbsrc[hashval] = tdbp;
@@ -717,20 +799,37 @@ tdb_delete(struct tdb *tdbp)
tdbpp = tdbpp->tdb_hnext) {
if (tdbpp->tdb_hnext == tdbp) {
tdbpp->tdb_hnext = tdbp->tdb_hnext;
break;
}
}
}
tdbp->tdb_hnext = NULL;
+ hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_flow,
+ &tdbp->tdb_dst, tdbp->tdb_sproto);
+
+ if (tdbflow[hashval] == tdbp) {
+ tdbflow[hashval] = tdbp->tdb_fnext;
+ } else {
+ for (tdbpp = tdbflow[hashval]; tdbpp != NULL;
+ tdbpp = tdbpp->tdb_fnext) {
+ if (tdbpp->tdb_fnext == tdbp) {
+ tdbpp->tdb_fnext = tdbp->tdb_fnext;
+ break;
+ }
+ }
+ }
+
+ tdbp->tdb_fnext = NULL;
+
hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_dst,
tdbp->tdb_sproto);
if (tdbdst[hashval] == tdbp) {
tdbdst[hashval] = tdbp->tdb_dnext;
} else {
for (tdbpp = tdbdst[hashval]; tdbpp != NULL;
tdbpp = tdbpp->tdb_dnext) {
if (tdbpp->tdb_dnext == tdbp) {
tdbpp->tdb_dnext = tdbp->tdb_dnext;
diff --git sys/netinet/ip_ipsp.h sys/netinet/ip_ipsp.h
index 47a5670..e15ed67 100644
--- sys/netinet/ip_ipsp.h
+++ sys/netinet/ip_ipsp.h
@@ -264,20 +264,21 @@ struct ipsec_policy {
struct tdb { /* tunnel descriptor block */
/*
* Each TDB is on three hash tables: one keyed on dst/spi/sproto,
* one keyed on dst/sproto, and one keyed on src/sproto. The first
* is used for finding a specific TDB, the second for finding TDBs
* for outgoing policy matching, and the third for incoming
* policy matching. The following three fields maintain the hash
* queues in those three tables.
*/
struct tdb *tdb_hnext; /* dst/spi/sproto table */
+ struct tdb *tdb_fnext; /* dst/flow/sproto table */
struct tdb *tdb_dnext; /* dst/sproto table */
struct tdb *tdb_snext; /* src/sproto table */
struct tdb *tdb_inext;
struct tdb *tdb_onext;
struct xformsw *tdb_xform; /* Transform to use */
struct enc_xform *tdb_encalgxform; /* Enc algorithm */
struct auth_hash *tdb_authalgxform; /* Auth algorithm */
struct comp_algo *tdb_compalgxform; /* Compression algo */
@@ -325,20 +326,21 @@ struct tdb { /* tunnel
descriptor block */
u_int64_t tdb_exp_first_use; /* Expire if tdb_first_use +
* tdb_exp_first_use <= curtime
*/
u_int64_t tdb_last_used; /* When was this SA last used */
u_int64_t tdb_last_marked;/* Last SKIPCRYPTO status change */
u_int64_t tdb_cryptoid; /* Crypto session ID */
u_int32_t tdb_spi; /* SPI */
+ u_int32_t tdb_flow; /* Flow ID for IP_IPSECFLOWINFO */
u_int16_t tdb_amxkeylen; /* Raw authentication key length */
u_int16_t tdb_emxkeylen; /* Raw encryption key length */
u_int16_t tdb_ivlen; /* IV length */
u_int8_t tdb_sproto; /* IPsec protocol */
u_int8_t tdb_wnd; /* Replay window */
u_int8_t tdb_satype; /* SA type (RFC2367, PF_KEY) */
u_int8_t tdb_updates; /* pfsync update counter */
union sockaddr_union tdb_dst; /* Destination address */
union sockaddr_union tdb_src; /* Source address */
@@ -497,20 +499,21 @@ do {
\
uint8_t get_sa_require(struct inpcb *);
#ifdef ENCDEBUG
const char *ipsp_address(union sockaddr_union, char *, socklen_t);
#endif /* ENCDEBUG */
/* TDB management routines */
void tdb_add_inp(struct tdb *, struct inpcb *, int);
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 *gettdbbyflow(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_ref *,
struct sockaddr_encap *, struct sockaddr_encap *);
struct tdb *gettdbbysrc(u_int, union sockaddr_union *, u_int8_t,
struct ipsec_ref *, struct ipsec_ref *,
struct sockaddr_encap *, struct sockaddr_encap *);
struct tdb *gettdbbysrcdst(u_int, u_int32_t, union sockaddr_union *,
union sockaddr_union *, u_int8_t);
void puttdb(struct tdb *);
void tdb_delete(struct tdb *);
--
2.3.4