初版功能完成
ci / Go checks (ubuntu-latest) (push) Has been cancelled
ci / Go checks (windows-latest) (push) Has been cancelled

This commit is contained in:
qsc
2026-08-29 13:12:17 +08:00
commit 142e5dc7d6
217 changed files with 21313 additions and 0 deletions
+387
View File
@@ -0,0 +1,387 @@
package session
import (
"context"
"errors"
"net"
"net/netip"
"sync"
"testing"
"time"
"remlink/internal/overlay/clientwg"
"remlink/internal/platform/windows/route"
"remlink/internal/protocol"
"remlink/internal/subnetgateway"
)
func TestEngineerRuntimePreflightConfigureActivateStop(t *testing.T) {
port := availableUDPPort(t, "127.0.0.1")
routes := &fakeRoutes{}
device := &fakePacketDevice{}
control := &fakeNodeControl{}
runtime, err := NewEngineerRuntime(context.Background(), EngineerConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.1"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, StatsInterval: time.Hour, Routes: routes, Device: device, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
requestID, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.13.0/24"})
if err != nil || requestID == "" || control.last(t, protocol.ControlCreateSession).requestID != requestID {
t.Fatalf("CreateSession = %q, %v", requestID, err)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlSessionConfig, requestID, protocol.SessionConfigPayload{
SessionID: 42, PeerOverlayIP: "127.0.0.2", CIDRs: []string{"192.168.13.0/24"}, MTU: 1280, UDPPort: port,
})); err != nil {
t.Fatal(err)
}
if len(routes.added) != 1 || device.router == nil || control.last(t, protocol.ControlRoutesReady).payload.(protocol.RoutesReadyPayload).SessionID != 42 {
t.Fatalf("Engineer READY wiring routes=%v router=%v", routes.added, device.router)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlSessionActive, "", protocol.SessionActivePayload{SessionID: 42})); err != nil {
t.Fatal(err)
}
if err := runtime.Disconnect(context.Background(), "operator"); err != nil {
t.Fatal(err)
}
if got := control.last(t, protocol.ControlStopSession).payload.(protocol.StopSessionPayload); got.SessionID != 42 {
t.Fatalf("Disconnect payload = %+v", got)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlStopSession, "", protocol.StopSessionPayload{SessionID: 42, Reason: "operator"})); err != nil {
t.Fatal(err)
}
if device.router != nil || len(routes.removed) != 1 {
t.Fatalf("Engineer cleanup router=%v removed=%v", device.router, routes.removed)
}
}
func TestEngineerRuntimeRejectsLocalConflictBeforeControl(t *testing.T) {
port := availableUDPPort(t, "127.0.0.1")
routes := &fakeRoutes{conflict: true}
control := &fakeNodeControl{}
runtime, err := NewEngineerRuntime(context.Background(), EngineerConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.1"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, Routes: routes, Device: &fakePacketDevice{}, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
if err := runtime.PreflightCIDRs([]string{"192.168.13.0/24"}); err == nil {
t.Fatal("preflight accepted local route conflict")
}
if control.count(protocol.ControlCreateSession) != 0 {
t.Fatal("preflight changed Control state")
}
if _, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.13.0/24"}); err == nil {
t.Fatal("local route conflict was accepted")
}
if control.count(protocol.ControlCreateSession) != 0 {
t.Fatal("CREATE_SESSION was sent despite local conflict")
}
}
func TestEngineerRuntimePrepareFailureReleasesOnlyMatchingPendingRequest(t *testing.T) {
port := availableUDPPort(t, "127.0.0.1")
control := &fakeNodeControl{}
runtime, err := NewEngineerRuntime(context.Background(), EngineerConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.1"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, Routes: &fakeRoutes{}, Device: &fakePacketDevice{}, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
requestID, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.13.0/24"})
if err != nil {
t.Fatal(err)
}
rejection := protocol.StopSessionPayload{SessionID: 81, Reason: string(protocol.ErrorSiteNoRoute)}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlStopSession, "stale-request", rejection)); err != nil {
t.Fatal(err)
}
if _, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.21.0/24"}); err == nil {
t.Fatal("stale rejection released the active pending request")
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlStopSession, requestID, rejection)); err != nil {
t.Fatal(err)
}
if _, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.21.0/24"}); err != nil {
t.Fatalf("matching PREPARE rejection did not release pending state: %v", err)
}
}
func TestSiteRuntimePrepareActivateAndCleanup(t *testing.T) {
port := availableUDPPort(t, "127.0.0.2")
control := &fakeNodeControl{}
gateway := &fakeGateway{}
runtime, err := NewSiteRuntime(context.Background(), SiteConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.2"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, StatsInterval: time.Hour, Routes: &fakeRoutes{lookup: route.LookupDirect},
Gateway: gateway, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
prepare := protocol.PrepareSessionPayload{SessionID: 77, EngineerOverlayIP: "127.0.0.1", TargetCIDRs: []string{"192.168.13.0/24"}}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlPrepareSession, "", prepare)); err != nil {
t.Fatal(err)
}
result := control.last(t, protocol.ControlPrepareResult).payload.(protocol.PrepareResultPayload)
if !result.OK || result.SubnetGatewayStatus != "netstack" || gateway.prepared != 77 || len(result.RouteResults) != 1 {
t.Fatalf("PREPARE_RESULT=%+v gateway=%d", result, gateway.prepared)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlSessionActive, "", protocol.SessionActivePayload{SessionID: 77})); err != nil {
t.Fatal(err)
}
if err := runtime.Egress(context.Background(), 77, testRuntimeIPv4("192.168.13.10", "127.0.0.1")); err != nil {
t.Fatal(err)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlStopSession, "", protocol.StopSessionPayload{SessionID: 77})); err != nil {
t.Fatal(err)
}
if gateway.closed != 77 {
t.Fatalf("closed gateway Session = %d", gateway.closed)
}
}
func TestSiteRuntimeRejectsDefaultOnlyRoute(t *testing.T) {
port := availableUDPPort(t, "127.0.0.2")
control := &fakeNodeControl{}
gateway := &fakeGateway{}
runtime, err := NewSiteRuntime(context.Background(), SiteConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.2"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, StatsInterval: time.Hour, Routes: &fakeRoutes{lookup: route.LookupDefaultOnly},
Gateway: gateway, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
prepare := protocol.PrepareSessionPayload{SessionID: 78, EngineerOverlayIP: "127.0.0.1", TargetCIDRs: []string{"192.168.13.0/24"}}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlPrepareSession, "", prepare)); err != nil {
t.Fatal(err)
}
result := control.last(t, protocol.ControlPrepareResult).payload.(protocol.PrepareResultPayload)
if result.OK || result.ErrorCode != protocol.ErrorSiteNoRoute || gateway.prepared != 0 {
t.Fatalf("PREPARE_RESULT=%+v gateway=%d", result, gateway.prepared)
}
}
func TestSiteRuntimeRejectsPrepareAtFlowCapacity(t *testing.T) {
for _, test := range []struct {
name string
tcpFlows int
udpFlows int
tcpLimit int
udpLimit int
}{
{name: "tcp", tcpFlows: 2, tcpLimit: 2, udpLimit: 4},
{name: "udp", udpFlows: 4, tcpLimit: 2, udpLimit: 4},
} {
t.Run(test.name, func(t *testing.T) {
port := availableUDPPort(t, "127.0.0.2")
control := &fakeNodeControl{}
gateway := &fakeGateway{tcpFlows: test.tcpFlows, udpFlows: test.udpFlows}
runtime, err := NewSiteRuntime(context.Background(), SiteConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.2"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"),
UDPPort: port, TCPFlowLimit: test.tcpLimit, UDPFlowLimit: test.udpLimit, StatsInterval: time.Hour,
Routes: &fakeRoutes{lookup: route.LookupDirect}, Gateway: gateway, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
prepare := protocol.PrepareSessionPayload{SessionID: 79, EngineerOverlayIP: "127.0.0.1", TargetCIDRs: []string{"192.168.13.0/24"}}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlPrepareSession, "", prepare)); err != nil {
t.Fatal(err)
}
result := control.last(t, protocol.ControlPrepareResult).payload.(protocol.PrepareResultPayload)
if result.OK || result.ErrorCode != protocol.ErrorFlowLimitReached || gateway.prepared != 0 {
t.Fatalf("PREPARE_RESULT=%+v gateway=%d", result, gateway.prepared)
}
})
}
}
func TestEngineerRuntimeInjectionFailureStopsOnlySession(t *testing.T) {
port := availableUDPPort(t, "127.0.0.1")
control := &fakeNodeControl{}
device := &fakePacketDevice{injectErr: errors.New("wintun write failed")}
runtime, err := NewEngineerRuntime(context.Background(), EngineerConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.1"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"), UDPPort: port,
StatsInterval: time.Hour, Routes: &fakeRoutes{}, Device: device, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
if _, err := runtime.CreateSession(context.Background(), "site", []string{"192.168.13.0/24"}); err != nil {
t.Fatal(err)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlSessionConfig, control.last(t, protocol.ControlCreateSession).requestID, protocol.SessionConfigPayload{
SessionID: 91, PeerOverlayIP: "127.0.0.2", CIDRs: []string{"192.168.13.0/24"}, MTU: 1280, UDPPort: port,
})); err != nil {
t.Fatal(err)
}
if err := runtime.inject(context.Background(), 91, testRuntimeIPv4("192.168.13.10", "127.0.0.1")); err == nil {
t.Fatal("injection failure was hidden")
}
stop := control.last(t, protocol.ControlStopSession).payload.(protocol.StopSessionPayload)
if stop.Reason != string(protocol.ErrorSessionInjectFailed) || runtime.Snapshot().ID != 0 || device.router != nil {
t.Fatalf("STOP=%+v snapshot=%+v router=%v", stop, runtime.Snapshot(), device.router)
}
}
func TestSiteRuntimeInjectionFailureClosesGatewaySession(t *testing.T) {
port := availableUDPPort(t, "127.0.0.2")
control := &fakeNodeControl{}
gateway := &fakeGateway{injectErr: errors.New("netstack inject failed")}
runtime, err := NewSiteRuntime(context.Background(), SiteConfig{
LocalOverlayIP: netip.MustParseAddr("127.0.0.2"), OverlayCIDR: netip.MustParsePrefix("127.0.0.0/8"), UDPPort: port,
StatsInterval: time.Hour, Routes: &fakeRoutes{lookup: route.LookupDirect}, Gateway: gateway, Control: control,
})
if err != nil {
t.Fatal(err)
}
defer runtime.Close()
prepare := protocol.PrepareSessionPayload{SessionID: 92, EngineerOverlayIP: "127.0.0.1", TargetCIDRs: []string{"192.168.13.0/24"}}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlPrepareSession, "", prepare)); err != nil {
t.Fatal(err)
}
if err := runtime.HandleControl(context.Background(), envelope(t, protocol.ControlSessionActive, "", protocol.SessionActivePayload{SessionID: 92})); err != nil {
t.Fatal(err)
}
if err := runtime.inject(context.Background(), 92, testRuntimeIPv4("127.0.0.1", "192.168.13.10")); err == nil {
t.Fatal("injection failure was hidden")
}
stop := control.last(t, protocol.ControlStopSession).payload.(protocol.StopSessionPayload)
if stop.Reason != string(protocol.ErrorSessionInjectFailed) || gateway.closed != 92 {
t.Fatalf("STOP=%+v gateway.closed=%d", stop, gateway.closed)
}
}
type fakeNodeControl struct {
mu sync.Mutex
messages []nodeMessage
}
type nodeMessage struct {
messageType protocol.ControlMessageType
requestID string
payload any
}
func (f *fakeNodeControl) Send(_ context.Context, messageType protocol.ControlMessageType, requestID string, payload any) error {
f.mu.Lock()
f.messages = append(f.messages, nodeMessage{messageType: messageType, requestID: requestID, payload: payload})
f.mu.Unlock()
return nil
}
func (f *fakeNodeControl) last(t *testing.T, messageType protocol.ControlMessageType) nodeMessage {
t.Helper()
f.mu.Lock()
defer f.mu.Unlock()
for index := len(f.messages) - 1; index >= 0; index-- {
if f.messages[index].messageType == messageType {
return f.messages[index]
}
}
t.Fatalf("no %s message", messageType)
return nodeMessage{}
}
func (f *fakeNodeControl) count(messageType protocol.ControlMessageType) int {
f.mu.Lock()
defer f.mu.Unlock()
count := 0
for _, message := range f.messages {
if message.messageType == messageType {
count++
}
}
return count
}
type fakePacketDevice struct {
router *clientwg.PacketMux
injectErr error
}
func (f *fakePacketDevice) SetPacketMux(router *clientwg.PacketMux) { f.router = router }
func (f *fakePacketDevice) InjectInbound([]byte) error { return f.injectErr }
type fakeRoutes struct {
added, removed []netip.Prefix
conflict bool
lookup route.LookupResult
}
func (f *fakeRoutes) AddRemote(prefix netip.Prefix) error {
f.added = append(f.added, prefix)
return nil
}
func (f *fakeRoutes) RemoveRemote(prefix netip.Prefix) error {
f.removed = append(f.removed, prefix)
return nil
}
func (f *fakeRoutes) Conflicts(prefix netip.Prefix) ([]route.Entry, error) {
if f.conflict {
return []route.Entry{{Destination: netip.MustParsePrefix("192.168.0.0/16")}}, nil
}
return nil, nil
}
func (*fakeRoutes) Reconcile() error { return nil }
func (f *fakeRoutes) Lookup(netip.Addr) (route.LookupResult, error) {
if f.lookup == "" {
return route.LookupNoRoute, nil
}
return f.lookup, nil
}
type fakeGateway struct {
prepared, closed uint64
injectErr error
tcpFlows int
udpFlows int
}
func (f *fakeGateway) Prepare(_ context.Context, config subnetgateway.SessionConfig) error {
f.prepared = config.SessionID
return nil
}
func (f *fakeGateway) InjectIPv4(context.Context, uint64, []byte) error { return f.injectErr }
func (f *fakeGateway) CloseSession(_ context.Context, id uint64) error {
f.closed = id
return nil
}
func (f *fakeGateway) FlowCounts() (int, int) { return f.tcpFlows, f.udpFlows }
func (*fakeGateway) Close() error { return nil }
func availableUDPPort(t *testing.T, host string) int {
t.Helper()
connection, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP(host), Port: 0})
if err != nil {
t.Fatal(err)
}
port := connection.LocalAddr().(*net.UDPAddr).Port
if err := connection.Close(); err != nil {
t.Fatal(err)
}
return port
}
func testRuntimeIPv4(sourceText, destinationText string) []byte {
packet := make([]byte, 20)
packet[0] = 0x45
packet[3] = 20
source := netip.MustParseAddr(sourceText).As4()
destination := netip.MustParseAddr(destinationText).As4()
copy(packet[12:16], source[:])
copy(packet[16:20], destination[:])
return packet
}