Module Name: src Committed By: martin Date: Wed Apr 18 14:06:24 UTC 2018
Modified Files: src/sys/netipsec [netbsd-8]: key.c keydb.h Log Message: Pull up following revision(s) (requested by yamaguchi in ticket #776): sys/netipsec/key.c: revision 1.251-1.253 sys/netipsec/keydb.h: revision 1.22 Introduced a hash table to sahlist An saidx of sah included in the list is unique so that the search can use a hash list whose hash is calculated by the saidx to find an sah quickly. The hash list of the sahlits is used in FreeBSD, too. reviewed by ozaki-r@n.o, thanks. Added a lookup table to find an sav quickly key_sad.sahlists doesn't work well for inbound packets because its key includes source address. For the reason, the look-up-table for the inbound packets is newly added. The table has all sav whose state is MATURE or DYING and uses a key calculated by destination address, protocol, and spi instead of saidx. reviewd ozaki-r@n.o, thanks. Fix panic of SADB when the state of sav is changed in timeout pointed out by ozaki-r@n.o, thanks To generate a diff of this commit: cvs rdiff -u -r1.163.2.8 -r1.163.2.9 src/sys/netipsec/key.c cvs rdiff -u -r1.15.2.2 -r1.15.2.3 src/sys/netipsec/keydb.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netipsec/key.c diff -u src/sys/netipsec/key.c:1.163.2.8 src/sys/netipsec/key.c:1.163.2.9 --- src/sys/netipsec/key.c:1.163.2.8 Mon Apr 16 14:31:44 2018 +++ src/sys/netipsec/key.c Wed Apr 18 14:06:24 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: key.c,v 1.163.2.8 2018/04/16 14:31:44 martin Exp $ */ +/* $NetBSD: key.c,v 1.163.2.9 2018/04/18 14:06:24 martin Exp $ */ /* $FreeBSD: src/sys/netipsec/key.c,v 1.3.2.3 2004/02/14 22:23:23 bms Exp $ */ /* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */ @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.163.2.8 2018/04/16 14:31:44 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.163.2.9 2018/04/18 14:06:24 martin Exp $"); /* * This code is referred to RFC 2367 @@ -72,6 +72,7 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.16 #include <sys/condvar.h> #include <sys/localcount.h> #include <sys/pserialize.h> +#include <sys/hash.h> #include <net/if.h> #include <net/route.h> @@ -122,6 +123,14 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.16 #define PORT_LOOSE 1 #define PORT_STRICT 2 +#ifndef SAHHASH_NHASH +#define SAHHASH_NHASH 128 +#endif + +#ifndef SAVLUT_NHASH +#define SAVLUT_NHASH 128 +#endif + percpu_t *pfkeystat_percpu; /* @@ -204,20 +213,23 @@ static u_int32_t acq_seq = 0; /* * Locking notes on SAD: * - Data structures - * - SAs are managed by the list called key_sad.sahlist and sav lists of sah - * entries + * - SAs are managed by the list called key_sad.sahlists and sav lists of + * sah entries * - An sav is supposed to be an SA from a viewpoint of users * - A sah has sav lists for each SA state - * - Multiple sahs with the same saidx can exist + * - Multiple saves with the same saidx can exist * - Only one entry has MATURE state and others should be DEAD * - DEAD entries are just ignored from searching - * - Modifications to the key_sad.sahlist and sah.savlist must be done with - * holding key_sad.lock which is a adaptive mutex - * - Read accesses to the key_sad.sahlist and sah.savlist must be in - * pserialize(9) read sections + * - All sav whose state is MATURE or DYING are registered to the lookup + * table called key_sad.savlut in addition to the savlists. + * - The table is used to search an sav without use of saidx. + * - Modifications to the key_sad.sahlists, sah.savlist and key_sad.savlut + * must be done with holding key_sad.lock which is a adaptive mutex + * - Read accesses to the key_sad.sahlists, sah.savlist and key_sad.savlut + * must be in pserialize(9) read sections * - sah's lifetime is managed by localcount(9) * - Getting an sah entry - * - We get an sah from the key_sad.sahlist + * - We get an sah from the key_sad.sahlists * - Must iterate the list and increment the reference count of a found sah * (by key_sah_ref) in a pserialize read section * - A gotten sah must be released after use by key_sah_unref @@ -261,7 +273,10 @@ static struct { static struct { kmutex_t lock; kcondvar_t cv_lc; - struct pslist_head sahlist; + struct pslist_head *sahlists; + u_long sahlistmask; + struct pslist_head *savlut; + u_long savlutmask; pserialize_t psz; kcondvar_t cv_psz; @@ -341,13 +356,23 @@ static struct { #define SAHLIST_WRITER_REMOVE(sah) \ PSLIST_WRITER_REMOVE((sah), pslist_entry) #define SAHLIST_READER_FOREACH(sah) \ - PSLIST_READER_FOREACH((sah), &key_sad.sahlist, struct secashead,\ - pslist_entry) + for(int _i_sah = 0; _i_sah <= key_sad.sahlistmask; _i_sah++) \ + PSLIST_READER_FOREACH((sah), &key_sad.sahlists[_i_sah], \ + struct secashead, pslist_entry) +#define SAHLIST_READER_FOREACH_SAIDX(sah, saidx) \ + PSLIST_READER_FOREACH((sah), \ + &key_sad.sahlists[key_saidxhash((saidx), \ + key_sad.sahlistmask)], \ + struct secashead, pslist_entry) #define SAHLIST_WRITER_FOREACH(sah) \ - PSLIST_WRITER_FOREACH((sah), &key_sad.sahlist, struct secashead,\ - pslist_entry) + for(int _i_sah = 0; _i_sah <= key_sad.sahlistmask; _i_sah++) \ + PSLIST_WRITER_FOREACH((sah), &key_sad.sahlists[_i_sah], \ + struct secashead, pslist_entry) #define SAHLIST_WRITER_INSERT_HEAD(sah) \ - PSLIST_WRITER_INSERT_HEAD(&key_sad.sahlist, (sah), pslist_entry) + PSLIST_WRITER_INSERT_HEAD( \ + &key_sad.sahlists[key_saidxhash(&(sah)->saidx, \ + key_sad.sahlistmask)], \ + (sah), pslist_entry) /* Macros for key_sad.sahlist#savlist */ #define SAVLIST_ENTRY_INIT(sav) \ @@ -395,6 +420,23 @@ static struct { #define SAVLIST_READER_NEXT(sav) \ PSLIST_READER_NEXT((sav), struct secasvar, pslist_entry) +/* Macros for key_sad.savlut */ +#define SAVLUT_ENTRY_INIT(sav) \ + PSLIST_ENTRY_INIT((sav), pslist_entry_savlut) +#define SAVLUT_READER_FOREACH(sav, dst, proto, hash_key) \ + PSLIST_READER_FOREACH((sav), \ + &key_sad.savlut[key_savluthash(dst, proto, hash_key, \ + key_sad.savlutmask)], \ + struct secasvar, pslist_entry_savlut) +#define SAVLUT_WRITER_INSERT_HEAD(sav) \ + key_savlut_writer_insert_head((sav)) +#define SAVLUT_WRITER_REMOVE(sav) \ + do { \ + if (!(sav)->savlut_added) \ + break; \ + PSLIST_WRITER_REMOVE((sav), pslist_entry_savlut); \ + (sav)->savlut_added = false; \ + } while(0) /* search order for SAs */ /* @@ -794,6 +836,14 @@ static struct callout key_timehandler_ch static struct workqueue *key_timehandler_wq; static struct work key_timehandler_wk; +static inline void + key_savlut_writer_insert_head(struct secasvar *sav); +static inline uint32_t + key_saidxhash(const struct secasindex *, u_long); +static inline uint32_t + key_savluthash(const struct sockaddr *, + uint32_t, uint32_t, u_long); + /* * Utilities for percpu counters for sadb_lifetime_allocations and * sadb_lifetime_bytes. @@ -1203,9 +1253,7 @@ key_lookup_sa( u_int16_t dport, const char* where, int tag) { - struct secashead *sah; struct secasvar *sav; - u_int state; int chkport; int s; @@ -1213,6 +1261,7 @@ key_lookup_sa( int must_check_alg = 0; u_int16_t cpi = 0; u_int8_t algo = 0; + uint32_t hash_key = spi; if ((sport != 0) && (dport != 0)) chkport = PORT_STRICT; @@ -1235,6 +1284,7 @@ key_lookup_sa( cpi = (u_int16_t) tmp; if (cpi < IPCOMP_CPI_NEGOTIATE_MIN) { algo = (u_int8_t) cpi; + hash_key = algo; must_check_spi = 0; must_check_alg = 1; } @@ -1251,57 +1301,51 @@ key_lookup_sa( * encrypted so we can't check internal IP header. */ s = pserialize_read_enter(); - SAHLIST_READER_FOREACH(sah) { - /* search valid state */ - SASTATE_USABLE_FOREACH(state) { - SAVLIST_READER_FOREACH(sav, sah, state) { - KEYDEBUG_PRINTF(KEYDEBUG_MATCH, - "try match spi %#x, %#x\n", - ntohl(spi), ntohl(sav->spi)); - /* sanity check */ - KEY_CHKSASTATE(sav->state, state); - /* do not return entries w/ unusable state */ - if (!SADB_SASTATE_USABLE_P(sav)) { - KEYDEBUG_PRINTF(KEYDEBUG_MATCH, - "bad state %d\n", sav->state); - continue; - } - if (proto != sav->sah->saidx.proto) { - KEYDEBUG_PRINTF(KEYDEBUG_MATCH, - "proto fail %d != %d\n", - proto, sav->sah->saidx.proto); - continue; - } - if (must_check_spi && spi != sav->spi) { - KEYDEBUG_PRINTF(KEYDEBUG_MATCH, - "spi fail %#x != %#x\n", - ntohl(spi), ntohl(sav->spi)); - continue; - } - /* XXX only on the ipcomp case */ - if (must_check_alg && algo != sav->alg_comp) { - KEYDEBUG_PRINTF(KEYDEBUG_MATCH, - "algo fail %d != %d\n", - algo, sav->alg_comp); - continue; - } + SAVLUT_READER_FOREACH(sav, &dst->sa, proto, hash_key) { + KEYDEBUG_PRINTF(KEYDEBUG_MATCH, + "try match spi %#x, %#x\n", + ntohl(spi), ntohl(sav->spi)); + + /* do not return entries w/ unusable state */ + if (!SADB_SASTATE_USABLE_P(sav)) { + KEYDEBUG_PRINTF(KEYDEBUG_MATCH, + "bad state %d\n", sav->state); + continue; + } + if (proto != sav->sah->saidx.proto) { + KEYDEBUG_PRINTF(KEYDEBUG_MATCH, + "proto fail %d != %d\n", + proto, sav->sah->saidx.proto); + continue; + } + if (must_check_spi && spi != sav->spi) { + KEYDEBUG_PRINTF(KEYDEBUG_MATCH, + "spi fail %#x != %#x\n", + ntohl(spi), ntohl(sav->spi)); + continue; + } + /* XXX only on the ipcomp case */ + if (must_check_alg && algo != sav->alg_comp) { + KEYDEBUG_PRINTF(KEYDEBUG_MATCH, + "algo fail %d != %d\n", + algo, sav->alg_comp); + continue; + } #if 0 /* don't check src */ /* Fix port in src->sa */ - /* check src address */ - if (!key_sockaddr_match(&src->sa, &sav->sah->saidx.src.sa, PORT_NONE)) - continue; + /* check src address */ + if (!key_sockaddr_match(&src->sa, &sav->sah->saidx.src.sa, PORT_NONE)) + continue; #endif - /* fix port of dst address XXX*/ - key_porttosaddr(__UNCONST(dst), dport); - /* check dst address */ - if (!key_sockaddr_match(&dst->sa, &sav->sah->saidx.dst.sa, chkport)) - continue; - key_sa_ref(sav, where, tag); - goto done; - } - } + /* fix port of dst address XXX*/ + key_porttosaddr(__UNCONST(dst), dport); + /* check dst address */ + if (!key_sockaddr_match(&dst->sa, &sav->sah->saidx.dst.sa, chkport)) + continue; + key_sa_ref(sav, where, tag); + goto done; } sav = NULL; done: @@ -1393,6 +1437,7 @@ key_init_sav(struct secasvar *sav) localcount_init(&sav->localcount); SAVLIST_ENTRY_INIT(sav); + SAVLUT_ENTRY_INIT(sav); } u_int @@ -1531,6 +1576,7 @@ key_unlink_sav(struct secasvar *sav) KASSERT(mutex_owned(&key_sad.lock)); SAVLIST_WRITER_REMOVE(sav); + SAVLUT_WRITER_REMOVE(sav); KDASSERT(mutex_ownable(softnet_lock)); key_sad_pserialize_perform(); @@ -1566,6 +1612,7 @@ key_destroy_sav_with_ref(struct secasvar mutex_enter(&key_sad.lock); sav->state = SADB_SASTATE_DEAD; SAVLIST_WRITER_REMOVE(sav); + SAVLUT_WRITER_REMOVE(sav); mutex_exit(&key_sad.lock); /* We cannot unref with holding key_sad.lock */ @@ -3367,7 +3414,7 @@ key_getsah(const struct secasindex *said { struct secashead *sah; - SAHLIST_READER_FOREACH(sah) { + SAHLIST_READER_FOREACH_SAIDX(sah, saidx) { if (sah->state == SADB_SASTATE_DEAD) continue; if (key_saidx_match(&sah->saidx, saidx, flag)) @@ -5700,6 +5747,7 @@ key_api_update(struct socket *so, struct newsav->state = SADB_SASTATE_MATURE; mutex_enter(&key_sad.lock); SAVLIST_WRITER_INSERT_TAIL(sah, SADB_SASTATE_MATURE, newsav); + SAVLUT_WRITER_INSERT_HEAD(newsav); mutex_exit(&key_sad.lock); key_validate_savlist(sah, SADB_SASTATE_MATURE); @@ -5897,6 +5945,7 @@ key_api_add(struct socket *so, struct mb newsav->state = SADB_SASTATE_MATURE; mutex_enter(&key_sad.lock); SAVLIST_WRITER_INSERT_TAIL(sah, SADB_SASTATE_MATURE, newsav); + SAVLUT_WRITER_INSERT_HEAD(newsav); mutex_exit(&key_sad.lock); key_validate_savlist(sah, SADB_SASTATE_MATURE); @@ -8091,7 +8140,10 @@ key_do_init(void) PSLIST_INIT(&key_spd.socksplist); - PSLIST_INIT(&key_sad.sahlist); + key_sad.sahlists = hashinit(SAHHASH_NHASH, HASH_PSLIST, true, + &key_sad.sahlistmask); + key_sad.savlut = hashinit(SAVLUT_NHASH, HASH_PSLIST, true, + &key_sad.savlutmask); for (i = 0; i <= SADB_SATYPE_MAX; i++) { LIST_INIT(&key_misc.reglist[i]); @@ -8335,6 +8387,9 @@ key_sa_chgstate(struct secasvar *sav, u_ if (_sav == NULL) { SAVLIST_WRITER_INSERT_TAIL(sav->sah, state, sav); } + + SAVLUT_WRITER_INSERT_HEAD(sav); + key_validate_savlist(sav->sah, state); } @@ -8537,6 +8592,99 @@ key_update_used(void) } } +static inline void +key_savlut_writer_insert_head(struct secasvar *sav) +{ + uint32_t hash_key; + uint32_t hash; + + KASSERT(mutex_owned(&key_sad.lock)); + KASSERT(!sav->savlut_added); + + if (sav->sah->saidx.proto == IPPROTO_IPCOMP) + hash_key = sav->alg_comp; + else + hash_key = sav->spi; + + hash = key_savluthash(&sav->sah->saidx.dst.sa, + sav->sah->saidx.proto, hash_key, key_sad.savlutmask); + + PSLIST_WRITER_INSERT_HEAD(&key_sad.savlut[hash], sav, + pslist_entry_savlut); + sav->savlut_added = true; +} + +/* + * Calculate hash using protocol, source address, + * and destination address included in saidx. + */ +static inline uint32_t +key_saidxhash(const struct secasindex *saidx, u_long mask) +{ + uint32_t hash32; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + + hash32 = saidx->proto; + + switch (saidx->src.sa.sa_family) { + case AF_INET: + sin = &saidx->src.sin; + hash32 = hash32_buf(&sin->sin_addr, + sizeof(sin->sin_addr), hash32); + sin = &saidx->dst.sin; + hash32 = hash32_buf(&sin->sin_addr, + sizeof(sin->sin_addr), hash32 << 1); + break; + case AF_INET6: + sin6 = &saidx->src.sin6; + hash32 = hash32_buf(&sin6->sin6_addr, + sizeof(sin6->sin6_addr), hash32); + sin6 = &saidx->dst.sin6; + hash32 = hash32_buf(&sin6->sin6_addr, + sizeof(sin6->sin6_addr), hash32 << 1); + break; + default: + hash32 = 0; + break; + } + + return hash32 & mask; +} + +/* + * Calculate hash using destination address, protocol, + * and spi. Those parameter depend on the search of + * key_lookup_sa(). + */ +static uint32_t +key_savluthash(const struct sockaddr *dst, uint32_t proto, + uint32_t spi, u_long mask) +{ + uint32_t hash32; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + + hash32 = hash32_buf(&proto, sizeof(proto), spi); + + switch(dst->sa_family) { + case AF_INET: + sin = satocsin(dst); + hash32 = hash32_buf(&sin->sin_addr, + sizeof(sin->sin_addr), hash32); + break; + case AF_INET6: + sin6 = satocsin6(dst); + hash32 = hash32_buf(&sin6->sin6_addr, + sizeof(sin6->sin6_addr), hash32); + break; + default: + hash32 = 0; + } + + return hash32 & mask; +} + static int sysctl_net_key_dumpsa(SYSCTLFN_ARGS) { Index: src/sys/netipsec/keydb.h diff -u src/sys/netipsec/keydb.h:1.15.2.2 src/sys/netipsec/keydb.h:1.15.2.3 --- src/sys/netipsec/keydb.h:1.15.2.2 Wed Mar 7 13:46:41 2018 +++ src/sys/netipsec/keydb.h Wed Apr 18 14:06:24 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: keydb.h,v 1.15.2.2 2018/03/07 13:46:41 martin Exp $ */ +/* $NetBSD: keydb.h,v 1.15.2.3 2018/04/18 14:06:24 martin Exp $ */ /* $FreeBSD: src/sys/netipsec/keydb.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */ /* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane Exp $ */ @@ -95,7 +95,9 @@ struct comp_algo; /* Security Association */ struct secasvar { struct pslist_entry pslist_entry; + struct pslist_entry pslist_entry_savlut; struct localcount localcount; /* reference count */ + bool savlut_added; /* Status of registration of the LUT */ u_int8_t state; /* Status of this Association */