From a795c2b2048ad06e60a794a75b69b7c85dd0a43c Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Tue, 21 Jul 2026 08:48:05 +1000 Subject: [PATCH] fix(vpc): clear subnet drop gate when installing egress reroute A subnet with an attached IGW but no 0.0.0.0/0 route carries a priority-1100 drop gate on the VPC router. When a route later lands (NATGW reroute at 900, IGW reroute at 1000) the reroute is installed but the drop gate was never removed, so the higher-priority gate wins and black-holes the subnet's egress in lr_in_policy before it reaches routing or SNAT. This left NAT-gateway private-subnet egress with 100% packet loss: the SNAT stage saw zero packets and no conntrack was committed. Remove the subnet's drop gate for the same prefix once a reroute is installed. Idempotent and prefix-scoped, so a specific-prefix reroute is a no-op and leaves the default gate intact. --- spinifex/network/external/igw.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/spinifex/network/external/igw.go b/spinifex/network/external/igw.go index 73bcfec7b..99b470db0 100644 --- a/spinifex/network/external/igw.go +++ b/spinifex/network/external/igw.go @@ -445,14 +445,26 @@ func (m *igwManager) ensureSubnetEgressAtPriority(ctx context.Context, vpcID, su if nexthop == "" { return fmt.Errorf("%s: no gateway nexthop available for %s (IGW not attached?)", opName, vpcID) } - return m.routes.AddSubnetEgress(ctx, vpcID, policy.SubnetEgressSpec{ + if err := m.routes.AddSubnetEgress(ctx, vpcID, policy.SubnetEgressSpec{ SubnetID: subnetID, Prefix: prefix, Nexthop: nexthop, OutputPort: topology.GatewayRouterPort(vpcID), Priority: priority, ExcludeCIDRs: m.rerouteExcludeCIDRs(ctx, vpcID), - }) + }); err != nil { + return err + } + // The priority-1100 drop gate is installed while a subnet is routeless and + // outranks both reroutes (IGW 1000, NATGW 900), so a gate left in place once + // a route lands black-holes the subnet's egress before it reaches routing or + // SNAT. Clear it now that this prefix has a reroute. Idempotent and + // prefix-scoped: gates only exist for the default route, so a specific-prefix + // reroute is a no-op and leaves the default gate intact. + if err := m.RemoveSubnetEgressDrop(ctx, vpcID, subnetID, prefix); err != nil { + return fmt.Errorf("%s: clear drop gate for %s: %w", opName, subnetID, err) + } + return nil } // EnsureSystemInstanceEgress installs a /32 reroute above the drop gate plus a plain