Fix VPN fabric-only routing guard

This commit is contained in:
2026-05-14 23:26:19 +03:00
parent 8f69d53193
commit 26cb65e936
5 changed files with 260 additions and 79 deletions
@@ -44,14 +44,16 @@ type FabricPacketTransport struct {
}
type FabricClientPacketIngress struct {
ForwardTransport mesh.ProductionForwardTransport
Inbox *FabricPacketInbox
Routes func() []mesh.SyntheticRoute
LocalGateway func(vpnConnectionID string) bool
FlowScheduler *FabricFlowScheduler
MaxParallelFlowSends int
RecoveryPolicyFingerprint string
AdaptivePolicyFingerprint string
ForwardTransport mesh.ProductionForwardTransport
Inbox *FabricPacketInbox
Routes func() []mesh.SyntheticRoute
LocalGateway func(vpnConnectionID string) bool
AllowLegacyLocalGatewayFallback bool
FlowScheduler *FabricFlowScheduler
MaxParallelFlowSends int
RecoveryPolicyFingerprint string
AdaptivePolicyFingerprint string
PreventLastRouteWithdrawal bool
ClusterID string
LocalNodeID string
@@ -1623,7 +1625,7 @@ func (i *FabricClientPacketIngress) ReceiveClientPacketBatch(ctx context.Context
}
func (i *FabricClientPacketIngress) localGatewayReady(vpnConnectionID string) bool {
if i == nil || i.inbox() == nil || vpnConnectionID == "" {
if i == nil || !i.AllowLegacyLocalGatewayFallback || i.inbox() == nil || vpnConnectionID == "" {
return false
}
localGateway := i.localGateway()
@@ -1669,6 +1671,7 @@ func (i *FabricClientPacketIngress) routeCandidatesWithPreference(clusterID stri
var preferred []fabricClientRouteCandidate
var alternates []fabricClientRouteCandidate
var deferred []fabricClientRouteCandidate
var withdrawn []fabricClientRouteCandidate
manager := i.routeManager()
if preferredRouteID != "" && manager.isWithdrawn(preferredRouteID) {
if replacementRouteID := manager.replacementRouteID(preferredRouteID); replacementRouteID != "" {
@@ -1684,9 +1687,6 @@ func (i *FabricClientPacketIngress) routeCandidatesWithPreference(clusterID stri
if route.ClusterID != clusterID || route.SourceNodeID != localNodeID || !containsString(route.AllowedChannels, mesh.ProductionChannelVPNPacket) {
continue
}
if manager.isWithdrawn(route.RouteID) {
continue
}
if !route.ExpiresAt.IsZero() && !route.ExpiresAt.After(now) {
continue
}
@@ -1695,6 +1695,10 @@ func (i *FabricClientPacketIngress) routeCandidatesWithPreference(clusterID stri
continue
}
candidate := fabricClientRouteCandidate{Route: route, NextHop: nextHop}
if manager.isWithdrawn(route.RouteID) {
withdrawn = append(withdrawn, candidate)
continue
}
if preferredRouteID != "" && route.RouteID == preferredRouteID {
preferred = append(preferred, candidate)
} else if avoidRouteID != "" && route.RouteID == avoidRouteID {
@@ -1703,9 +1707,32 @@ func (i *FabricClientPacketIngress) routeCandidatesWithPreference(clusterID stri
alternates = append(alternates, candidate)
}
}
if len(preferred) > 0 {
destinationNodeID := strings.TrimSpace(preferred[0].Route.DestinationNodeID)
alternates = filterRouteCandidatesByDestination(alternates, destinationNodeID)
deferred = filterRouteCandidatesByDestination(deferred, destinationNodeID)
}
out := append(preferred, alternates...)
out = i.applyRouteQualityPreferences(out, preferredRouteID)
return append(out, deferred...)
out = append(out, deferred...)
if len(out) == 0 && i.preventLastRouteWithdrawal() {
return withdrawn
}
return out
}
func filterRouteCandidatesByDestination(candidates []fabricClientRouteCandidate, destinationNodeID string) []fabricClientRouteCandidate {
destinationNodeID = strings.TrimSpace(destinationNodeID)
if destinationNodeID == "" || len(candidates) == 0 {
return candidates
}
out := candidates[:0]
for _, candidate := range candidates {
if strings.TrimSpace(candidate.Route.DestinationNodeID) == destinationNodeID {
out = append(out, candidate)
}
}
return out
}
func (i *FabricClientPacketIngress) applyRouteQualityPreferences(candidates []fabricClientRouteCandidate, preferredRouteID string) []fabricClientRouteCandidate {
@@ -1744,6 +1771,15 @@ func (i *FabricClientPacketIngress) applyRouteQualityPreferences(candidates []fa
return out
}
func (i *FabricClientPacketIngress) preventLastRouteWithdrawal() bool {
if i == nil {
return false
}
i.mu.Lock()
defer i.mu.Unlock()
return i.PreventLastRouteWithdrawal
}
func (t *FabricPacketTransport) ReceiveGatewayPacketBatch(ctx context.Context, timeout time.Duration) ([][]byte, error) {
if t == nil || t.Inbox == nil {
return nil, mesh.ErrForwardRuntimeUnavailable