Skip to content

Commit 6d8746f

Browse files
committed
add config network.nic.snat.enabled to setup nic aware snat rule
1 parent 4a9d0f4 commit 6d8746f

8 files changed

Lines changed: 52 additions & 28 deletions

File tree

api/src/main/java/com/cloud/agent/api/to/StaticNatRuleTO.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
public class StaticNatRuleTO extends FirewallRuleTO {
3030
String dstIp;
31-
boolean destinationIpOnDefaultNic = true;
31+
boolean shouldApplyCrossNetworkSnat = false;
3232

3333
protected StaticNatRuleTO() {
3434
}
@@ -80,12 +80,12 @@ public String getDstIp() {
8080
return dstIp;
8181
}
8282

83-
public boolean isDestinationIpOnDefaultNic() {
84-
return destinationIpOnDefaultNic;
83+
public boolean isShouldApplyCrossNetworkSnat() {
84+
return shouldApplyCrossNetworkSnat;
8585
}
8686

87-
public void setDestinationIpOnDefaultNic(boolean destinationIpOnDefaultNic) {
88-
this.destinationIpOnDefaultNic = destinationIpOnDefaultNic;
87+
public void setShouldApplyCrossNetworkSnat(boolean shouldApplyCrossNetworkSnat) {
88+
this.shouldApplyCrossNetworkSnat = shouldApplyCrossNetworkSnat;
8989
}
9090

9191
}

core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetStaticNatRulesConfigItem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public List<ConfigItem> generateConfig(final NetworkElementCommand cmd) {
4040
final LinkedList<StaticNatRule> rules = new LinkedList<>();
4141
for (final StaticNatRuleTO rule : command.getRules()) {
4242
final StaticNatRule staticNatRule = new StaticNatRule(rule.revoked(), rule.getProtocol(), rule.getSrcIp(),
43-
rule.getStringSrcPortRange(), rule.getDstIp(), rule.isDestinationIpOnDefaultNic());
43+
rule.getStringSrcPortRange(), rule.getDstIp(), rule.isShouldApplyCrossNetworkSnat());
4444
rules.add(staticNatRule);
4545
}
4646
final StaticNatRules staticNatRules = new StaticNatRules(rules);

core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/StaticNatRule.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,25 @@ public class StaticNatRule {
2525
private String sourceIpAddress;
2626
private String sourcePortRange;
2727
private String destinationIpAddress;
28-
private boolean destinationIpOnDefaultNic = true;
28+
private boolean shouldApplyCrossNetworkSnat = false;
2929

3030
public StaticNatRule() {
3131
// Empty constructor for (de)serialization
3232
}
3333

3434
public StaticNatRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress) {
35-
this(revoke, protocol, sourceIpAddress, sourcePortRange, destinationIpAddress, true);
35+
this(revoke, protocol, sourceIpAddress, sourcePortRange, destinationIpAddress, false);
3636
}
3737

3838
public StaticNatRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange,
39-
String destinationIpAddress, boolean destinationIpOnDefaultNic) {
39+
String destinationIpAddress, boolean shouldApplyCrossNetworkSnat) {
4040
super();
4141
this.revoke = revoke;
4242
this.protocol = protocol;
4343
this.sourceIpAddress = sourceIpAddress;
4444
this.sourcePortRange = sourcePortRange;
4545
this.destinationIpAddress = destinationIpAddress;
46-
this.destinationIpOnDefaultNic = destinationIpOnDefaultNic;
46+
this.shouldApplyCrossNetworkSnat = shouldApplyCrossNetworkSnat;
4747
}
4848

4949
public boolean isRevoke() {
@@ -86,12 +86,12 @@ public void setDestinationIpAddress(String destinationIpAddress) {
8686
this.destinationIpAddress = destinationIpAddress;
8787
}
8888

89-
public boolean isDestinationIpOnDefaultNic() {
90-
return destinationIpOnDefaultNic;
89+
public boolean isShouldApplyCrossNetworkSnat() {
90+
return shouldApplyCrossNetworkSnat;
9191
}
9292

93-
public void setDestinationIpOnDefaultNic(boolean destinationIpOnDefaultNic) {
94-
this.destinationIpOnDefaultNic = destinationIpOnDefaultNic;
93+
public void setShouldApplyCrossNetworkSnat(boolean shouldApplyCrossNetworkSnat) {
94+
this.shouldApplyCrossNetworkSnat = shouldApplyCrossNetworkSnat;
9595
}
9696

9797
}

server/src/main/java/com/cloud/network/router/CommandSetupHelper.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ public void createApplyStaticNatRulesCommands(final List<? extends StaticNatRule
446446
for (final StaticNatRule rule : rules) {
447447
final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
448448
final StaticNatRuleTO ruleTO = new StaticNatRuleTO(rule, null, sourceIp.getAddress().addr(), rule.getDestIpAddress());
449-
ruleTO.setDestinationIpOnDefaultNic(isDestinationIpOnDefaultNic(guestNetworkId, rule.getDestIpAddress()));
449+
ruleTO.setShouldApplyCrossNetworkSnat(requiresReturnPathSnat(guestNetworkId, rule.getDestIpAddress()));
450450
rulesTO.add(ruleTO);
451451
}
452452
}
@@ -460,13 +460,23 @@ public void createApplyStaticNatRulesCommands(final List<? extends StaticNatRule
460460
cmds.addCommand(cmd);
461461
}
462462

463-
private boolean isDestinationIpOnDefaultNic(final long networkId, final String destinationIp) {
463+
/**
464+
* This method determines whether to use network-wide SNAT or NIC aware SNAT
465+
* @param networkId
466+
* @param destinationIp
467+
* @return
468+
*/
469+
private boolean requiresReturnPathSnat(final long networkId, final String destinationIp) {
470+
if (!VirtualNetworkApplianceManager.NicSnatEnabled.value()) {
471+
return false;
472+
}
473+
464474
final NicVO destinationNic = _nicDao.findByIp4AddressAndNetworkId(destinationIp, networkId);
465475
if (destinationNic == null) {
466476
logger.debug("Unable to find destination NIC for ip [{}] in network [{}], assuming default NIC.", destinationIp, networkId);
467-
return true;
477+
return false;
468478
}
469-
return destinationNic.isDefaultNic();
479+
return !destinationNic.isDefaultNic();
470480
}
471481

472482
public void createApplyFirewallRulesCommands(final List<? extends FirewallRule> rules, final VirtualRouter router, final Commands cmds, final long guestNetworkId) {
@@ -707,7 +717,7 @@ public void createApplyStaticNatCommands(final List<? extends StaticNat> rules,
707717
final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
708718
final StaticNatRuleTO ruleTO = new StaticNatRuleTO(0, sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(),
709719
false);
710-
ruleTO.setDestinationIpOnDefaultNic(isDestinationIpOnDefaultNic(guestNetworkId, rule.getDestIpAddress()));
720+
ruleTO.setShouldApplyCrossNetworkSnat(requiresReturnPathSnat(guestNetworkId, rule.getDestIpAddress()));
711721
rulesTO.add(ruleTO);
712722
}
713723
}

server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManager.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA
5050
String RouterHealthChecksResultFetchIntervalCK = "router.health.checks.results.fetch.interval";
5151
String RouterHealthChecksFailuresToRecreateVrCK = "router.health.checks.failures.to.recreate.vr";
5252
String RemoveControlIpOnStopCK = "systemvm.release.control.ip.on.stop";
53+
String UseNicAwareSnat = "network.nic.snat.enabled";
5354

5455
ConfigKey<String> RouterTemplateXen = new ConfigKey<>(String.class, RouterTemplateXenCK, "Advanced", "SystemVM Template (XenServer)",
5556
"Name of the default router template on Xenserver.", true, ConfigKey.Scope.Zone, null);
@@ -131,6 +132,17 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA
131132
ConfigKey<Boolean> RemoveControlIpOnStop = new ConfigKey<>(Boolean.class, RemoveControlIpOnStopCK, "Advanced", "true",
132133
"on stopping routers and system VMs the IP will be released to preserve IPv4 space.", true, ConfigKey.Scope.Zone, null);
133134

135+
ConfigKey<Boolean> NicSnatEnabled = new ConfigKey<>(Boolean.class,
136+
UseNicAwareSnat,
137+
"Advanced",
138+
"false",
139+
"When enabled, Virtual Router overwrites the SNAT source IP in POSTROUTING for traffic exiting " +
140+
"non-default NICs. The SNAT source is selected based on the egress interface to preserve return path " +
141+
"consistency in multi-NIC configurations.",
142+
true,
143+
ConfigKey.Scope.Global,
144+
null);
145+
134146
int DEFAULT_ROUTER_VM_RAMSIZE = 256; // 256M
135147
int DEFAULT_ROUTER_CPU_MHZ = 500; // 500 MHz
136148
boolean USE_POD_VLAN = false;

server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3382,7 +3382,8 @@ public ConfigKey<?>[] getConfigKeys() {
33823382
ExposeDnsAndBootpServer,
33833383
RouterLogrotateFrequency,
33843384
RemoveControlIpOnStop,
3385-
VirtualRouterUserData
3385+
VirtualRouterUserData,
3386+
NicSnatEnabled
33863387
};
33873388
}
33883389

systemvm/debian/opt/cloud/bin/configure.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,17 +1563,18 @@ def processStaticNatRule(self, rule):
15631563
self.fw.append(["filter", "",
15641564
"-A FORWARD -i %s -o eth0 -d %s -m state --state NEW -j ACCEPT " % (device, rule["internal_ip"])])
15651565

1566-
# Configure the hairpin snat
1567-
self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" %
1568-
(self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))])
1569-
1570-
destination_ip_on_default_nic = rule.get("destination_ip_on_default_nic", True)
1571-
if not destination_ip_on_default_nic:
1566+
# Configure the hairpin snat for default nic or nic-aware snat for non-default
1567+
apply_cross_network_snat = rule.get("should_apply_cross_network_snat", False)
1568+
if apply_cross_network_snat:
15721569
internal_device = self.getDeviceByIp(rule["internal_ip"])
15731570
internal_vr_ip = self.getGuestIpByIp(rule["internal_ip"])
15741571
if internal_device and internal_vr_ip and internal_device != device:
15751572
self.fw.append(["nat", "front",
15761573
"-A POSTROUTING -o %s -d %s/32 -j SNAT --to-source %s" % (internal_device, rule["internal_ip"], internal_vr_ip)])
1574+
else:
1575+
self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" %
1576+
(self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))])
1577+
15771578

15781579

15791580
class IpTablesExecutor:

systemvm/debian/opt/cloud/bin/cs_forwardingrules.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def merge(dbag, rules):
2727
newrule = dict()
2828
newrule["public_ip"] = source_ip
2929
newrule["internal_ip"] = destination_ip
30-
if "destination_ip_on_default_nic" in rule:
31-
newrule["destination_ip_on_default_nic"] = rule["destination_ip_on_default_nic"]
30+
if "should_apply_cross_network_snat" in rule:
31+
newrule["should_apply_cross_network_snat"] = rule["should_apply_cross_network_snat"]
3232

3333
if rules["type"] == "staticnatrules":
3434
newrule["type"] = "staticnat"

0 commit comments

Comments
 (0)