server: send granted traffic from the address the session actually used
fmr binds two IPv4 addresses. connFor picked whichever socket of the right family came first in the bind list, so a downtrain for a session established on .150 went out from .151 — and every packet was dropped by the client's NAT, which has no mapping for that pair. tcpdump on the server showed all 50 leaving; the client saw none. Read as "100% downstream loss", which is the worst kind of wrong: a confident measurement of something that never happened. Sessions now record which of our own bound addresses received their traffic, and granted sends (and delayed echo) go back out through that socket. The fallback to a family match is kept for the case where nothing has been received yet, and the test pins both paths — a single-homed lab can never reproduce this. Also: the client-side halves of the same work — anonymizer (core-privacy), local run archive with retention (core-archive), upload client, and the app's settings and history screens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7a94c9a3d7
commit
ce1aaa332a
@@ -27,6 +27,7 @@ type Session struct {
|
||||
// Last data-plane source seen with a valid HMAC (NAT rebinding evidence).
|
||||
mu sync.Mutex
|
||||
dataSource netip.AddrPort
|
||||
dataLocal netip.AddrPort
|
||||
// Replay window (spec §3.1: 1024-wide seq window). Highest seq seen plus
|
||||
// a bitmask of the 1024 preceding.
|
||||
maxSeq uint32
|
||||
@@ -192,6 +193,25 @@ func (s *Session) CheckSeq(seq uint32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// DataLocal returns the server-side address that received this session's data-plane traffic.
|
||||
//
|
||||
// This matters more than it looks: a server bound to several addresses must send granted traffic
|
||||
// back from the one the client has been talking to. Any stateful firewall or NAT in between has
|
||||
// a mapping keyed on that exact pair, and a reply from a sibling address is dropped — which the
|
||||
// client would then measure as downstream loss. See connFor.
|
||||
func (s *Session) DataLocal() netip.AddrPort {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.dataLocal
|
||||
}
|
||||
|
||||
// NoteDataLocal records which of our own bound addresses saw this session's traffic.
|
||||
func (s *Session) NoteDataLocal(ap netip.AddrPort) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.dataLocal = ap
|
||||
}
|
||||
|
||||
// NoteDataSource records the latest verified data-plane source.
|
||||
func (s *Session) NoteDataSource(ap netip.AddrPort) {
|
||||
s.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user