Skip to content

F #-: VRouter: NAT4: SNAT to VIP or given address - #418

Open
netaviator wants to merge 3 commits into
OpenNebula:masterfrom
netaviator:pr/vrouter-nat4-snat-to-vip
Open

netaviator wants to merge 3 commits into
OpenNebula:masterfrom
netaviator:pr/vrouter-nat4-snat-to-vip

Conversation

@netaviator

Copy link
Copy Markdown

NAT4 masquerades the traffic leaving through the selected NICs, so the outside address is the address of whichever node forwards it, and it changes with a failover. Add two opt-in ways to get a stable source address. With neither set, the rules are unchanged.

  • ONEAPP_VNF_NAT4_SNAT_ADDRESS: SNAT every outgoing NIC to this address, for example a public address that the upstream routes to the VIP.
  • ONEAPP_VNF_NAT4_SNAT_TO_VIP: SNAT to the first IPv4 VIP of the NIC. A NIC without a VIP keeps MASQUERADE.

The address wins over the VIP. Traffic generated by the router itself is not SNATed.

The SNAT address is added as an extra /32 virtual IP in Keepalived, on the first outgoing NIC that has a VRRP instance, so only the master owns it. Without an owner, a packet for that address with no conntrack entry would be forwarded, leave by the default route and be routed back by the upstream, until the TTL runs out. NAT4 only SNATs to an address Keepalived owns. The address must be outside every NIC subnet and must not be a NIC address, alias or VIP. Otherwise it is ignored with a warning and MASQUERADE stays. Values are checked because they end up in a shell command.

LVS serves load balancers on that address, so a load balancer can use the public address. HAProxy is unchanged, because binding a /32 that only the master holds would fail on the standby.

Services that are not bound to one address (sshd, DNS) also answer on this address on the master, the same as on the VIP.

NAT4 masquerades the traffic leaving through the selected NICs, so the
outside address is the primary address of the NIC of the node that
happens to forward it, and it changes with a failover. Add two opt-in
ways to get a stable source address; with neither set the rules are
unchanged.

* ONEAPP_VNF_NAT4_SNAT_ADDRESS: SNAT every outgoing NIC to this address.
  For example a public address that is statically routed to the VIP: the
  address does not have to be configured on the router for the NAT to
  work (the replies come back through the route and are translated back),
  but to reach it from outside it can be added as an extra VIP
  (ONEAPP_VROUTER_ETH<n>_VIP<m>=<address>/32), which puts it on the VRRP
  master only.
* ONEAPP_VNF_NAT4_SNAT_TO_VIP: for a NIC that has a VIP, SNAT to the first
  IPv4 VIP of the NIC in index order (the OpenNebula floating IP counts).
  IPv6 VIPs are ignored.

The address wins over the VIP. The address ends up in a shell command, so
anything but a plain dotted quad is rejected (a prefix length and
surrounding spaces are stripped): the outgoing NICs then keep MASQUERADE,
also when SNAT to the VIP is enabled, and the problem is logged with the
attribute or the NIC (at most 40 characters of the value are logged). A
NIC without a VIP keeps MASQUERADE.

Checked with unit tests, and on a kernel with three network namespaces
(client, router, server): with SNAT to an address the server sees the
client as that address, with and without the address configured on the
router, and the replies are translated back; with MASQUERADE it sees the
NIC address of the router.

Signed-off-by: Jan Gilla <jgilla@level66.network>
SNAT rewrote the VRRP advertisements the router sends itself to the NAT address, so both nodes appeared with the same source and kept the VIPs.

Signed-off-by: Jan Gilla <jgilla@level66.network>
…ers on it

The SNAT address is added as an extra Keepalived virtual_ipaddress /32 on the
first outgoing NIC that gets a vrrp_instance, so only the MASTER owns it and
the upstream route to the VIP does not loop. LVS serves load balancers
on that address. NAT4 SNATs only to an address the router owns and can use
(not inside a NIC subnet, not an alias or VIP) and falls back to MASQUERADE
otherwise. Keepalived, LVS and NAT4 share one helper.

Signed-off-by: Jan Gilla <jgilla@level66.network>
@netaviator
netaviator force-pushed the pr/vrouter-nat4-snat-to-vip branch from 6df663c to d1e7d7e Compare October 20, 2026 22:18

@roamingengineer311 roamingengineer311 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I manually tested the functionality and confirmed it works as described.
I also updated the wiki documentation to cover the new functionality.

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.

2 participants