From 645a47ff6e9e00b279d25705c6542a5b9ba919c0 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 2 Nov 2020 14:32:35 +0100 Subject: [PATCH] config: support netmask when parsing interfaces file --- src/config/network/helper.rs | 59 ++++++++++++++++++--------- src/config/network/parser.rs | 79 ++++++++++++++++++++++++++++++------ 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/config/network/helper.rs b/src/config/network/helper.rs index 3d59e392..340589f9 100644 --- a/src/config/network/helper.rs +++ b/src/config/network/helper.rs @@ -57,38 +57,57 @@ lazy_static! { } pub fn parse_cidr(cidr: &str) -> Result<(String, u8, bool), Error> { + let (address, mask, is_v6) = parse_address_or_cidr(cidr)?; + if let Some(mask) = mask { + return Ok((address, mask, is_v6)); + } else { + bail!("missing netmask in '{}'", cidr); + } +} + +pub fn check_netmask(mask: u8, is_v6: bool) -> Result<(), Error> { + if is_v6 { + if !(mask >= 1 && mask <= 128) { + bail!("IPv6 mask '{}' is out of range (1..128).", mask); + } + } else { + if !(mask > 0 && mask <= 32) { + bail!("IPv4 mask '{}' is out of range (1..32).", mask); + } + } + Ok(()) +} + +// parse ip address with otional cidr mask +pub fn parse_address_or_cidr(cidr: &str) -> Result<(String, Option, bool), Error> { lazy_static! { pub static ref CIDR_V4_REGEX: Regex = Regex::new( - concat!(r"^(", IPV4RE!(), r")(?:/(\d{1,2}))$") + concat!(r"^(", IPV4RE!(), r")(?:/(\d{1,2}))?$") ).unwrap(); pub static ref CIDR_V6_REGEX: Regex = Regex::new( - concat!(r"^(", IPV6RE!(), r")(?:/(\d{1,3}))$") + concat!(r"^(", IPV6RE!(), r")(?:/(\d{1,3}))?$") ).unwrap(); } if let Some(caps) = CIDR_V4_REGEX.captures(&cidr) { let address = &caps[1]; - let mask = &caps[2]; - let mask = u8::from_str_radix(mask, 10) - .map(|mask| { - if !(mask > 0 && mask <= 32) { - bail!("IPv4 mask '{}' is out of range (1..32).", mask); - } - Ok(mask) - })?; - return Ok((address.to_string(), mask.unwrap(), false)); + if let Some(mask) = caps.get(2) { + let mask = u8::from_str_radix(mask.as_str(), 10)?; + check_netmask(mask, false)?; + return Ok((address.to_string(), Some(mask), false)); + } else { + return Ok((address.to_string(), None, false)); + } } else if let Some(caps) = CIDR_V6_REGEX.captures(&cidr) { let address = &caps[1]; - let mask = &caps[2]; - let mask = u8::from_str_radix(mask, 10) - .map(|mask| { - if !(mask >= 1 && mask <= 128) { - bail!("IPv6 mask '{}' is out of range (1..128).", mask); - } - Ok(mask) - })?; - return Ok((address.to_string(), mask.unwrap(), true)); + if let Some(mask) = caps.get(2) { + let mask = u8::from_str_radix(mask.as_str(), 10)?; + check_netmask(mask, true)?; + return Ok((address.to_string(), Some(mask), true)); + } else { + return Ok((address.to_string(), None, true)); + } } else { bail!("invalid address/mask '{}'", cidr); } diff --git a/src/config/network/parser.rs b/src/config/network/parser.rs index 7c9c69f1..fa4809b3 100644 --- a/src/config/network/parser.rs +++ b/src/config/network/parser.rs @@ -86,20 +86,35 @@ impl NetworkParser { Ok(()) } - fn parse_iface_address(&mut self, interface: &mut Interface) -> Result<(), Error> { - self.eat(Token::Address)?; - let cidr = self.next_text()?; + fn parse_netmask(&mut self) -> Result { + self.eat(Token::Netmask)?; + let netmask = self.next_text()?; - let (_address, _mask, ipv6) = parse_cidr(&cidr)?; - if ipv6 { - interface.set_cidr_v6(cidr)?; + let mask = if let Some(mask) = IPV4_MASK_HASH_LOCALNET.get(netmask.as_str()) { + *mask } else { - interface.set_cidr_v4(cidr)?; - } + match u8::from_str_radix(netmask.as_str(), 10) { + Ok(mask) => mask, + Err(err) => { + bail!("unable to parse netmask '{}'", netmask); + } + } + }; self.eat(Token::Newline)?; - Ok(()) + Ok(mask) + } + + fn parse_iface_address(&mut self) -> Result<(String, Option, bool), Error> { + self.eat(Token::Address)?; + let cidr = self.next_text()?; + + let (_address, mask, ipv6) = parse_address_or_cidr(&cidr)?; + + self.eat(Token::Newline)?; + + Ok((cidr, mask, ipv6)) } fn parse_iface_gateway(&mut self, interface: &mut Interface) -> Result<(), Error> { @@ -191,6 +206,9 @@ impl NetworkParser { address_family_v6: bool, ) -> Result<(), Error> { + let mut netmask = None; + let mut address_list = Vec::new(); + loop { match self.peek()? { Token::Attribute => { self.eat(Token::Attribute)?; }, @@ -214,8 +232,15 @@ impl NetworkParser { } match self.peek()? { - Token::Address => self.parse_iface_address(interface)?, + Token::Address => { + let (cidr, mask, is_v6) = self.parse_iface_address()?; + address_list.push((cidr, mask, is_v6)); + } Token::Gateway => self.parse_iface_gateway(interface)?, + Token::Netmask => { + //Note: netmask is deprecated, but we try to do our best + netmask = Some(self.parse_netmask()?); + } Token::MTU => { let mtu = self.parse_iface_mtu()?; interface.mtu = Some(mtu); @@ -255,8 +280,6 @@ impl NetworkParser { interface.bond_xmit_hash_policy = Some(policy); self.eat(Token::Newline)?; } - Token::Netmask => bail!("netmask is deprecated and no longer supported"), - _ => { // parse addon attributes let option = self.parse_to_eol()?; if !option.is_empty() { @@ -270,6 +293,38 @@ impl NetworkParser { } } + if let Some(netmask) = netmask { + if address_list.len() > 1 { + bail!("unable to apply netmask to multiple addresses (please use cidr notation)"); + } else if address_list.len() == 1 { + let (mut cidr, mask, is_v6) = address_list.pop().unwrap(); + if mask.is_some() { + // address already has a mask - ignore netmask + } else { + check_netmask(netmask, is_v6)?; + cidr.push_str(&format!("/{}", netmask)); + } + if is_v6 { + interface.set_cidr_v6(cidr)?; + } else { + interface.set_cidr_v4(cidr)?; + } + } else { + // no address - simply ignore useless netmask + } + } else { + for (cidr, mask, is_v6) in address_list { + if mask.is_none() { + bail!("missing netmask in '{}'", cidr); + } + if is_v6 { + interface.set_cidr_v6(cidr)?; + } else { + interface.set_cidr_v4(cidr)?; + } + } + } + Ok(()) }