net/traffic: switch rendezvous hashing from SHA256 to FNV-1a (#19821)
In PR tailscale/corp#30448, we originally decided to break ties using SHA256 for our rendezvous hashing algorithm. Now that we’ve had some experience with it, we think that FNV-1a is a better choice. It distributes bits evenly, it’s much faster, and it doesn’t need to be cryptographically secure. The FNV designers recommend FNV-1a over the deprecated FNV-1. This PR makes the switch and updates the related tests, since changing the algorithm changes which stable pick gets selected. As of 2026-05, this is the best time to make this change, since there are almost no clients in the wild with traffic steering enabled. Updates #17366 Updates tailscale/corp#29964 Updates tailscale/corp#29966 Updates tailscale/corp#33033 Signed-off-by: Simon Law <sfllaw@tailscale.com>
This commit is contained in:
@@ -1099,7 +1099,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
hash from compress/zlib+
|
||||
hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from google.golang.org/protobuf/internal/detrand
|
||||
hash/fnv from google.golang.org/protobuf/internal/detrand+
|
||||
hash/maphash from go4.org/mem
|
||||
html from html/template+
|
||||
html/template from tailscale.com/util/eventbus
|
||||
|
||||
@@ -335,6 +335,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
fmt from compress/flate+
|
||||
hash from crypto+
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/net/traffic
|
||||
hash/maphash from go4.org/mem
|
||||
html from tailscale.com/ipn/ipnlocal+
|
||||
internal/abi from hash/maphash+
|
||||
|
||||
@@ -356,6 +356,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
fmt from compress/flate+
|
||||
hash from crypto+
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/net/traffic
|
||||
hash/maphash from go4.org/mem
|
||||
html from tailscale.com/ipn/ipnlocal+
|
||||
internal/abi from hash/maphash+
|
||||
|
||||
@@ -665,6 +665,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
hash from compress/zlib+
|
||||
hash/adler32 from compress/zlib+
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/net/traffic
|
||||
hash/maphash from go4.org/mem
|
||||
html from html/template+
|
||||
html/template from tailscale.com/util/eventbus
|
||||
|
||||
@@ -482,6 +482,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
hash from crypto+
|
||||
W hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/net/traffic
|
||||
hash/maphash from go4.org/mem
|
||||
html from html/template+
|
||||
html/template from tailscale.com/util/eventbus+
|
||||
|
||||
@@ -5832,8 +5832,8 @@ func TestSuggestExitNodeTrafficSteering(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// Change this, if the hashing function changes.
|
||||
wantID: "stable3",
|
||||
wantName: "peer3",
|
||||
wantID: "stable1",
|
||||
wantName: "peer1",
|
||||
},
|
||||
{
|
||||
name: "exit-nodes-without-priority-for-suggestions",
|
||||
@@ -5973,8 +5973,9 @@ func TestSuggestExitNodeTrafficSteering(t *testing.T) {
|
||||
withLocationPriority(2)), // top
|
||||
},
|
||||
},
|
||||
wantID: "stable5",
|
||||
wantName: "peer5",
|
||||
// Change this, if the hashing function changes.
|
||||
wantID: "stable2",
|
||||
wantName: "peer2",
|
||||
wantPri: 2,
|
||||
},
|
||||
{
|
||||
|
||||
+11
-3
@@ -7,8 +7,8 @@ package traffic
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"hash/fnv"
|
||||
"iter"
|
||||
"maps"
|
||||
"slices"
|
||||
@@ -107,8 +107,16 @@ func MakeRendezvousHasher(seed tailcfg.NodeID) NodeHasher {
|
||||
var b [16]byte
|
||||
en.PutUint64(b[:], uint64(seed))
|
||||
en.PutUint64(b[8:], uint64(n))
|
||||
v := sha256.Sum256(b[:])
|
||||
return en.Uint64(v[:])
|
||||
|
||||
// FNV-1a is more modern and distributes bits more evenly,
|
||||
// so it is recommended by the designers.
|
||||
//
|
||||
// Note that we don’t use a global hasher and h.Reset
|
||||
// because this closure could be called concurrently.
|
||||
// This is cheap because hash/fnv doesn’t need to allocate.
|
||||
h := fnv.New64a()
|
||||
h.Write(b[:])
|
||||
return h.Sum64()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -475,6 +475,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
hash from crypto+
|
||||
W hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/net/traffic
|
||||
hash/maphash from go4.org/mem
|
||||
html from html/template+
|
||||
LDW html/template from tailscale.com/util/eventbus
|
||||
|
||||
Reference in New Issue
Block a user