use crate::constants; use crate::utils::net_utils; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; #[derive(thiserror::Error, Debug)] enum NatDefError { #[error("Invalid nat definition: {0}")] InvalidNatDef(&'static str), } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum NatSourceIP { Interface { name: String }, Ip { ip: IPv }, } #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] pub enum NatProtocol { TCP, UDP, Both, } impl NatProtocol { pub fn has_tcp(&self) -> bool { !matches!(&self, NatProtocol::UDP) } pub fn has_udp(&self) -> bool { !matches!(&self, NatProtocol::TCP) } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum NatHostPort { Single { port: u16 }, Range { start: u16, end: u16 }, } impl NatHostPort { pub fn as_seq(&self) -> Vec { match self { NatHostPort::Single { port } => vec![*port], NatHostPort::Range { start, end } => (*start..(*end + 1)).collect(), } } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Nat { pub protocol: NatProtocol, pub host_ip: NatSourceIP, pub host_port: NatHostPort, pub guest_ip: IPv, pub guest_port: u16, pub comment: Option, } impl Nat { pub fn check(&self) -> anyhow::Result<()> { if let NatSourceIP::Interface { name } = &self.host_ip { if !net_utils::is_net_interface_name_valid(name) { return Err(NatDefError::InvalidNatDef("Invalid nat interface name!").into()); } } if let NatHostPort::Range { start, end } = &self.host_port { if *start == 0 { return Err(NatDefError::InvalidNatDef("Invalid start range!").into()); } if start > end { return Err(NatDefError::InvalidNatDef("Invalid port range!").into()); } if u16::MAX - (end - start) < self.guest_port { return Err(NatDefError::InvalidNatDef("Guest port is too high!").into()); } } if self.guest_port == 0 { return Err(NatDefError::InvalidNatDef("Invalid guest port!").into()); } if let Some(comment) = &self.comment { if comment.len() > constants::NET_NAT_COMMENT_MAX_SIZE { return Err(NatDefError::InvalidNatDef("Comment is too large!").into()); } } Ok(()) } } impl Nat { pub fn generalize(&self) -> Nat { Nat { protocol: self.protocol, host_ip: match &self.host_ip { NatSourceIP::Ip { ip } => NatSourceIP::Ip { ip: IpAddr::V4(*ip), }, NatSourceIP::Interface { name } => NatSourceIP::Interface { name: name.to_string(), }, }, host_port: self.host_port.clone(), guest_ip: IpAddr::V4(self.guest_ip), guest_port: self.guest_port, comment: self.comment.clone(), } } } impl Nat { pub fn generalize(&self) -> Nat { Nat { protocol: self.protocol, host_ip: match &self.host_ip { NatSourceIP::Ip { ip } => NatSourceIP::Ip { ip: IpAddr::V6(*ip), }, NatSourceIP::Interface { name } => NatSourceIP::Interface { name: name.to_string(), }, }, host_port: self.host_port.clone(), guest_ip: IpAddr::V6(self.guest_ip), guest_port: self.guest_port, comment: self.comment.clone(), } } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] pub struct NetNat { pub interface: String, pub ipv4: Option>>, pub ipv6: Option>>, }