ALL

Solo para leer y verificar. No puedes copiar, usar, modificar ni redistribuir este código. Aviso completo en la página principal.

tunnel/hints.go

SHA-256: f5179c76616e54b561b788b7e63fca769f3d7f6b8ddba8029adc429eb8593718
120 líneas · 3278 bytes · Ver en GitHub

1package main
2
3import (
4 "net/netip"
5 "sync"
6 "time"
7)
8
9// HINT (the design plan, sections 2 and 5): a device tells its node which source addresses just completed authenticated calls to it. They get the
10// slice of the device's stream cap that anonymous sources cannot use (policy node_limits.label_hint_slice), so a flood from many addresses cannot lock
11// out the one that is ChatGPT. The table is memory only, an address stays good for hint_ttl_s after the last HINT that named it, a device can mark
12// at most hint_addresses_per_label addresses (the oldest go first), and a mark is filed under the host the control plane's answer gave that connection:
13// a device cannot mark an address for any host but its own.
14
15type hintSet struct {
16 until map[netip.Addr]time.Time
17}
18
19// hintTable is the node's record of the addresses devices marked good, per device host.
20type hintTable struct {
21 clk clock
22 ttl time.Duration
23 cap int
24
25 mu sync.Mutex
26 hosts map[string]*hintSet
27}
28
29func newHintTable(clk clock, ttl time.Duration, perLabel int) *hintTable {
30 return &hintTable{clk: clk, ttl: ttl, cap: perLabel, hosts: map[string]*hintSet{}}
31}
32
33// mark records the addresses (never more than the protocol's list) as good for host from now until the TTL.
34func (h *hintTable) mark(host string, addrs []netip.Addr) {
35 now := h.clk.Now()
36 h.mu.Lock()
37 defer h.mu.Unlock()
38 set := h.hosts[host]
39 if set == nil {
40 set = &hintSet{until: map[netip.Addr]time.Time{}}
41 h.hosts[host] = set
42 }
43 for _, a := range addrs {
44 set.until[a.WithZone("").Unmap()] = now.Add(h.ttl)
45 }
46 if len(set.until) <= h.cap {
47 return
48 }
49 for a, t := range set.until { // expired first
50 if !now.Before(t) {
51 delete(set.until, a)
52 }
53 }
54 for len(set.until) > h.cap { // then the ones that will expire soonest, which are the ones marked longest ago
55 var oldest netip.Addr
56 var first time.Time
57 for a, t := range set.until {
58 if first.IsZero() || t.Before(first) {
59 oldest, first = a, t
60 }
61 }
62 delete(set.until, oldest)
63 }
64}
65
66// good reports whether the device of host marked addr and the mark has not expired.
67func (h *hintTable) good(host string, addr netip.Addr) bool {
68 now := h.clk.Now()
69 addr = addr.WithZone("").Unmap()
70 h.mu.Lock()
71 defer h.mu.Unlock()
72 set := h.hosts[host]
73 if set == nil {
74 return false
75 }
76 t, ok := set.until[addr]
77 return ok && now.Before(t)
78}
79
80// goodAnywhere reports whether the device of ANY host marked addr and the mark has not expired.
81func (h *hintTable) goodAnywhere(addr netip.Addr) bool {
82 now := h.clk.Now()
83 addr = addr.WithZone("").Unmap()
84 h.mu.Lock()
85 defer h.mu.Unlock()
86 for _, set := range h.hosts {
87 if t, ok := set.until[addr]; ok && now.Before(t) {
88 return true
89 }
90 }
91 return false
92}
93
94// Sweep forgets what expired (a background task calls it).
95func (h *hintTable) Sweep() {
96 now := h.clk.Now()
97 h.mu.Lock()
98 defer h.mu.Unlock()
99 for host, set := range h.hosts {
100 for a, t := range set.until {
101 if !now.Before(t) {
102 delete(set.until, a)
103 }
104 }
105 if len(set.until) == 0 {
106 delete(h.hosts, host)
107 }
108 }
109}
110
111// size is the number of addresses held, for the metrics (an aggregate: never per device).
112func (h *hintTable) size() int {
113 h.mu.Lock()
114 defer h.mu.Unlock()
115 n := 0
116 for _, set := range h.hosts {
117 n += len(set.until)
118 }
119 return n
120}