Why touch the Egress Gateway at all?
Cilium’s Egress Gateway gives pods a predictable, stable source IP when they talk to the outside world. Current gateway implementation, at least the community version
- All Nat convertion is handled at the gateway, this creates a single point of conntracking for port/address translation and overloading.
The idea: Split-Stage Deterministic NAT
Using;
- MAP-T (RFC 7599) — stateless, algorithmic address+port mapping.
- A+P (RFC 6346) — giving an edge device a slice of the port range instead of a whole address.
- Deterministic CGNAT (RFC 7422) — derive the public identity arithmetically from the subscriber, so you can log/reverse without per-flow state.
Instead of one stateful hop, the pipeline is split into two eBPF stages:
Stage-1 — Worker / CE node (A+P clamp).
The worker acts as the Customer Edge. It compresses the pod’s random outbound
ephemeral source port into the node’s assigned deterministic port block
(port_lo .. port_hi). The worker’s own conntrack remembers
ephemeral ↔ block_port for that pod — this is the only state, and it lives
on the source node where it belongs.
Stage-2 — Gateway / Border Relay (pure arithmetic). The gateway acts as a stateless Border Relay. Forward SNAT and reverse de-NAT are a bit-shift:
Thus instead of directing traffic from all nodes to the gateway for NAT, simply each node handles the NAT overlading and uses conntrack.
Gateways simply used for return traffic to appropriate node and then using the connt track to the right POD.
Example translation flow
Real capture from the lab (blockSize = 2048 → shift = 11, NAT pool
172.16.99.0/28):
# forward — pod on a worker (CE)
10.1.0.33:57906 ── Stage-1 (A+P clamp) ─▶ 10.1.0.33:9139 # ephemeral → node port block; node CT keeps 57906 ↔ 9139
── Stage-2 (BR SNAT) ─▶ 172.16.99.0:9139 ─▶ 255.255.0.1:8080
# node_index = 9139 >> 11 = 4 → NAT IP + block derived arithmetically
# return — may hit ANY gateway (active-active ECMP)
255.255.0.1:8080 ─▶ 172.16.99.0:9139 ── Stage-2 reverse (stateless) ─▶ node[ 9139 >> 11 ]
# gateway holds no CT — it recomputes the owner node from the port
── node CT reverse ──▶ 10.1.0.33:57906 # delivered back to the pod
It’s a contained addition to the existing Egress Gateway stack — and crucially, no new eBPF maps on the gateway:
- CRD —
egressGatewayMode: deterministicplus adeterministicConfig(natIPv4Pool,blockSize,podIPv4CIDRs,podIPv4CIDRMaskSize) onCiliumEgressGatewayPolicy, and anegressGatewayslist for active-active. - eBPF datapath — the two-stage clamp / SNAT / stateless reverse in
bpf/lib/egress_gateway.h, wired into the from-pod and from-overlay hooks (bpf/bpf_host.c,bpf/bpf_overlay.c, reply path inbpf/lib/nodeport.h). - Control plane / operator — deterministic pool sizing and fail-fast policy
validation in
pkg/egressgateway/andoperator/pkg/egressgateway/. - Observability — a new
DROP_NAT_BLOCK_EXHAUSTEDdrop reason, surfaced incilium monitorand Hubble flows.
The content of this post was created by the author; AI was used for editing and restructuring.