Skip to content

Commit 2c9849d

Browse files
committed
feat: Option to disable masquerade
1 parent ca86815 commit 2c9849d

File tree

6 files changed

+94
-18
lines changed

6 files changed

+94
-18
lines changed

src/firewall/firewalld.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,15 @@ impl firewall::FirewallDriver for FirewallD {
4949
))
5050
}
5151
};
52+
// Determine if masquerade should be enabled based on SNAT configuration
53+
// For firewalld, we check if either IPv4 or IPv6 SNAT is enabled
54+
// since firewalld policy applies to both address families at the same level.
55+
// Note: Unlike nftables/iptables, firewalld applies masquerade at the policy level,
56+
// not per-subnet, so we use OR logic - if any protocol needs SNAT, enable it.
57+
let enable_masquerade = network_setup.snat_ipv4 || network_setup.snat_ipv6;
58+
5259
need_reload |= match add_policy_if_not_exist(
53-
&self.conn, POLICYNAME, ZONENAME, "ANY", "ACCEPT", true, None,
60+
&self.conn, POLICYNAME, ZONENAME, "ANY", "ACCEPT", enable_masquerade, None,
5461
) {
5562
Ok(b) => b,
5663
Err(e) => {

src/firewall/iptables.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ impl firewall::FirewallDriver for IptablesDriver {
5454
conn = &self.conn6;
5555
}
5656

57+
let enable_snat = if is_ipv6 {
58+
network_setup.snat_ipv6
59+
} else {
60+
network_setup.snat_ipv4
61+
};
62+
5763
let chains = get_network_chains(
5864
conn,
5965
network,
@@ -62,6 +68,7 @@ impl firewall::FirewallDriver for IptablesDriver {
6268
network_setup.bridge_name.clone(),
6369
network_setup.isolation,
6470
network_setup.dns_port,
71+
enable_snat,
6572
);
6673

6774
create_network_chains(chains)?;
@@ -83,6 +90,12 @@ impl firewall::FirewallDriver for IptablesDriver {
8390
if is_ipv6 {
8491
conn = &self.conn6;
8592
}
93+
let enable_snat = if is_ipv6 {
94+
tear.config.snat_ipv6
95+
} else {
96+
tear.config.snat_ipv4
97+
};
98+
8699
let chains = get_network_chains(
87100
conn,
88101
network,
@@ -91,6 +104,7 @@ impl firewall::FirewallDriver for IptablesDriver {
91104
tear.config.bridge_name.clone(),
92105
tear.config.isolation,
93106
tear.config.dns_port,
107+
enable_snat,
94108
);
95109

96110
for c in &chains {

src/firewall/nft.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,17 +379,25 @@ impl firewall::FirewallDriver for Nftables {
379379
));
380380

381381
// Subnet chain: ip daddr != 224.0.0.0/4 masquerade
382-
let multicast_address: IpNet = match subnet {
383-
IpNet::V4(_) => "224.0.0.0/4".parse()?,
384-
IpNet::V6(_) => "ff::00/8".parse()?,
382+
// Only add masquerade rule if SNAT is enabled for this address family
383+
let should_add_masquerade = match subnet {
384+
IpNet::V4(_) => network_setup.snat_ipv4,
385+
IpNet::V6(_) => network_setup.snat_ipv6,
385386
};
386-
batch.add(make_rule(
387-
chain.clone(),
388-
Cow::Owned(vec![
389-
get_subnet_match(&multicast_address, "daddr", stmt::Operator::NEQ),
390-
stmt::Statement::Masquerade(None),
391-
]),
392-
));
387+
388+
if should_add_masquerade {
389+
let multicast_address: IpNet = match subnet {
390+
IpNet::V4(_) => "224.0.0.0/4".parse()?,
391+
IpNet::V6(_) => "ff::00/8".parse()?,
392+
};
393+
batch.add(make_rule(
394+
chain.clone(),
395+
Cow::Owned(vec![
396+
get_subnet_match(&multicast_address, "daddr", stmt::Operator::NEQ),
397+
stmt::Statement::Masquerade(None),
398+
]),
399+
));
400+
}
393401

394402
// Next, populate basic chains with forwarding rules
395403
// Input chain: ip saddr <subnet> udp dport 53 accept

src/firewall/varktables/types.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ pub fn get_network_chains<'a>(
200200
interface_name: String,
201201
isolation: IsolateOption,
202202
dns_port: u16,
203+
enable_snat: bool,
203204
) -> Vec<VarkChain<'a>> {
204205
let mut chains = Vec::new();
205206
let prefixed_network_hash_name = format!("{}-{}", "NETAVARK", network_hash_name);
@@ -218,14 +219,17 @@ pub fn get_network_chains<'a>(
218219
Some(TeardownPolicy::OnComplete),
219220
));
220221

221-
let mut multicast_dest = MULTICAST_NET_V4;
222-
if is_ipv6 {
223-
multicast_dest = MULTICAST_NET_V6;
222+
// Only add MASQUERADE rule if SNAT is enabled for this address family
223+
if enable_snat {
224+
let mut multicast_dest = MULTICAST_NET_V4;
225+
if is_ipv6 {
226+
multicast_dest = MULTICAST_NET_V6;
227+
}
228+
hashed_network_chain.build_rule(VarkRule::new(
229+
format!("! -d {multicast_dest} -j {MASQUERADE}"),
230+
Some(TeardownPolicy::OnComplete),
231+
));
224232
}
225-
hashed_network_chain.build_rule(VarkRule::new(
226-
format!("! -d {multicast_dest} -j {MASQUERADE}"),
227-
Some(TeardownPolicy::OnComplete),
228-
));
229233
chains.push(hashed_network_chain);
230234

231235
// POSTROUTING

src/network/bridge.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,33 @@ impl<'a> Bridge<'a> {
434434
) -> NetavarkResult<(SetupNetwork, PortForwardConfig<'a>)> {
435435
let id_network_hash =
436436
CoreUtils::create_network_hash(&self.info.network.name, MAX_HASH_SIZE);
437+
// Parse SNAT configuration from network options
438+
// Format: "snat_ipv4": "true|false", "snat_ipv6": "true|false"
439+
// Defaults to true for backward compatibility
440+
let snat_ipv4 = match parse_option::<bool>(
441+
&self.info.network.options,
442+
"snat_ipv4",
443+
) {
444+
Ok(Some(val)) => val,
445+
Ok(None) => true, // Default to true
446+
Err(e) => {
447+
debug!("Failed to parse snat_ipv4 option: {}, using default (true)", e);
448+
true
449+
}
450+
};
451+
452+
let snat_ipv6 = match parse_option::<bool>(
453+
&self.info.network.options,
454+
"snat_ipv6",
455+
) {
456+
Ok(Some(val)) => val,
457+
Ok(None) => true, // Default to true
458+
Err(e) => {
459+
debug!("Failed to parse snat_ipv6 option: {}, using default (true)", e);
460+
true
461+
}
462+
};
463+
437464
let sn = SetupNetwork {
438465
subnets: self
439466
.info
@@ -446,6 +473,8 @@ impl<'a> Bridge<'a> {
446473
network_hash_name: id_network_hash.clone(),
447474
isolation: isolate,
448475
dns_port: self.info.dns_port,
476+
snat_ipv4,
477+
snat_ipv6,
449478
};
450479

451480
let mut has_ipv4 = false;

src/network/internal_types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ pub struct SetupNetwork {
2626
pub isolation: IsolateOption,
2727
/// port used for the dns server
2828
pub dns_port: u16,
29+
/// enable SNAT for IPv4 traffic (default: true)
30+
#[serde(default = "default_snat_ipv4")]
31+
pub snat_ipv4: bool,
32+
/// enable SNAT for IPv6 traffic (default: true)
33+
#[serde(default = "default_snat_ipv6")]
34+
pub snat_ipv6: bool,
35+
}
36+
37+
fn default_snat_ipv4() -> bool {
38+
true
39+
}
40+
41+
fn default_snat_ipv6() -> bool {
42+
true
2943
}
3044

3145
#[derive(Debug)]

0 commit comments

Comments
 (0)