Use siphash on some non-critical hashtables just in case
This commit is contained in:
parent
22b24a81d9
commit
e65f05d29a
|
@ -33,7 +33,7 @@
|
|||
v1 ^= key->key[1]; \
|
||||
v0 ^= key->key[0];
|
||||
|
||||
#define POSTAMBLE \
|
||||
#define POSTAMBLE24 \
|
||||
v3 ^= b; \
|
||||
SIPROUND; \
|
||||
SIPROUND; \
|
||||
|
@ -45,6 +45,17 @@
|
|||
SIPROUND; \
|
||||
return (v0 ^ v1) ^ (v2 ^ v3);
|
||||
|
||||
#define POSTAMBLE13 \
|
||||
v3 ^= b; \
|
||||
SIPROUND; \
|
||||
v0 ^= b; \
|
||||
v2 ^= 0xff; \
|
||||
SIPROUND; \
|
||||
SIPROUND; \
|
||||
SIPROUND; \
|
||||
return (v0 ^ v1) ^ (v2 ^ v3);
|
||||
|
||||
|
||||
uint64 siphash(const void *data, size_t len, const siphash_key_t *key) {
|
||||
const uint8 *end = (uint8*)data + len - (len % sizeof(uint64));
|
||||
const uint8 left = len & (sizeof(uint64) - 1);
|
||||
|
@ -66,7 +77,7 @@ uint64 siphash(const void *data, size_t len, const siphash_key_t *key) {
|
|||
case 2: b |= ReadLE16(data); break;
|
||||
case 1: b |= end[0];
|
||||
}
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +92,7 @@ uint64 siphash_1u64(const uint64 first, const siphash_key_t *key)
|
|||
SIPROUND;
|
||||
SIPROUND;
|
||||
v0 ^= first;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +112,7 @@ uint64 siphash_2u64(const uint64 first, const uint64 second, const siphash_key_t
|
|||
SIPROUND;
|
||||
SIPROUND;
|
||||
v0 ^= second;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +138,58 @@ uint64 siphash_3u64(const uint64 first, const uint64 second, const uint64 third,
|
|||
SIPROUND;
|
||||
SIPROUND;
|
||||
v0 ^= third;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
/**
|
||||
* siphash13_3u64 - compute 64-bit siphash13 PRF value of 3 uint64
|
||||
* @first: first uint64
|
||||
* @second: second uint64
|
||||
* @third: third uint64
|
||||
* @key: the siphash key
|
||||
*/
|
||||
uint64 siphash13_3u64(const uint64 first, const uint64 second, const uint64 third,
|
||||
const siphash_key_t *key) {
|
||||
PREAMBLE(24)
|
||||
v3 ^= first;
|
||||
SIPROUND;
|
||||
v0 ^= first;
|
||||
v3 ^= second;
|
||||
SIPROUND;
|
||||
v0 ^= second;
|
||||
v3 ^= third;
|
||||
SIPROUND;
|
||||
v0 ^= third;
|
||||
POSTAMBLE13
|
||||
}
|
||||
|
||||
uint64 siphash13_2u64(const uint64 first, const uint64 second, const siphash_key_t *key) {
|
||||
PREAMBLE(24)
|
||||
v3 ^= first;
|
||||
SIPROUND;
|
||||
v0 ^= first;
|
||||
v3 ^= second;
|
||||
SIPROUND;
|
||||
v0 ^= second;
|
||||
POSTAMBLE13
|
||||
}
|
||||
|
||||
uint64 siphash13_4u64(const uint64 first, const uint64 second, const uint64 third, const uint64 fourth,
|
||||
const siphash_key_t *key) {
|
||||
PREAMBLE(24)
|
||||
v3 ^= first;
|
||||
SIPROUND;
|
||||
v0 ^= first;
|
||||
v3 ^= second;
|
||||
SIPROUND;
|
||||
v0 ^= second;
|
||||
v3 ^= third;
|
||||
SIPROUND;
|
||||
v0 ^= third;
|
||||
v3 ^= fourth;
|
||||
SIPROUND;
|
||||
v0 ^= fourth;
|
||||
POSTAMBLE13
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,14 +220,14 @@ uint64 siphash_4u64(const uint64 first, const uint64 second, const uint64 third,
|
|||
SIPROUND;
|
||||
SIPROUND;
|
||||
v0 ^= forth;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
uint64 siphash_1u32(const uint32 first, const siphash_key_t *key)
|
||||
{
|
||||
PREAMBLE(4)
|
||||
b |= first;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
uint64 siphash_3u32(const uint32 first, const uint32 second, const uint32 third,
|
||||
|
@ -178,7 +240,7 @@ uint64 siphash_3u32(const uint32 first, const uint32 second, const uint32 third,
|
|||
SIPROUND;
|
||||
v0 ^= combined;
|
||||
b |= third;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
uint64 siphash_u64_u32(const uint64 combined, const uint32 third, const siphash_key_t *key) {
|
||||
|
@ -188,6 +250,6 @@ uint64 siphash_u64_u32(const uint64 combined, const uint32 third, const siphash_
|
|||
SIPROUND;
|
||||
v0 ^= combined;
|
||||
b |= third;
|
||||
POSTAMBLE
|
||||
POSTAMBLE24
|
||||
}
|
||||
|
||||
|
|
|
@ -50,4 +50,11 @@ uint64 siphash_u64_u32(const uint64 combined, const uint32 third, const siphash_
|
|||
*/
|
||||
uint64 siphash(const void *data, size_t len, const siphash_key_t *key);
|
||||
|
||||
uint64 siphash13_2u64(const uint64 first, const uint64 second, const siphash_key_t *key);
|
||||
uint64 siphash13_3u64(const uint64 first, const uint64 second, const uint64 third,
|
||||
const siphash_key_t *key);
|
||||
|
||||
uint64 siphash13_4u64(const uint64 first, const uint64 second, const uint64 third,
|
||||
const uint64 fourth, const siphash_key_t *key);
|
||||
|
||||
#endif // TUNSAFE_CRYPTO_SIPHASH_H_
|
||||
|
|
1455
third_party/flat_hash_map/bytell_hash_map.hpp
vendored
Normal file
1455
third_party/flat_hash_map/bytell_hash_map.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -9,3 +9,6 @@
|
|||
#define WITH_HEADER_OBFUSCATION 0
|
||||
#define WITH_AVX512_OPTIMIZATIONS 0
|
||||
#define WITH_BENCHMARK 0
|
||||
|
||||
// Use bytell hashmap instead. Only works in 64-bit builds
|
||||
#define WITH_BYTELL_HASHMAP 0
|
||||
|
|
|
@ -766,9 +766,7 @@ void WireguardProcessor::HandleShortHeaderFormatPacket(uint32 tag, Packet *packe
|
|||
data += 4, bytes_left -= 4;
|
||||
keypair = dev_.LookupKeypairByKeyId(key_id);
|
||||
} else {
|
||||
// Lookup the packet source ip and port in the address mapping
|
||||
uint64 addr_id = packet->addr.sin.sin_addr.s_addr | ((uint64)packet->addr.sin.sin_port << 32);
|
||||
keypair = dev_.LookupKeypairInAddrEntryMap(addr_id, ((tag / WG_SHORT_HEADER_KEY_ID) & 3) - 1);
|
||||
keypair = dev_.LookupKeypairInAddrEntryMap(packet->addr, ((tag & WG_SHORT_HEADER_KEY_ID_MASK) / WG_SHORT_HEADER_KEY_ID) - 1);
|
||||
}
|
||||
|
||||
if (!keypair || !keypair->enabled_features[WG_FEATURE_ID_SHORT_HEADER])
|
||||
|
@ -854,10 +852,8 @@ void WireguardProcessor::HandleShortHeaderFormatPacket(uint32 tag, Packet *packe
|
|||
// Periodically broadcast out the short key
|
||||
if ((tag & WG_SHORT_HEADER_KEY_ID_MASK) == 0x00 && !keypair->did_attempt_remember_ip_port) {
|
||||
keypair->did_attempt_remember_ip_port = true;
|
||||
if (keypair->enabled_features[WG_FEATURE_ID_SKIP_KEYID_IN]) {
|
||||
uint64 addr_id = packet->addr.sin.sin_addr.s_addr | ((uint64)packet->addr.sin.sin_port << 32);
|
||||
dev_.UpdateKeypairAddrEntry_Locked(addr_id, keypair);
|
||||
}
|
||||
if (keypair->enabled_features[WG_FEATURE_ID_SKIP_KEYID_IN])
|
||||
dev_.UpdateKeypairAddrEntry_Locked(packet->addr, keypair);
|
||||
}
|
||||
// Ack header may also signal that we can omit the key id in packets from now on.
|
||||
if (tag & WG_SHORT_HEADER_ACK)
|
||||
|
|
|
@ -263,20 +263,36 @@ void WgDevice::EraseKeypairAddrEntry_Locked(WgKeypair *kp) {
|
|||
}
|
||||
}
|
||||
|
||||
WgKeypair *WgDevice::LookupKeypairInAddrEntryMap(uint64 addr, uint32 slot) {
|
||||
static WgAddrEntry::IpPort ConvertIpAddrToAddrX(const IpAddr &src) {
|
||||
WgAddrEntry::IpPort r;
|
||||
if (src.sin.sin_family == AF_INET) {
|
||||
Write64(r.bytes, src.sin.sin_addr.s_addr);
|
||||
Write64(r.bytes + 8, 0);
|
||||
Write32(r.bytes + 16, src.sin.sin_port);
|
||||
} else {
|
||||
memcpy(r.bytes, &src.sin6.sin6_addr, 16);
|
||||
Write32(r.bytes + 16, (AF_INET6 << 16) + src.sin6.sin6_port);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
WgKeypair *WgDevice::LookupKeypairInAddrEntryMap(const IpAddr &addr, uint32 slot) {
|
||||
// Convert IpAddr to WgAddrEntry::IpPort suitable for use in hash.
|
||||
WgAddrEntry::IpPort addr_x = ConvertIpAddrToAddrX(addr);
|
||||
WG_SCOPED_RWLOCK_SHARED(addr_entry_lookup_lock_);
|
||||
auto it = addr_entry_lookup_.find(addr);
|
||||
auto it = addr_entry_lookup_.find(addr_x);
|
||||
if (it == addr_entry_lookup_.end())
|
||||
return NULL;
|
||||
WgAddrEntry *addr_entry = it->second;
|
||||
return addr_entry->keys[slot];
|
||||
}
|
||||
|
||||
void WgDevice::UpdateKeypairAddrEntry_Locked(uint64 addr_id, WgKeypair *keypair) {
|
||||
void WgDevice::UpdateKeypairAddrEntry_Locked(const IpAddr &addr, WgKeypair *keypair) {
|
||||
assert(keypair->peer->IsPeerLocked());
|
||||
WgAddrEntry::IpPort addr_x = ConvertIpAddrToAddrX(addr);
|
||||
{
|
||||
WG_SCOPED_RWLOCK_SHARED(addr_entry_lookup_lock_);
|
||||
if (keypair->addr_entry != NULL && keypair->addr_entry->addr_entry_id == addr_id) {
|
||||
if (keypair->addr_entry != NULL && keypair->addr_entry->addr_entry_id == addr_x) {
|
||||
keypair->broadcast_short_key = 1;
|
||||
return;
|
||||
}
|
||||
|
@ -286,10 +302,10 @@ void WgDevice::UpdateKeypairAddrEntry_Locked(uint64 addr_id, WgKeypair *keypair)
|
|||
if (keypair->addr_entry != NULL)
|
||||
EraseKeypairAddrEntry_Locked(keypair);
|
||||
|
||||
WgAddrEntry **aep = &addr_entry_lookup_[addr_id], *ae;
|
||||
WgAddrEntry **aep = &addr_entry_lookup_[addr_x], *ae;
|
||||
|
||||
if ((ae = *aep) == NULL) {
|
||||
*aep = ae = new WgAddrEntry(addr_id);
|
||||
*aep = ae = new WgAddrEntry(addr_x);
|
||||
} else {
|
||||
// Ensure we don't insert new things in this addr entry too often.
|
||||
if (ae->time_of_last_insertion + 1000 * 60 > low_resolution_timestamp_)
|
||||
|
@ -1452,3 +1468,20 @@ bool WgKeypairDecryptPayload(uint8 *dst, size_t src_len,
|
|||
return memcmp_crypto(mac, dst + src_len, keypair->auth_tag_length) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// A random siphash key that can be used for hashing so it gets harder to induce hash collisions.
|
||||
struct RandomSiphashKey {
|
||||
RandomSiphashKey() { OsGetRandomBytes((uint8*)&key, sizeof(key)); }
|
||||
siphash_key_t key;
|
||||
};
|
||||
static RandomSiphashKey random_siphash_key;
|
||||
|
||||
size_t WgAddrEntry::IpPortHasher::operator()(const WgAddrEntry::IpPort &a) const {
|
||||
uint32 xx = Read32(a.bytes + 16);
|
||||
return siphash13_2u64(Read64(a.bytes) + xx, Read64(a.bytes + 8) + xx, &random_siphash_key.key);
|
||||
}
|
||||
|
||||
size_t WgPublicKeyHasher::operator()(const WgPublicKey&a) const {
|
||||
return siphash13_4u64(a.u64[0], a.u64[1], a.u64[2], a.u64[3], &random_siphash_key.key);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,18 @@
|
|||
#include "netapi.h"
|
||||
#include "ipzip2/ipzip2.h"
|
||||
#include "tunsafe_config.h"
|
||||
#include "tunsafe_endian.h"
|
||||
#include "tunsafe_threading.h"
|
||||
#include "ip_to_peer_map.h"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
#include <string.h>
|
||||
|
||||
#if WITH_BYTELL_HASHMAP
|
||||
#include "third_party/flat_hash_map/bytell_hash_map.hpp"
|
||||
#endif // WITH_BYTELL_HASHMAP
|
||||
|
||||
// Threading macros that enable locks only in MT builds
|
||||
#if WITH_WG_THREADING
|
||||
#define WG_SCOPED_LOCK(name) ScopedLock scoped_lock(&name)
|
||||
|
@ -41,6 +47,13 @@
|
|||
#define WG_IF_LOCKS_ENABLED_ELSE(expr, def) (def)
|
||||
#endif // WITH_WG_THREADING
|
||||
|
||||
// bytell hash is faster but more untested
|
||||
#if WITH_BYTELL_HASHMAP
|
||||
#define WG_HASHTABLE_IMPL ska::bytell_hash_map
|
||||
#else
|
||||
#define WG_HASHTABLE_IMPL std::unordered_map
|
||||
#endif
|
||||
|
||||
enum ProtocolTimeouts {
|
||||
COOKIE_SECRET_MAX_AGE_MS = 120000,
|
||||
COOKIE_SECRET_LATENCY_MS = 5000,
|
||||
|
@ -235,13 +248,23 @@ private:
|
|||
};
|
||||
|
||||
struct WgAddrEntry {
|
||||
// The id of the addr entry, so we can delete ourselves
|
||||
uint64 addr_entry_id;
|
||||
struct IpPort {
|
||||
uint8 bytes[20];
|
||||
|
||||
// Ensure there's at least 1 minute between we allow registering
|
||||
// a new key in this table. This means that each key will have
|
||||
// a life time of at least 3 minutes.
|
||||
uint64 time_of_last_insertion;
|
||||
friend bool operator==(const IpPort &a, const IpPort &b) {
|
||||
uint64 rv = Read64(a.bytes) ^ Read64(b.bytes);
|
||||
rv |= Read64(a.bytes + 8) ^ Read64(b.bytes + 8);
|
||||
rv |= Read32(a.bytes + 16) ^ Read32(b.bytes + 16);
|
||||
return (rv == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct IpPortHasher {
|
||||
size_t operator()(const IpPort &a) const;
|
||||
};
|
||||
|
||||
// The id of the addr entry, so we can delete ourselves
|
||||
IpPort addr_entry_id;
|
||||
|
||||
// This entry gets erased when there's no longer any key pointing at it.
|
||||
uint8 ref_count;
|
||||
|
@ -249,13 +272,19 @@ struct WgAddrEntry {
|
|||
// Index of the next slot 0-2 where we'll insert the next key.
|
||||
uint8 next_slot;
|
||||
|
||||
// Ensure there's at least 1 minute between we allow registering
|
||||
// a new key in this table. This means that each key will have
|
||||
// a life time of at least 3 minutes.
|
||||
uint64 time_of_last_insertion;
|
||||
|
||||
// The three keys.
|
||||
WgKeypair *keys[3];
|
||||
|
||||
WgAddrEntry(uint64 addr_entry_id) : addr_entry_id(addr_entry_id), ref_count(0), next_slot(0) {
|
||||
WgAddrEntry(const IpPort &addr_entry_id)
|
||||
: addr_entry_id(addr_entry_id), ref_count(0), next_slot(0), time_of_last_insertion(0) {
|
||||
keys[0] = keys[1] = keys[2] = NULL;
|
||||
time_of_last_insertion = 0x123456789123456;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct ScramblerSiphashKeys {
|
||||
|
@ -271,10 +300,7 @@ union WgPublicKey {
|
|||
};
|
||||
|
||||
struct WgPublicKeyHasher {
|
||||
size_t operator()(const WgPublicKey&a) const {
|
||||
uint64 rv = a.u64[0] ^ a.u64[1] ^ a.u64[2] ^ a.u64[3];
|
||||
return (size_t)(rv ^ (rv >> 32));
|
||||
}
|
||||
size_t operator()(const WgPublicKey&a) const;
|
||||
};
|
||||
|
||||
class WgDevice {
|
||||
|
@ -314,14 +340,12 @@ public:
|
|||
bool CheckCookieMac2(Packet *packet);
|
||||
|
||||
void CreateCookieMessage(MessageHandshakeCookie *dst, Packet *packet, uint32 remote_key_id);
|
||||
void UpdateKeypairAddrEntry_Locked(uint64 addr_id, WgKeypair *keypair);
|
||||
void SecondLoop(uint64 now);
|
||||
|
||||
IpToPeerMap &ip_to_peer_map() { return ip_to_peer_map_; }
|
||||
WgPeer *first_peer() { return peers_; }
|
||||
const uint8 *public_key() const { return s_pub_; }
|
||||
WgRateLimit *rate_limiter() { return &rate_limiter_; }
|
||||
std::unordered_map<uint64, WgAddrEntry*> &addr_entry_map() { return addr_entry_lookup_; }
|
||||
WgPacketCompressionVer01 *compression_header() { return &compression_header_; }
|
||||
bool is_private_key_initialized() { return is_private_key_initialized_; }
|
||||
|
||||
|
@ -333,7 +357,9 @@ public:
|
|||
private:
|
||||
std::pair<WgPeer*, WgKeypair*> *LookupPeerInKeyIdLookup(uint32 key_id);
|
||||
WgKeypair *LookupKeypairByKeyId(uint32 key_id);
|
||||
WgKeypair *LookupKeypairInAddrEntryMap(uint64 addr, uint32 slot);
|
||||
|
||||
void UpdateKeypairAddrEntry_Locked(const IpAddr &addr, WgKeypair *keypair);
|
||||
WgKeypair *LookupKeypairInAddrEntryMap(const IpAddr &addr, uint32 slot);
|
||||
// Return the peer matching the |public_key| or NULL
|
||||
WgPeer *GetPeerFromPublicKey(const WgPublicKey &pubkey);
|
||||
// Create a cookie by inspecting the source address of the |packet|
|
||||
|
@ -357,20 +383,26 @@ private:
|
|||
// For hooking
|
||||
Delegate *delegate_;
|
||||
|
||||
|
||||
// Keypair IDs are generated randomly by us so no point in wasting cycles on
|
||||
// hashing the random value.
|
||||
struct KeyIdHasher {
|
||||
size_t operator()(uint32 v) const { return v; }
|
||||
};
|
||||
|
||||
// Lock that protects key_id_lookup_
|
||||
WG_DECLARE_RWLOCK(key_id_lookup_lock_);
|
||||
// Mapping from key-id to either an active keypair (if keypair is non-NULL),
|
||||
// or to a handshake.
|
||||
std::unordered_map<uint32, std::pair<WgPeer*, WgKeypair*> > key_id_lookup_;
|
||||
WG_HASHTABLE_IMPL<uint32, std::pair<WgPeer*, WgKeypair*>, KeyIdHasher> key_id_lookup_;
|
||||
|
||||
// Mapping from IPV4 IP/PORT to WgPeer*, so we can find the peer when a key id is
|
||||
// not explicitly included.
|
||||
std::unordered_map<uint64, WgAddrEntry*> addr_entry_lookup_;
|
||||
WG_HASHTABLE_IMPL<WgAddrEntry::IpPort, WgAddrEntry*, WgAddrEntry::IpPortHasher> addr_entry_lookup_;
|
||||
WG_DECLARE_RWLOCK(addr_entry_lookup_lock_);
|
||||
|
||||
// Mapping from peer id to peer. This may be accessed only from MT.
|
||||
std::unordered_map<WgPublicKey, WgPeer*, WgPublicKeyHasher> peer_id_lookup_;
|
||||
|
||||
WG_HASHTABLE_IMPL<WgPublicKey, WgPeer*, WgPublicKeyHasher> peer_id_lookup_;
|
||||
// Queue of things scheduled to run on the main thread.
|
||||
WG_DECLARE_LOCK(main_thread_scheduled_lock_);
|
||||
WgPeer *main_thread_scheduled_, **main_thread_scheduled_last_;
|
||||
|
|
Loading…
Reference in a new issue