diff --git a/src/iptables.rs b/src/iptables.rs index 1cc82f7..bed2371 100644 --- a/src/iptables.rs +++ b/src/iptables.rs @@ -158,16 +158,16 @@ pub(crate) async fn forward_postrouting( proto: Proto, internal_ip: Ipv4Addr, internal_mask: u8, + internal_port: u16, external_ip: Ipv4Addr, - external_port: u16, destination_ip: Ipv4Addr, ) -> Result<(), anyhow::Error> { forward_postrouting_snat( proto, internal_ip, internal_mask, + internal_port, external_ip, - external_port, destination_ip, |cmd| cmd.arg("-I"), ) @@ -178,16 +178,16 @@ pub(crate) async fn delete_forward_postrouting( proto: Proto, internal_ip: Ipv4Addr, internal_mask: u8, + internal_port: u16, external_ip: Ipv4Addr, - external_port: u16, destination_ip: Ipv4Addr, ) -> Result<(), anyhow::Error> { forward_postrouting_snat( proto, internal_ip, internal_mask, + internal_port, external_ip, - external_port, destination_ip, |cmd| cmd.arg("-D"), ) @@ -198,8 +198,8 @@ async fn forward_postrouting_snat( proto: Proto, internal_ip: Ipv4Addr, internal_mask: u8, + internal_port: u16, external_ip: Ipv4Addr, - external_port: u16, destination_ip: Ipv4Addr, func: impl Fn(&mut Command) -> &mut Command, ) -> Result<(), anyhow::Error> { @@ -215,7 +215,7 @@ async fn forward_postrouting_snat( "-m", proto.as_iptables_str(), "--dport", - &external_port.to_string(), + &internal_port.to_string(), "-m", "conntrack", "--ctstate", diff --git a/src/rules.rs b/src/rules.rs index 0a47cc8..1978c19 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -109,34 +109,37 @@ pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), any dest_port, ) .await?; - for iface in &interfaces.internal { - iptables::delete_forward_postrouting( - rule.proto, - iface.ip, - iface.mask, - interfaces.external.ip, - rule.port, - dest_ip, - ) - .await?; - } - for iface in &interfaces.tunnel { - if interfaces + for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) { + let is_nat_iface = interfaces .nats .iter() - .any(|nat_iface| *nat_iface == iface.interface) - { + .any(|nat_iface| *nat_iface == iface.interface); + + let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| { + *nat_iface != iface.interface + && interfaces + .internal + .iter() + .chain(interfaces.tunnel.iter()) + .any(|other_iface| { + *nat_iface == other_iface.interface + && other_iface.ip == iface.ip + && other_iface.mask == iface.mask + }) + }); + + if is_nat_iface { iptables::delete_forward_prerouting( rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port, ) .await?; - } else { + } else if !has_nat_subnet { iptables::delete_forward_postrouting( rule.proto, iface.ip, iface.mask, + dest_port, interfaces.external.ip, - rule.port, dest_ip, ) .await?; @@ -192,34 +195,37 @@ pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), any dest_port, ) .await?; - for iface in &interfaces.internal { - iptables::forward_postrouting( - rule.proto, - iface.ip, - iface.mask, - interfaces.external.ip, - rule.port, - dest_ip, - ) - .await?; - } - for iface in &interfaces.tunnel { - if interfaces + for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) { + let is_nat_iface = interfaces .nats .iter() - .any(|nat_iface| *nat_iface == iface.interface) - { + .any(|nat_iface| *nat_iface == iface.interface); + + let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| { + *nat_iface != iface.interface + && interfaces + .internal + .iter() + .chain(interfaces.tunnel.iter()) + .any(|other_iface| { + *nat_iface == other_iface.interface + && other_iface.ip == iface.ip + && other_iface.mask == iface.mask + }) + }); + + if is_nat_iface { iptables::forward_prerouting( rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port, ) .await?; - } else { + } else if !has_nat_subnet { iptables::forward_postrouting( rule.proto, iface.ip, iface.mask, + dest_port, interfaces.external.ip, - rule.port, dest_ip, ) .await?; diff --git a/src/startup/preload.rs b/src/startup/preload.rs index 348bbef..4c21871 100644 --- a/src/startup/preload.rs +++ b/src/startup/preload.rs @@ -198,6 +198,52 @@ fn filter(interfaces: &Interfaces) -> String { // FORWARD: Forwarding traffic across interfaces + for nat_iface in &interfaces.nats { + if let Some(iface) = interfaces + .internal + .iter() + .chain(interfaces.tunnel.iter()) + .find(|iface| iface.interface == *nat_iface) + { + filter += &format!( + "-A FORWARD -i {natif} -d {natip}/{natmask} -j ACCEPT\n", + natif = iface.interface, + natip = iface.ip, + natmask = iface.mask, + ); + } + + for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) { + let is_nat_iface = *nat_iface == iface.interface; + + let has_nat_subnet = interfaces + .internal + .iter() + .chain(interfaces.tunnel.iter()) + .any(|other_iface| { + *nat_iface == other_iface.interface + && other_iface.ip == iface.ip + && other_iface.mask == iface.mask + }); + + if !is_nat_iface && !has_nat_subnet { + // Accept packets over NAT'd tunnel interfaces from pre-existing connections + filter += &format!( + "-A FORWARD -i {natif} -o {intif} -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\n", + natif = nat_iface, + intif = iface.interface, + ); + + // Reject packets over NAT'd tunnel interfaces + filter += &format!( + "-A FORWARD -i {natif} -o {intif} -j REJECT --reject-with icmp-port-unreachable\n", + natif = nat_iface, + intif = iface.interface, + ); + } + } + } + // Accept packets over tunnel interfaces for iface in &interfaces.tunnel { filter += &format!( @@ -273,13 +319,27 @@ fn nat(interfaces: &Interfaces) -> String { ); for nat_iface in &interfaces.nats { - for internal in &interfaces.internal { - nat += &format!( - "-A POSTROUTING -s {intip}/{intmask} -o {natiface} -j MASQUERADE\n", - intip = internal.ip, - intmask = internal.mask, - natiface = nat_iface, - ); + for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) { + let is_nat_iface = *nat_iface == iface.interface; + + let has_nat_subnet = interfaces + .internal + .iter() + .chain(interfaces.tunnel.iter()) + .any(|other_iface| { + *nat_iface == other_iface.interface + && other_iface.ip == iface.ip + && other_iface.mask == iface.mask + }); + + if !is_nat_iface && !has_nat_subnet { + nat += &format!( + "-A POSTROUTING -s {intip}/{intmask} -o {natiface} -j MASQUERADE\n", + intip = iface.ip, + intmask = iface.mask, + natiface = nat_iface, + ); + } } }