From 2a88f86b285fd65ae6d597fadab95e2a139c1bf2 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Wed, 20 May 2026 16:04:38 +0530 Subject: [PATCH 1/2] Fix Static NAT/Port Forwarding when VM NIC is not the default --- systemvm/debian/opt/cloud/bin/configure.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index bf48be66694c..22671b95a071 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -1448,7 +1448,7 @@ def forward_vr(self, rule): ) fw4 = "-j SNAT --to-source %s -A POSTROUTING -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ ( - self.getGuestIp(), + self.getGuestIpByIp(rule['internal_ip']), self.getNetworkByIp(rule['internal_ip']), rule['internal_ip'], internal_fwinterface, @@ -1567,6 +1567,13 @@ def processStaticNatRule(self, rule): self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" % (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))]) + internal_device = self.getDeviceByIp(rule["internal_ip"]) + internal_vr_ip = self.getGuestIpByIp(rule["internal_ip"]) + if internal_device and internal_vr_ip and internal_device != device: + self.fw.append(["nat", "front", + "-A POSTROUTING -o %s -d %s/32 -j SNAT --to-source %s" % + (internal_device, rule["internal_ip"], internal_vr_ip)]) + class IpTablesExecutor: From 4a9d0f43bba187a66681bea561a1e46fe8180561 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Thu, 21 May 2026 17:24:38 +0530 Subject: [PATCH 2/2] rewrite source only for non-default nics --- .../com/cloud/agent/api/to/StaticNatRuleTO.java | 9 +++++++++ .../facade/SetStaticNatRulesConfigItem.java | 3 ++- .../virtualnetwork/model/StaticNatRule.java | 15 +++++++++++++++ .../cloud/network/router/CommandSetupHelper.java | 11 +++++++++++ systemvm/debian/opt/cloud/bin/configure.py | 13 +++++++------ .../debian/opt/cloud/bin/cs_forwardingrules.py | 2 ++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/com/cloud/agent/api/to/StaticNatRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/StaticNatRuleTO.java index 8064c301991a..4cac660ace1b 100644 --- a/api/src/main/java/com/cloud/agent/api/to/StaticNatRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/StaticNatRuleTO.java @@ -28,6 +28,7 @@ public class StaticNatRuleTO extends FirewallRuleTO { String dstIp; + boolean destinationIpOnDefaultNic = true; protected StaticNatRuleTO() { } @@ -79,4 +80,12 @@ public String getDstIp() { return dstIp; } + public boolean isDestinationIpOnDefaultNic() { + return destinationIpOnDefaultNic; + } + + public void setDestinationIpOnDefaultNic(boolean destinationIpOnDefaultNic) { + this.destinationIpOnDefaultNic = destinationIpOnDefaultNic; + } + } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetStaticNatRulesConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetStaticNatRulesConfigItem.java index 0439b168a9da..1b60cc6ca7bb 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetStaticNatRulesConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetStaticNatRulesConfigItem.java @@ -39,7 +39,8 @@ public List generateConfig(final NetworkElementCommand cmd) { final LinkedList rules = new LinkedList<>(); for (final StaticNatRuleTO rule : command.getRules()) { - final StaticNatRule staticNatRule = new StaticNatRule(rule.revoked(), rule.getProtocol(), rule.getSrcIp(), rule.getStringSrcPortRange(), rule.getDstIp()); + final StaticNatRule staticNatRule = new StaticNatRule(rule.revoked(), rule.getProtocol(), rule.getSrcIp(), + rule.getStringSrcPortRange(), rule.getDstIp(), rule.isDestinationIpOnDefaultNic()); rules.add(staticNatRule); } final StaticNatRules staticNatRules = new StaticNatRules(rules); diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/StaticNatRule.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/StaticNatRule.java index a375a913b284..417c5edc11de 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/StaticNatRule.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/StaticNatRule.java @@ -25,18 +25,25 @@ public class StaticNatRule { private String sourceIpAddress; private String sourcePortRange; private String destinationIpAddress; + private boolean destinationIpOnDefaultNic = true; public StaticNatRule() { // Empty constructor for (de)serialization } public StaticNatRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress) { + this(revoke, protocol, sourceIpAddress, sourcePortRange, destinationIpAddress, true); + } + + public StaticNatRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, + String destinationIpAddress, boolean destinationIpOnDefaultNic) { super(); this.revoke = revoke; this.protocol = protocol; this.sourceIpAddress = sourceIpAddress; this.sourcePortRange = sourcePortRange; this.destinationIpAddress = destinationIpAddress; + this.destinationIpOnDefaultNic = destinationIpOnDefaultNic; } public boolean isRevoke() { @@ -79,4 +86,12 @@ public void setDestinationIpAddress(String destinationIpAddress) { this.destinationIpAddress = destinationIpAddress; } + public boolean isDestinationIpOnDefaultNic() { + return destinationIpOnDefaultNic; + } + + public void setDestinationIpOnDefaultNic(boolean destinationIpOnDefaultNic) { + this.destinationIpOnDefaultNic = destinationIpOnDefaultNic; + } + } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index c6296682bb09..1214e8e3d262 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -446,6 +446,7 @@ public void createApplyStaticNatRulesCommands(final List rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) { final List rulesTO = new ArrayList<>(); String systemRule = null; @@ -697,6 +707,7 @@ public void createApplyStaticNatCommands(final List rules, final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); final StaticNatRuleTO ruleTO = new StaticNatRuleTO(0, sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(), false); + ruleTO.setDestinationIpOnDefaultNic(isDestinationIpOnDefaultNic(guestNetworkId, rule.getDestIpAddress())); rulesTO.add(ruleTO); } } diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index 22671b95a071..787a6c97ac55 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -1567,12 +1567,13 @@ def processStaticNatRule(self, rule): self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" % (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))]) - internal_device = self.getDeviceByIp(rule["internal_ip"]) - internal_vr_ip = self.getGuestIpByIp(rule["internal_ip"]) - if internal_device and internal_vr_ip and internal_device != device: - self.fw.append(["nat", "front", - "-A POSTROUTING -o %s -d %s/32 -j SNAT --to-source %s" % - (internal_device, rule["internal_ip"], internal_vr_ip)]) + destination_ip_on_default_nic = rule.get("destination_ip_on_default_nic", True) + if not destination_ip_on_default_nic: + internal_device = self.getDeviceByIp(rule["internal_ip"]) + internal_vr_ip = self.getGuestIpByIp(rule["internal_ip"]) + if internal_device and internal_vr_ip and internal_device != device: + self.fw.append(["nat", "front", + "-A POSTROUTING -o %s -d %s/32 -j SNAT --to-source %s" % (internal_device, rule["internal_ip"], internal_vr_ip)]) class IpTablesExecutor: diff --git a/systemvm/debian/opt/cloud/bin/cs_forwardingrules.py b/systemvm/debian/opt/cloud/bin/cs_forwardingrules.py index 199d4e77a988..f106d6b7a2db 100755 --- a/systemvm/debian/opt/cloud/bin/cs_forwardingrules.py +++ b/systemvm/debian/opt/cloud/bin/cs_forwardingrules.py @@ -27,6 +27,8 @@ def merge(dbag, rules): newrule = dict() newrule["public_ip"] = source_ip newrule["internal_ip"] = destination_ip + if "destination_ip_on_default_nic" in rule: + newrule["destination_ip_on_default_nic"] = rule["destination_ip_on_default_nic"] if rules["type"] == "staticnatrules": newrule["type"] = "staticnat"