Skip to content

fix(cni): Keep NAT64 tenants off the standby egress shard - #861

Closed
privateip wants to merge 1 commit into
mainfrom
fix/egress-nat64-standby
Closed

privateip wants to merge 1 commit into
mainfrom
fix/egress-nat64-standby

Conversation

@privateip

Copy link
Copy Markdown
Collaborator

Summary

In hashed egress mode, compute nodes place NAT64 tenants on a site's standby edge shard, whose IPv4 replies all arrive at the primary and are dropped.

A shard marked nat64Standby now takes no NAT64 tenants while a primary is reachable, and takes over when none is. Tenants pinned to it during a failover move back once the primary returns, and NAT66 is unchanged.

Pool metrics report a held standby as standby, which the pool minimum alert counts, so a two-shard site still meets a minimum of 2.

Before merging

Important

This builds against an unreleased pseudo-version of datum-cloud/network#38 and must move to a tagged network release. Infra's copy of the pool minimum check counts only active and needs the same active|standby change before nat64Standby is rendered.

Test plan

  • With both edges up, NAT64 tenants all reach the primary and get replies, and NAT66 still spreads across both
  • With the primary withdrawn, NAT64 tenants move to the standby, and back once the primary returns
  • Unit, datapath (root) and alert rule tests pass

Related to datum-cloud/infra#7033
Related to datum-cloud/network#38

🤖 Generated with Claude Code

Each edge site runs two egress shards, and both announce the site's IPv4 unicast prefix upstream. The standby prepends its announcement, so every IPv4 reply, including replies to the standby's own shardAddressIPv4, arrives at the primary, which holds no session for it and drops it. In hashed egress mode the standby is a member of every NAT64 class it translates, so tenants hashed to it lose NAT64 entirely. NAT66 is unaffected, since each shard announces its own IPv6 /128.

EgressShard now carries spec.nat64Standby (go.datum.net/network, pinned here to a pseudo-version of the unreleased branch). egressShardCandidates marks such a shard a standby for NAT64 classes only. ShardGroupTable.Apply, which is where each candidate's SID is resolved, holds every standby out of a NAT64 group while any candidate without the flag resolves, draining or not, because a draining primary still attracts the standby's replies. A held standby keeps its slot and generation but is published without the alive flag, so it gets no Maglev slot and usid_egress rejects every pin to it as stale; a tenant pinned to it during a failover is re-placed on its next packet. Apply decides afresh on every sweep, and the sweep already runs on every route or neighbour change near a member's locator, so the standby takes the class as soon as no primary resolves and is held again when one returns. A local primary does not count, since this node cannot send it tenants.

The pool metric reports a held standby as a new standby state. GalacticEgressPoolBelowMinimum counts it toward GALACTIC_CNI_EGRESS_POOL_MIN_ACTIVE, since it takes over without anyone acting; otherwise every NAT64 class would sit below a minimum of 2 at a two-shard site.

Related to datum-cloud/infra#7033

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@privateip privateip closed this Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant