1 use std
::io
::{BufRead}
;
2 use std
::iter
::{Peekable, Iterator}
;
3 use std
::collections
::{HashMap, HashSet}
;
5 use anyhow
::{Error, bail, format_err}
;
6 use lazy_static
::lazy_static
;
12 use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType, bond_mode_from_str, bond_xmit_hash_policy_from_str}
;
14 fn set_method_v4(iface
: &mut Interface
, method
: NetworkConfigMethod
) -> Result
<(), Error
> {
15 if iface
.method
.is_none() {
16 iface
.method
= Some(method
);
18 bail
!("inet configuration method already set.");
23 fn set_method_v6(iface
: &mut Interface
, method
: NetworkConfigMethod
) -> Result
<(), Error
> {
24 if iface
.method6
.is_none() {
25 iface
.method6
= Some(method
);
27 bail
!("inet6 configuration method already set.");
32 fn set_cidr_v4(iface
: &mut Interface
, address
: String
) -> Result
<(), Error
> {
33 if iface
.cidr
.is_none() {
34 iface
.cidr
= Some(address
);
36 bail
!("duplicate IPv4 address.");
41 fn set_gateway_v4(iface
: &mut Interface
, gateway
: String
) -> Result
<(), Error
> {
42 if iface
.gateway
.is_none() {
43 iface
.gateway
= Some(gateway
);
45 bail
!("duplicate IPv4 gateway.");
50 fn set_cidr_v6(iface
: &mut Interface
, address
: String
) -> Result
<(), Error
> {
51 if iface
.cidr6
.is_none() {
52 iface
.cidr6
= Some(address
);
54 bail
!("duplicate IPv6 address.");
59 fn set_gateway_v6(iface
: &mut Interface
, gateway
: String
) -> Result
<(), Error
> {
60 if iface
.gateway6
.is_none() {
61 iface
.gateway6
= Some(gateway
);
63 bail
!("duplicate IPv4 gateway.");
68 fn set_interface_type(iface
: &mut Interface
, interface_type
: NetworkInterfaceType
) -> Result
<(), Error
> {
69 if iface
.interface_type
== NetworkInterfaceType
::Unknown
{
70 iface
.interface_type
= interface_type
;
71 } else if iface
.interface_type
!= interface_type
{
72 bail
!("interface type already defined - cannot change from {:?} to {:?}", iface
.interface_type
, interface_type
);
77 pub struct NetworkParser
<R
: BufRead
> {
78 input
: Peekable
<Lexer
<R
>>,
82 impl <R
: BufRead
> NetworkParser
<R
> {
84 pub fn new(reader
: R
) -> Self {
85 let input
= Lexer
::new(reader
).peekable();
86 Self { input, line_nr: 1 }
89 fn peek(&mut self) -> Result
<Token
, Error
> {
90 match self.input
.peek() {
92 bail
!("input error - {}", err
);
94 Some(Ok((token
, _
))) => {
98 bail
!("got unexpected end of stream (inside peek)");
103 fn next(&mut self) -> Result
<(Token
, String
), Error
> {
104 match self.input
.next() {
106 bail
!("input error - {}", err
);
108 Some(Ok((token
, text
))) => {
109 if token
== Token
::Newline { self.line_nr += 1; }
113 bail
!("got unexpected end of stream (inside peek)");
118 fn next_text(&mut self) -> Result
<String
, Error
> {
120 (Token
::Text
, text
) => Ok(text
),
121 (unexpected
, _
) => bail
!("got unexpected token {:?} (expecting Text)", unexpected
),
125 fn eat(&mut self, expected
: Token
) -> Result
<String
, Error
> {
126 let (next
, text
) = self.next()?
;
127 if next
!= expected
{
128 bail
!("expected {:?}, got {:?}", expected
, next
);
133 fn parse_auto(&mut self, auto_flag
: &mut HashSet
<String
>) -> Result
<(), Error
> {
134 self.eat(Token
::Auto
)?
;
138 (Token
::Text
, iface
) => {
139 auto_flag
.insert(iface
.to_string());
141 (Token
::Newline
, _
) => break,
143 bail
!("expected {:?}, got {:?}", Token
::Text
, unexpected
);
152 fn parse_netmask(&mut self) -> Result
<u8, Error
> {
153 self.eat(Token
::Netmask
)?
;
154 let netmask
= self.next_text()?
;
156 let mask
= if let Some(mask
) = IPV4_MASK_HASH_LOCALNET
.get(netmask
.as_str()) {
159 match u8::from_str_radix(netmask
.as_str(), 10) {
162 bail
!("unable to parse netmask '{}' - {}", netmask
, err
);
167 self.eat(Token
::Newline
)?
;
172 fn parse_iface_address(&mut self) -> Result
<(String
, Option
<u8>, bool
), Error
> {
173 self.eat(Token
::Address
)?
;
174 let cidr
= self.next_text()?
;
176 let (_address
, mask
, ipv6
) = parse_address_or_cidr(&cidr
)?
;
178 self.eat(Token
::Newline
)?
;
180 Ok((cidr
, mask
, ipv6
))
183 fn parse_iface_gateway(&mut self, interface
: &mut Interface
) -> Result
<(), Error
> {
184 self.eat(Token
::Gateway
)?
;
185 let gateway
= self.next_text()?
;
187 if proxmox
::tools
::common_regex
::IP_REGEX
.is_match(&gateway
) {
188 if gateway
.contains('
:'
) {
189 set_gateway_v6(interface
, gateway
)?
;
191 set_gateway_v4(interface
, gateway
)?
;
194 bail
!("unable to parse gateway address");
197 self.eat(Token
::Newline
)?
;
202 fn parse_iface_mtu(&mut self) -> Result
<u64, Error
> {
203 self.eat(Token
::MTU
)?
;
205 let mtu
= self.next_text()?
;
206 let mtu
= match u64::from_str_radix(&mtu
, 10) {
209 bail
!("unable to parse mtu value '{}' - {}", mtu
, err
);
213 self.eat(Token
::Newline
)?
;
218 fn parse_yes_no(&mut self) -> Result
<bool
, Error
> {
219 let text
= self.next_text()?
;
220 let value
= match text
.to_lowercase().as_str() {
224 bail
!("unable to bool value '{}' - (expected yes/no)", text
);
228 self.eat(Token
::Newline
)?
;
233 fn parse_to_eol(&mut self) -> Result
<String
, Error
> {
234 let mut line
= String
::new();
237 (Token
::Newline
, _
) => return Ok(line
),
239 if !line
.is_empty() { line.push(' '); }
240 line
.push_str(&text
);
246 fn parse_iface_list(&mut self) -> Result
<Vec
<String
>, Error
> {
247 let mut list
= Vec
::new();
250 let (token
, text
) = self.next()?
;
252 Token
::Newline
=> break,
258 _
=> bail
!("unable to parse interface list - unexpected token '{:?}'", token
),
265 fn parse_iface_attributes(
267 interface
: &mut Interface
,
268 address_family_v4
: bool
,
269 address_family_v6
: bool
,
270 ) -> Result
<(), Error
> {
272 let mut netmask
= None
;
273 let mut address_list
= Vec
::new();
277 Token
::Attribute
=> { self.eat(Token::Attribute)?; }
,
279 let comment
= self.eat(Token
::Comment
)?
;
280 if !address_family_v4
&& address_family_v6
{
281 let mut comments
= interface
.comments6
.take().unwrap_or_default();
282 if !comments
.is_empty() { comments.push('\n'); }
283 comments
.push_str(&comment
);
284 interface
.comments6
= Some(comments
);
286 let mut comments
= interface
.comments
.take().unwrap_or_default();
287 if !comments
.is_empty() { comments.push('\n'); }
288 comments
.push_str(&comment
);
289 interface
.comments
= Some(comments
);
291 self.eat(Token
::Newline
)?
;
299 let (cidr
, mask
, is_v6
) = self.parse_iface_address()?
;
300 address_list
.push((cidr
, mask
, is_v6
));
302 Token
::Gateway
=> self.parse_iface_gateway(interface
)?
,
304 //Note: netmask is deprecated, but we try to do our best
305 netmask
= Some(self.parse_netmask()?
);
308 let mtu
= self.parse_iface_mtu()?
;
309 interface
.mtu
= Some(mtu
);
311 Token
::BridgeVlanAware
=> {
312 self.eat(Token
::BridgeVlanAware
)?
;
313 let bridge_vlan_aware
= self.parse_yes_no()?
;
314 interface
.bridge_vlan_aware
= Some(bridge_vlan_aware
);
316 Token
::BridgePorts
=> {
317 self.eat(Token
::BridgePorts
)?
;
318 let ports
= self.parse_iface_list()?
;
319 interface
.bridge_ports
= Some(ports
);
320 set_interface_type(interface
, NetworkInterfaceType
::Bridge
)?
;
322 Token
::BondSlaves
=> {
323 self.eat(Token
::BondSlaves
)?
;
324 let slaves
= self.parse_iface_list()?
;
325 interface
.slaves
= Some(slaves
);
326 set_interface_type(interface
, NetworkInterfaceType
::Bond
)?
;
329 self.eat(Token
::BondMode
)?
;
330 let mode
= self.next_text()?
;
331 interface
.bond_mode
= Some(bond_mode_from_str(&mode
)?
);
332 self.eat(Token
::Newline
)?
;
334 Token
::BondPrimary
=> {
335 self.eat(Token
::BondPrimary
)?
;
336 let primary
= self.next_text()?
;
337 interface
.bond_primary
= Some(primary
);
338 self.eat(Token
::Newline
)?
;
340 Token
::BondXmitHashPolicy
=> {
341 self.eat(Token
::BondXmitHashPolicy
)?
;
342 let policy
= bond_xmit_hash_policy_from_str(&self.next_text()?
)?
;
343 interface
.bond_xmit_hash_policy
= Some(policy
);
344 self.eat(Token
::Newline
)?
;
346 _
=> { // parse addon attributes
347 let option
= self.parse_to_eol()?
;
348 if !option
.is_empty() {
349 if !address_family_v4
&& address_family_v6
{
350 interface
.options6
.push(option
);
352 interface
.options
.push(option
);
359 #[allow(clippy::comparison_chain)]
360 if let Some(netmask
) = netmask
{
361 if address_list
.len() > 1 {
362 bail
!("unable to apply netmask to multiple addresses (please use cidr notation)");
363 } else if address_list
.len() == 1 {
364 let (mut cidr
, mask
, is_v6
) = address_list
.pop().unwrap();
366 // address already has a mask - ignore netmask
368 check_netmask(netmask
, is_v6
)?
;
369 cidr
.push_str(&format
!("/{}", netmask
));
372 set_cidr_v6(interface
, cidr
)?
;
374 set_cidr_v4(interface
, cidr
)?
;
377 // no address - simply ignore useless netmask
380 for (cidr
, mask
, is_v6
) in address_list
{
382 bail
!("missing netmask in '{}'", cidr
);
385 set_cidr_v6(interface
, cidr
)?
;
387 set_cidr_v4(interface
, cidr
)?
;
395 fn parse_iface(&mut self, config
: &mut NetworkConfig
) -> Result
<(), Error
> {
396 self.eat(Token
::Iface
)?
;
397 let iface
= self.next_text()?
;
399 let mut address_family_v4
= false;
400 let mut address_family_v6
= false;
401 let mut config_method
= None
;
404 let (token
, text
) = self.next()?
;
406 Token
::Newline
=> break,
407 Token
::Inet
=> address_family_v4
= true,
408 Token
::Inet6
=> address_family_v6
= true,
409 Token
::Loopback
=> config_method
= Some(NetworkConfigMethod
::Loopback
),
410 Token
::Static
=> config_method
= Some(NetworkConfigMethod
::Static
),
411 Token
::Manual
=> config_method
= Some(NetworkConfigMethod
::Manual
),
412 Token
::DHCP
=> config_method
= Some(NetworkConfigMethod
::DHCP
),
413 _
=> bail
!("unknown iface option {}", text
),
417 let config_method
= config_method
.unwrap_or(NetworkConfigMethod
::Static
);
419 if !(address_family_v4
|| address_family_v6
) {
420 address_family_v4
= true;
421 address_family_v6
= true;
424 if let Some(mut interface
) = config
.interfaces
.get_mut(&iface
) {
425 if address_family_v4
{
426 set_method_v4(interface
, config_method
)?
;
428 if address_family_v6
{
429 set_method_v6(interface
, config_method
)?
;
432 self.parse_iface_attributes(&mut interface
, address_family_v4
, address_family_v6
)?
;
434 let mut interface
= Interface
::new(iface
.clone());
435 if address_family_v4
{
436 set_method_v4(&mut interface
, config_method
)?
;
438 if address_family_v6
{
439 set_method_v6(&mut interface
, config_method
)?
;
442 self.parse_iface_attributes(&mut interface
, address_family_v4
, address_family_v6
)?
;
444 config
.interfaces
.insert(interface
.name
.clone(), interface
);
446 config
.order
.push(NetworkOrderEntry
::Iface(iface
));
452 pub fn parse_interfaces(&mut self, existing_interfaces
: Option
<&HashMap
<String
, bool
>>) -> Result
<NetworkConfig
, Error
> {
453 self._parse_interfaces(existing_interfaces
)
454 .map_err(|err
| format_err
!("line {}: {}", self.line_nr
, err
))
457 pub fn _parse_interfaces(&mut self, existing_interfaces
: Option
<&HashMap
<String
, bool
>>) -> Result
<NetworkConfig
, Error
> {
458 let mut config
= NetworkConfig
::new();
460 let mut auto_flag
: HashSet
<String
> = HashSet
::new();
469 self.eat(Token
::Newline
)?
;
472 let (_
, text
) = self.next()?
;
473 config
.order
.push(NetworkOrderEntry
::Comment(text
));
474 self.eat(Token
::Newline
)?
;
477 self.parse_auto(&mut auto_flag
)?
;
480 self.parse_iface(&mut config
)?
;
483 let option
= self.parse_to_eol()?
;
484 if !option
.is_empty() {
485 config
.order
.push(NetworkOrderEntry
::Option(option
));
491 for iface
in auto_flag
.iter() {
492 if let Some(interface
) = config
.interfaces
.get_mut(iface
) {
493 interface
.autostart
= true;
498 static ref INTERFACE_ALIAS_REGEX
: Regex
= Regex
::new(r
"^\S+:\d+$").unwrap();
499 static ref VLAN_INTERFACE_REGEX
: Regex
= Regex
::new(r
"^\S+\.\d+$").unwrap();
502 if let Some(existing_interfaces
) = existing_interfaces
{
503 for (iface
, active
) in existing_interfaces
.iter() {
504 if let Some(interface
) = config
.interfaces
.get_mut(iface
) {
505 interface
.active
= *active
;
506 if interface
.interface_type
== NetworkInterfaceType
::Unknown
&& super::is_physical_nic(iface
) {
507 interface
.interface_type
= NetworkInterfaceType
::Eth
;
509 } else if super::is_physical_nic(iface
) { // also add all physical NICs
510 let mut interface
= Interface
::new(iface
.clone());
511 set_method_v4(&mut interface
, NetworkConfigMethod
::Manual
)?
;
512 interface
.interface_type
= NetworkInterfaceType
::Eth
;
513 interface
.active
= *active
;
514 config
.interfaces
.insert(interface
.name
.clone(), interface
);
515 config
.order
.push(NetworkOrderEntry
::Iface(iface
.to_string()));
520 for (name
, interface
) in config
.interfaces
.iter_mut() {
521 if interface
.interface_type
!= NetworkInterfaceType
::Unknown { continue; }
523 interface
.interface_type
= NetworkInterfaceType
::Loopback
;
526 if INTERFACE_ALIAS_REGEX
.is_match(name
) {
527 interface
.interface_type
= NetworkInterfaceType
::Alias
;
530 if VLAN_INTERFACE_REGEX
.is_match(name
) {
531 interface
.interface_type
= NetworkInterfaceType
::Vlan
;
534 if super::is_physical_nic(name
) {
535 interface
.interface_type
= NetworkInterfaceType
::Eth
;
540 if config
.interfaces
.get("lo").is_none() {
541 let mut interface
= Interface
::new(String
::from("lo"));
542 set_method_v4(&mut interface
, NetworkConfigMethod
::Loopback
)?
;
543 interface
.interface_type
= NetworkInterfaceType
::Loopback
;
544 interface
.autostart
= true;
545 config
.interfaces
.insert(interface
.name
.clone(), interface
);
547 // Note: insert 'lo' as first interface after initial comments
548 let mut new_order
= Vec
::new();
549 let mut added_lo
= false;
550 for entry
in config
.order
{
551 if added_lo { new_order.push(entry); continue; }
// copy the rest
553 NetworkOrderEntry
::Comment(_
) => {
554 new_order
.push(entry
);
557 new_order
.push(NetworkOrderEntry
::Iface(String
::from("lo")));
559 new_order
.push(entry
);
563 config
.order
= new_order
;