server: STUN, TCP echo, observations API, delayed-echo + connect-back actions
- stun: RFC 5389 binding responder + RFC 5780 attributes (OTHER-ADDRESS,
RESPONSE-ORIGIN, CHANGE-REQUEST) on a primary/alt-port socket grid per
address; advertises stun-5780 with >=2 same-family addrs, else
stun-basic. Unmodified framing for tooling interop. Tested.
- tcpecho: JSON greeting with observed src + TCP_INFO MSS/options
(Linux getsockopt; zeroed elsewhere via build tags), then byte echo.
- session: per-packet UDP observations + connect-back results, ByID lookup.
- control: GET /v1/sessions/{id}/observations, POST .../actions
(delayed_echo → DELAYED_ECHO at the observed data-plane source;
connect_back → dial the control-plane source, record connected/refused/
timeout+rtt). Capabilities computed from what is actually wired.
- config/main: comma-separated STUN listeners; all planes bind explicit
addresses; graceful shutdown of the new listeners.
Full flow smoke-tested; go test green (stun binding/change-port,
dataplane wire format).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
507a8bfc1f
commit
7b676e666e
@@ -31,6 +31,64 @@ type Session struct {
|
||||
// a bitmask of the 1024 preceding.
|
||||
maxSeq uint32
|
||||
window [16]uint64
|
||||
|
||||
// Observations (spec §6): per-packet UDP view + connect-back results.
|
||||
packetsSeen uint64
|
||||
udpObs []UDPObservation // ring, newest last, cap obsCap
|
||||
connectBack []ConnectBackResult
|
||||
}
|
||||
|
||||
const obsCap = 4096
|
||||
|
||||
// UDPObservation is the server's witnessed view of one data-plane packet.
|
||||
type UDPObservation struct {
|
||||
Seq uint32 `json:"seq"`
|
||||
TRxNs int64 `json:"t_rx_ns"`
|
||||
TTxNs int64 `json:"t_tx_ns"`
|
||||
Src string `json:"src"`
|
||||
Size int `json:"size"`
|
||||
Type uint8 `json:"type"`
|
||||
}
|
||||
|
||||
// ConnectBackResult records one connect-back action outcome.
|
||||
type ConnectBackResult struct {
|
||||
ActionID string `json:"action_id"`
|
||||
Result string `json:"result"` // connected | refused | timeout
|
||||
RttMs float64 `json:"rtt_ms"`
|
||||
}
|
||||
|
||||
// RecordUDP appends a packet observation (ring-capped).
|
||||
func (s *Session) RecordUDP(o UDPObservation) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.packetsSeen++
|
||||
if len(s.udpObs) >= obsCap {
|
||||
s.udpObs = s.udpObs[1:]
|
||||
}
|
||||
s.udpObs = append(s.udpObs, o)
|
||||
}
|
||||
|
||||
// RecordConnectBack appends a connect-back outcome.
|
||||
func (s *Session) RecordConnectBack(r ConnectBackResult) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.connectBack = append(s.connectBack, r)
|
||||
}
|
||||
|
||||
// Observations returns a copy of everything witnessed so far.
|
||||
func (s *Session) Observations() (packetsSeen uint64, udp []UDPObservation, cb []ConnectBackResult) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.packetsSeen, append([]UDPObservation(nil), s.udpObs...),
|
||||
append([]ConnectBackResult(nil), s.connectBack...)
|
||||
}
|
||||
|
||||
// DataSource returns the last verified data-plane source (invalid when the
|
||||
// session has not sent data-plane traffic yet).
|
||||
func (s *Session) DataSource() netip.AddrPort {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.dataSource
|
||||
}
|
||||
|
||||
// KeySalt returns nothing — the salt is not retained after derivation; it is
|
||||
@@ -87,6 +145,20 @@ func (m *Manager) ByWirePrefix(prefix [8]byte) *Session {
|
||||
return s
|
||||
}
|
||||
|
||||
// ByID resolves a full session id (sessions are keyed by their wire prefix).
|
||||
func (m *Manager) ByID(id string) *Session {
|
||||
if len(id) < 16 {
|
||||
return nil
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
s := m.byPrefix[id[:16]]
|
||||
if s == nil || s.ID != id || time.Now().After(s.Expires) {
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *Manager) Delete(id string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user