Port forwarding is working

This commit is contained in:
2024-01-10 21:59:41 +01:00
parent ed25eed31e
commit d6c8964380
4 changed files with 246 additions and 35 deletions

View File

@ -1,11 +1,18 @@
use crate::constants;
use crate::libvirt_rest_structures::net::NetworkName;
use crate::nat::nat_definition::NetNat;
use crate::nat::nat_definition::{Nat, NatSourceIP, NetNat};
use crate::utils::net_utils;
use clap::Parser;
use std::collections::HashMap;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
#[derive(thiserror::Error, Debug)]
enum NatConfModeError {
#[error("UpdateFirewall failed!")]
UpdateFirewall,
}
/// VirtWeb NAT configuration mode. This executable should never be executed manually
#[derive(Parser, Debug, Clone)]
@ -49,11 +56,17 @@ pub async fn sub_main() -> anyhow::Result<()> {
let conf_json = std::fs::read_to_string(args.network_file())?;
let conf: NetNat = serde_json::from_str(&conf_json)?;
let ips = net_utils::net_list_and_ips()?;
let nic_ips = net_utils::net_list_and_ips()?;
match (args.operation.as_str(), args.sub_operation.as_str()) {
("started", "begin") => network_started_begin(&conf, &ips).await?,
("stopped", "end") => network_stopped_end(&conf, &ips).await?,
("started", "begin") => {
log::info!("Enable port forwarding for network");
trigger_nat_forwarding(true, &conf, &nic_ips).await?
}
("stopped", "end") => {
log::info!("Disable port forwarding for network");
trigger_nat_forwarding(false, &conf, &nic_ips).await?
}
_ => log::debug!(
"Operation {} - {} not supported",
args.operation,
@ -64,16 +77,156 @@ pub async fn sub_main() -> anyhow::Result<()> {
Ok(())
}
pub async fn network_started_begin(
async fn trigger_nat_forwarding(
enable: bool,
conf: &NetNat,
ips: &HashMap<String, Vec<IpAddr>>,
nic_ips: &HashMap<String, Vec<IpAddr>>,
) -> anyhow::Result<()> {
todo!()
if let Some(ipv4) = &conf.ipv4 {
trigger_nat_forwarding_nat_ipv(
enable,
&conf.interface,
&ipv4.iter().map(|i| i.generalize()).collect::<Vec<_>>(),
nic_ips,
)
.await?;
}
if let Some(ipv6) = &conf.ipv6 {
trigger_nat_forwarding_nat_ipv(
enable,
&conf.interface,
&ipv6.iter().map(|i| i.generalize()).collect::<Vec<_>>(),
nic_ips,
)
.await?;
}
Ok(())
}
pub async fn network_stopped_end(
conf: &NetNat,
ips: &HashMap<String, Vec<IpAddr>>,
async fn trigger_nat_forwarding_nat_ipv(
enable: bool,
net_interface: &str,
rules: &[Nat<IpAddr>],
nic_ips: &HashMap<String, Vec<IpAddr>>,
) -> anyhow::Result<()> {
todo!()
for r in rules {
let host_ips = match &r.host_ip {
NatSourceIP::Interface { name } => nic_ips.get(name).cloned().unwrap_or_default(),
NatSourceIP::Ip { ip } => vec![*ip],
};
for host_ip in host_ips {
let mut guest_port = r.guest_port;
for host_port in r.host_port.as_seq() {
if r.protocol.has_tcp() {
toggle_port_forwarding(
enable,
false,
host_ip,
host_port,
net_interface,
r.guest_ip,
guest_port,
)?
}
if r.protocol.has_udp() {
toggle_port_forwarding(
enable,
true,
host_ip,
host_port,
net_interface,
r.guest_ip,
guest_port,
)?
}
guest_port += 1;
}
}
}
Ok(())
}
fn check_cmd(s: ExitStatus) -> anyhow::Result<()> {
if !s.success() {
log::error!("Failed to update firewall rules!");
return Err(NatConfModeError::UpdateFirewall.into());
}
Ok(())
}
fn toggle_port_forwarding(
enable: bool,
is_udp: bool,
host_ip: IpAddr,
host_port: u16,
net_interface: &str,
guest_ip: IpAddr,
guest_port: u16,
) -> anyhow::Result<()> {
if host_ip.is_ipv4() != guest_ip.is_ipv4() {
log::trace!("Skipping invalid combination {host_ip} -> {guest_ip}");
return Ok(());
}
let program = match host_ip.is_ipv4() {
true => "/sbin/iptables",
false => "/sbin/ip6tables",
};
let protocol = match is_udp {
true => "udp",
false => "tcp",
};
log::info!("Forward (add={enable}) incoming {protocol} connections for {host_ip}:{host_port} to {guest_ip}:{guest_port} int {net_interface}");
// Rule 1
let cmd = Command::new(program)
.arg(match enable {
true => "-I",
false => "-D",
})
.arg("FORWARD")
.arg("-o")
.arg(net_interface)
.arg("-p")
.arg(protocol)
.arg("-d")
.arg(guest_ip.to_string())
.arg("--dport")
.arg(guest_port.to_string())
.arg("-j")
.arg("ACCEPT")
.status()?;
check_cmd(cmd)?;
// Rule 2
let cmd = Command::new(program)
.arg("-t")
.arg("nat")
.arg(match enable {
true => "-I",
false => "-D",
})
.arg("PREROUTING")
.arg("-p")
.arg(protocol)
.arg("-d")
.arg(host_ip.to_string())
.arg("--dport")
.arg(host_port.to_string())
.arg("-j")
.arg("DNAT")
.arg("--to")
.arg(format!("{guest_ip}:{guest_port}"))
.status()?;
check_cmd(cmd)?;
Ok(())
}