feat: audit api, sdwan persist, relay fallback updates

This commit is contained in:
2026-03-06 14:47:03 +08:00
parent e96a2e5dd9
commit 57b4dadd42
26 changed files with 991 additions and 183 deletions

View File

@@ -1,13 +1,13 @@
// Package relay implements relay/super-relay node capabilities.
//
// Relay flow:
// 1. Client A asks server for relay (RelayNodeReq)
// 2. Server finds relay R, generates TOTP/token, responds to A (RelayNodeRsp)
// 3. Server pushes RelayOffer to R with session info
// 4. A connects to R:relayPort, sends RelayHandshake{SessionID, Role="from", Token}
// 5. B connects to R:relayPort, sends RelayHandshake{SessionID, Role="to", Token}
// (B gets the session info via server push)
// 6. R verifies both tokens, bridges A↔B
// 1. Client A asks server for relay (RelayNodeReq)
// 2. Server finds relay R, generates TOTP/token, responds to A (RelayNodeRsp)
// 3. Server pushes RelayOffer to R with session info
// 4. A connects to R:relayPort, sends RelayHandshake{SessionID, Role="from", Token}
// 5. B connects to R:relayPort, sends RelayHandshake{SessionID, Role="to", Token}
// (B gets the session info via server push)
// 6. R verifies both tokens, bridges A↔B
package relay
import (
@@ -68,6 +68,7 @@ type Manager struct {
enabled bool
superRelay bool
maxLoad int
maxMbps int
token uint64 // this node's auth token
port int
listener net.Listener
@@ -92,11 +93,12 @@ type Session struct {
}
// NewManager creates a relay manager.
func NewManager(port int, enabled, superRelay bool, maxLoad int, token uint64) *Manager {
func NewManager(port int, enabled, superRelay bool, maxLoad int, token uint64, maxMbps int) *Manager {
return &Manager{
enabled: enabled,
superRelay: superRelay,
maxLoad: maxLoad,
maxMbps: maxMbps,
token: token,
port: port,
pending: make(map[string]*pendingSession),
@@ -296,14 +298,47 @@ func (m *Manager) bridge(ps *pendingSession) {
var wg sync.WaitGroup
wg.Add(2)
copyWithLimit := func(dst, src net.Conn) int64 {
if m.maxMbps <= 0 {
n, _ := io.Copy(dst, src)
return n
}
bytesPerSec := int64(m.maxMbps) * 1024 * 1024 / 8
if bytesPerSec < 1 {
bytesPerSec = 1
}
var total int64
buf := make([]byte, 32*1024)
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
var allowance = bytesPerSec / 10
for {
n, err := src.Read(buf)
if n > 0 {
// simple token bucket
if allowance < int64(n) {
<-ticker.C
allowance = bytesPerSec / 10
}
allowance -= int64(n)
w, _ := dst.Write(buf[:n])
total += int64(w)
}
if err != nil {
break
}
}
return total
}
go func() {
defer wg.Done()
n, _ := io.Copy(sess.ConnB, sess.ConnA)
n := copyWithLimit(sess.ConnB, sess.ConnA)
atomic.AddInt64(&sess.BytesFwd, n)
}()
go func() {
defer wg.Done()
n, _ := io.Copy(sess.ConnA, sess.ConnB)
n := copyWithLimit(sess.ConnA, sess.ConnB)
atomic.AddInt64(&sess.BytesFwd, n)
}()

View File

@@ -12,7 +12,7 @@ import (
func TestRelayBridge(t *testing.T) {
token := auth.MakeToken("test", "pass")
mgr := NewManager(29700, true, false, 10, token)
mgr := NewManager(29700, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}
@@ -94,7 +94,7 @@ func TestRelayBridge(t *testing.T) {
func TestRelayLargeData(t *testing.T) {
token := auth.MakeToken("test", "pass")
mgr := NewManager(29701, true, false, 10, token)
mgr := NewManager(29701, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}
@@ -173,7 +173,7 @@ func TestRelayLargeData(t *testing.T) {
func TestRelayAuthDenied(t *testing.T) {
token := auth.MakeToken("real", "token")
mgr := NewManager(29702, true, false, 10, token)
mgr := NewManager(29702, true, false, 10, token, 10)
if err := mgr.Start(); err != nil {
t.Fatal(err)
}