use std::io::{Write}; use std::collections::{HashSet, HashMap}; use anyhow::{Error, bail}; mod helper; //pub use helper::*; mod lexer; pub use lexer::*; mod parser; pub use parser::*; #[derive(Debug, PartialEq)] pub enum AddressFamily { Inet4and6, // both v4 and v6 Inet, // v4 Inet6, // v6 } #[derive(Debug)] pub struct Interface { pub name: String, pub address_family: AddressFamily, pub method_v4: Option, pub method_v6: Option, pub address_v4: Option, pub gateway_v4: Option, pub netmask_v4: Option, pub address_v6: Option, pub gateway_v6: Option, pub netmask_v6: Option, pub options_v4: Vec, pub options_v6: Vec, } impl Interface { pub fn new( name: String, address_family: AddressFamily, config_method: Option, ) -> Self { let config_method_v4 = match address_family { AddressFamily::Inet | AddressFamily::Inet4and6 => Some(config_method.unwrap_or(Token::Static)), _ => None, }; let config_method_v6 = match address_family { AddressFamily::Inet6 | AddressFamily::Inet4and6 => Some(config_method.unwrap_or(Token::Static)), _ => None, }; Self { name, address_family, method_v4: config_method_v4, method_v6: config_method_v6, address_v4: None, gateway_v4: None, netmask_v4: None, address_v6: None, gateway_v6: None, netmask_v6: None, options_v4: Vec::new(), options_v6: Vec::new(), } } fn set_address_v4(&mut self, address: String) -> Result<(), Error> { if self.address_v4.is_none() { self.address_v4 = Some(address); } else { bail!("duplicate IPv4 address."); } Ok(()) } fn set_gateway_v4(&mut self, gateway: String) -> Result<(), Error> { if self.gateway_v4.is_none() { self.gateway_v4 = Some(gateway); } else { bail!("duplicate IPv4 gateway."); } Ok(()) } fn set_netmask_v4(&mut self, mask: u8) -> Result<(), Error> { if self.netmask_v4.is_none() { if mask > 0 && mask <= 32 { self.netmask_v4 = Some(mask); } else { bail!("invalid ipv4 netmaks '{}'", mask); } } else { bail!("duplicate IPv4 netmask."); } Ok(()) } fn set_address_v6(&mut self, address: String) -> Result<(), Error> { if self.address_v6.is_none() { self.address_v6 = Some(address); } else { bail!("duplicate IPv6 address."); } Ok(()) } fn set_gateway_v6(&mut self, gateway: String) -> Result<(), Error> { if self.gateway_v6.is_none() { self.gateway_v6 = Some(gateway); } else { bail!("duplicate IPv4 gateway."); } Ok(()) } fn set_netmask_v6(&mut self, mask: u8) -> Result<(), Error> { if self.netmask_v6.is_none() { if mask > 0 && mask <= 128 { self.netmask_v6 = Some(mask); } else { bail!("invalid ipv6 netmaks '{}'", mask); } } else { bail!("duplicate IPv6 netmask."); } Ok(()) } fn push_addon_option(&mut self, text: String) { match self.address_family { AddressFamily::Inet | AddressFamily::Inet4and6 => self.options_v4.push(text), AddressFamily::Inet6 => self.options_v6.push(text), } } } #[derive(Debug)] enum NetworkOrderEntry { Iface(String), Comment(String), Option(String), } #[derive(Debug)] pub struct NetworkConfig { pub auto_flag: HashSet, interfaces: HashMap, order: Vec, } impl NetworkConfig { pub fn new() -> Self { Self { auto_flag: HashSet::new(), interfaces: HashMap::new(), order: Vec::new(), } } pub fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> { fn method_to_str(method: &Option) -> &str { match method { None => "static", Some(method) => { match method { Token::Static => "static", Token::Loopback => "loopback", Token::Manual => "manual", Token::DHCP => "dhcp", _ => unreachable!(), } } } } fn write_attributes_v4(w: &mut dyn Write, interface: &Interface) -> Result<(), Error> { if let Some(address) = &interface.address_v4 { if let Some(netmask) = interface.netmask_v4 { writeln!(w, " address {}/{}", address, netmask)?; } else { writeln!(w, " address {}", address)?; } } if let Some(gateway) = &interface.gateway_v4 { writeln!(w, " gateway {}", gateway)?; } for option in &interface.options_v4 { writeln!(w, " {}", option)?; } Ok(()) }; fn write_attributes_v6(w: &mut dyn Write, interface: &Interface) -> Result<(), Error> { if let Some(address) = &interface.address_v6 { if let Some(netmask) = interface.netmask_v6 { writeln!(w, " address {}/{}", address, netmask)?; } else { writeln!(w, " address {}", address)?; } } if let Some(gateway) = &interface.gateway_v6 { writeln!(w, " gateway {}", gateway)?; } for option in &interface.options_v6 { writeln!(w, " {}", option)?; } Ok(()) }; fn write_interface(w: &mut dyn Write, config: &NetworkConfig, name: &str, interface: &Interface) -> Result<(), Error> { if config.auto_flag.contains(name) { writeln!(w, "auto {}", name)?; } if interface.address_family == AddressFamily::Inet4and6 && interface.method_v4 == interface.method_v6 { writeln!(w, "iface {} {}", name, method_to_str(&interface.method_v4))?; write_attributes_v4(w, &interface)?; write_attributes_v6(w, &interface)?; writeln!(w)?; } else if interface.address_family == AddressFamily::Inet4and6 { writeln!(w, "iface {} inet {}", name, method_to_str(&interface.method_v4))?; write_attributes_v4(w, &interface)?; writeln!(w)?; writeln!(w, "iface {} inet6 {}", name, method_to_str(&interface.method_v6))?; write_attributes_v6(w, &interface)?; writeln!(w)?; } else if interface.address_family == AddressFamily::Inet { writeln!(w, "iface {} inet {}", name, method_to_str(&interface.method_v4))?; write_attributes_v4(w, &interface)?; writeln!(w)?; } else if interface.address_family == AddressFamily::Inet6 { writeln!(w, "iface {} inet {}", name, method_to_str(&interface.method_v6))?; write_attributes_v6(w, &interface)?; writeln!(w)?; } else { unreachable!(); } Ok(()) } let mut done = HashSet::new(); let mut last_entry_was_comment = false; for entry in self.order.iter() { match entry { NetworkOrderEntry::Comment(comment) => { writeln!(w, "#{}", comment)?; last_entry_was_comment = true; } NetworkOrderEntry::Option(option) => { if last_entry_was_comment { writeln!(w)?; } last_entry_was_comment = false; writeln!(w, "{}", option)?; writeln!(w)?; } NetworkOrderEntry::Iface(name) => { let interface = match self.interfaces.get(name) { Some(interface) => interface, None => continue, }; if last_entry_was_comment { writeln!(w)?; } last_entry_was_comment = false; if done.contains(name) { continue; } done.insert(name); write_interface(w, self, name, interface)?; } } } for (name, interface) in &self.interfaces { if done.contains(name) { continue; } write_interface(w, self, name, interface)?; } Ok(()) } }