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}
;
14 pub struct NetworkParser
<R
: BufRead
> {
15 input
: Peekable
<Lexer
<R
>>,
19 impl <R
: BufRead
> NetworkParser
<R
> {
21 pub fn new(reader
: R
) -> Self {
22 let input
= Lexer
::new(reader
).peekable();
23 Self { input, line_nr: 1 }
26 fn peek(&mut self) -> Result
<Token
, Error
> {
27 match self.input
.peek() {
29 bail
!("input error - {}", err
);
31 Some(Ok((token
, _
))) => {
35 bail
!("got unexpected end of stream (inside peek)");
40 fn next(&mut self) -> Result
<(Token
, String
), Error
> {
41 match self.input
.next() {
43 bail
!("input error - {}", err
);
45 Some(Ok((token
, text
))) => {
46 if token
== Token
::Newline { self.line_nr += 1; }
47 return Ok((token
, text
));
50 bail
!("got unexpected end of stream (inside peek)");
55 fn next_text(&mut self) -> Result
<String
, Error
> {
57 (Token
::Text
, text
) => Ok(text
),
58 (unexpected
, _
) => bail
!("got unexpected token {:?} (expecting Text)", unexpected
),
62 fn eat(&mut self, expected
: Token
) -> Result
<String
, Error
> {
63 let (next
, text
) = self.next()?
;
65 bail
!("expected {:?}, got {:?}", expected
, next
);
70 fn parse_auto(&mut self, auto_flag
: &mut HashSet
<String
>) -> Result
<(), Error
> {
71 self.eat(Token
::Auto
)?
;
75 (Token
::Text
, iface
) => {
76 auto_flag
.insert(iface
.to_string());
78 (Token
::Newline
, _
) => break,
80 bail
!("expected {:?}, got {:?}", Token
::Text
, unexpected
);
89 fn parse_iface_address(&mut self, interface
: &mut Interface
) -> Result
<(), Error
> {
90 self.eat(Token
::Address
)?
;
91 let cidr
= self.next_text()?
;
93 let (_address
, _mask
, ipv6
) = parse_cidr(&cidr
)?
;
95 interface
.set_cidr_v6(cidr
)?
;
97 interface
.set_cidr_v4(cidr
)?
;
100 self.eat(Token
::Newline
)?
;
105 fn parse_iface_gateway(&mut self, interface
: &mut Interface
) -> Result
<(), Error
> {
106 self.eat(Token
::Gateway
)?
;
107 let gateway
= self.next_text()?
;
109 if proxmox
::tools
::common_regex
::IP_REGEX
.is_match(&gateway
) {
110 if gateway
.contains('
:'
) {
111 interface
.set_gateway_v6(gateway
)?
;
113 interface
.set_gateway_v4(gateway
)?
;
116 bail
!("unable to parse gateway address");
119 self.eat(Token
::Newline
)?
;
124 fn parse_iface_mtu(&mut self) -> Result
<u64, Error
> {
125 self.eat(Token
::MTU
)?
;
127 let mtu
= self.next_text()?
;
128 let mtu
= match u64::from_str_radix(&mtu
, 10) {
131 bail
!("unable to parse mtu value '{}' - {}", mtu
, err
);
135 self.eat(Token
::Newline
)?
;
140 fn parse_yes_no(&mut self) -> Result
<bool
, Error
> {
141 let text
= self.next_text()?
;
142 let value
= match text
.to_lowercase().as_str() {
146 bail
!("unable to bool value '{}' - (expected yes/no)", text
);
150 self.eat(Token
::Newline
)?
;
155 fn parse_to_eol(&mut self) -> Result
<String
, Error
> {
156 let mut line
= String
::new();
159 (Token
::Newline
, _
) => return Ok(line
),
161 if !line
.is_empty() { line.push(' '); }
162 line
.push_str(&text
);
168 fn parse_iface_list(&mut self) -> Result
<Vec
<String
>, Error
> {
169 let mut list
= Vec
::new();
172 let (token
, text
) = self.next()?
;
174 Token
::Newline
=> break,
180 _
=> bail
!("unable to parse interface list - unexpected token '{:?}'", token
),
187 fn parse_iface_attributes(
189 interface
: &mut Interface
,
190 address_family_v4
: bool
,
191 address_family_v6
: bool
,
192 ) -> Result
<(), Error
> {
196 Token
::Attribute
=> { self.eat(Token::Attribute)?; }
,
198 let comment
= self.eat(Token
::Comment
)?
;
199 if !address_family_v4
&& address_family_v6
{
200 let mut comments
= interface
.comments6
.take().unwrap_or(String
::new());
201 if !comments
.is_empty() { comments.push('\n'); }
202 comments
.push_str(&comment
);
203 interface
.comments6
= Some(comments
);
205 let mut comments
= interface
.comments
.take().unwrap_or(String
::new());
206 if !comments
.is_empty() { comments.push('\n'); }
207 comments
.push_str(&comment
);
208 interface
.comments
= Some(comments
);
210 self.eat(Token
::Newline
)?
;
213 Token
::Newline
=> break,
215 unexpected
=> bail
!("unexpected token {:?} (expected iface attribute)", unexpected
),
219 Token
::Address
=> self.parse_iface_address(interface
)?
,
220 Token
::Gateway
=> self.parse_iface_gateway(interface
)?
,
222 let mtu
= self.parse_iface_mtu()?
;
223 interface
.mtu
= Some(mtu
);
225 Token
::BridgeVlanAware
=> {
226 self.eat(Token
::BridgeVlanAware
)?
;
227 let bridge_vlan_aware
= self.parse_yes_no()?
;
228 interface
.bridge_vlan_aware
= Some(bridge_vlan_aware
);
230 Token
::BridgePorts
=> {
231 self.eat(Token
::BridgePorts
)?
;
232 let ports
= self.parse_iface_list()?
;
233 interface
.bridge_ports
= Some(ports
);
234 interface
.set_interface_type(NetworkInterfaceType
::Bridge
)?
;
236 Token
::BondSlaves
=> {
237 self.eat(Token
::BondSlaves
)?
;
238 let slaves
= self.parse_iface_list()?
;
239 interface
.bond_slaves
= Some(slaves
);
240 interface
.set_interface_type(NetworkInterfaceType
::Bond
)?
;
242 Token
::Netmask
=> bail
!("netmask is deprecated and no longer supported"),
244 _
=> { // parse addon attributes
245 let option
= self.parse_to_eol()?
;
246 if !option
.is_empty() {
247 if !address_family_v4
&& address_family_v6
{
248 interface
.options6
.push(option
);
250 interface
.options
.push(option
);
260 fn parse_iface(&mut self, config
: &mut NetworkConfig
) -> Result
<(), Error
> {
261 self.eat(Token
::Iface
)?
;
262 let iface
= self.next_text()?
;
264 let mut address_family_v4
= false;
265 let mut address_family_v6
= false;
266 let mut config_method
= None
;
269 let (token
, text
) = self.next()?
;
271 Token
::Newline
=> break,
272 Token
::Inet
=> address_family_v4
= true,
273 Token
::Inet6
=> address_family_v6
= true,
274 Token
::Loopback
=> config_method
= Some(NetworkConfigMethod
::Loopback
),
275 Token
::Static
=> config_method
= Some(NetworkConfigMethod
::Static
),
276 Token
::Manual
=> config_method
= Some(NetworkConfigMethod
::Manual
),
277 Token
::DHCP
=> config_method
= Some(NetworkConfigMethod
::DHCP
),
278 _
=> bail
!("unknown iface option {}", text
),
282 let config_method
= config_method
.unwrap_or(NetworkConfigMethod
::Static
);
284 if !(address_family_v4
|| address_family_v6
) {
285 address_family_v4
= true;
286 address_family_v6
= true;
289 if let Some(mut interface
) = config
.interfaces
.get_mut(&iface
) {
290 if address_family_v4
{
291 interface
.set_method_v4(config_method
)?
;
293 if address_family_v6
{
294 interface
.set_method_v6(config_method
)?
;
297 self.parse_iface_attributes(&mut interface
, address_family_v4
, address_family_v6
)?
;
299 let mut interface
= Interface
::new(iface
.clone());
300 if address_family_v4
{
301 interface
.set_method_v4(config_method
)?
;
303 if address_family_v6
{
304 interface
.set_method_v6(config_method
)?
;
307 self.parse_iface_attributes(&mut interface
, address_family_v4
, address_family_v6
)?
;
309 config
.interfaces
.insert(interface
.name
.clone(), interface
);
311 config
.order
.push(NetworkOrderEntry
::Iface(iface
));
317 pub fn parse_interfaces(&mut self, existing_interfaces
: Option
<&HashMap
<String
, bool
>>) -> Result
<NetworkConfig
, Error
> {
318 self._parse_interfaces(existing_interfaces
)
319 .map_err(|err
| format_err
!("line {}: {}", self.line_nr
, err
))
322 pub fn _parse_interfaces(&mut self, existing_interfaces
: Option
<&HashMap
<String
, bool
>>) -> Result
<NetworkConfig
, Error
> {
323 let mut config
= NetworkConfig
::new();
325 let mut auto_flag
: HashSet
<String
> = HashSet
::new();
334 self.eat(Token
::Newline
)?
;
337 let (_
, text
) = self.next()?
;
338 config
.order
.push(NetworkOrderEntry
::Comment(text
));
339 self.eat(Token
::Newline
)?
;
342 self.parse_auto(&mut auto_flag
)?
;
345 self.parse_iface(&mut config
)?
;
348 let option
= self.parse_to_eol()?
;
349 if !option
.is_empty() {
350 config
.order
.push(NetworkOrderEntry
::Option(option
));
356 for iface
in auto_flag
.iter() {
357 if let Some(interface
) = config
.interfaces
.get_mut(iface
) {
358 interface
.autostart
= true;
363 static ref PHYSICAL_NIC_REGEX
: Regex
= Regex
::new(r
"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
364 static ref INTERFACE_ALIAS_REGEX
: Regex
= Regex
::new(r
"^\S+:\d+$").unwrap();
365 static ref VLAN_INTERFACE_REGEX
: Regex
= Regex
::new(r
"^\S+\.\d+$").unwrap();
368 if let Some(existing_interfaces
) = existing_interfaces
{
369 for (iface
, active
) in existing_interfaces
.iter() {
370 if let Some(interface
) = config
.interfaces
.get_mut(iface
) {
371 interface
.active
= *active
;
372 if interface
.interface_type
== NetworkInterfaceType
::Unknown
&& PHYSICAL_NIC_REGEX
.is_match(iface
) {
373 interface
.interface_type
= NetworkInterfaceType
::Eth
;
375 } else if PHYSICAL_NIC_REGEX
.is_match(iface
) { // also add all physical NICs
376 let mut interface
= Interface
::new(iface
.clone());
377 interface
.set_method_v4(NetworkConfigMethod
::Manual
)?
;
378 interface
.interface_type
= NetworkInterfaceType
::Eth
;
379 interface
.active
= *active
;
380 config
.interfaces
.insert(interface
.name
.clone(), interface
);
381 config
.order
.push(NetworkOrderEntry
::Iface(iface
.to_string()));
386 for (name
, interface
) in config
.interfaces
.iter_mut() {
387 if interface
.interface_type
!= NetworkInterfaceType
::Unknown { continue; }
389 interface
.interface_type
= NetworkInterfaceType
::Loopback
;
392 if INTERFACE_ALIAS_REGEX
.is_match(name
) {
393 interface
.interface_type
= NetworkInterfaceType
::Alias
;
396 if VLAN_INTERFACE_REGEX
.is_match(name
) {
397 interface
.interface_type
= NetworkInterfaceType
::Vlan
;
400 if PHYSICAL_NIC_REGEX
.is_match(name
) {
401 interface
.interface_type
= NetworkInterfaceType
::Eth
;
406 if config
.interfaces
.get("lo").is_none() {
407 let mut interface
= Interface
::new(String
::from("lo"));
408 interface
.set_method_v4(NetworkConfigMethod
::Loopback
)?
;
409 interface
.interface_type
= NetworkInterfaceType
::Loopback
;
410 interface
.autostart
= true;
411 config
.interfaces
.insert(interface
.name
.clone(), interface
);
413 // Note: insert 'lo' as first interface after initial comments
414 let mut new_order
= Vec
::new();
415 let mut added_lo
= false;
416 for entry
in config
.order
{
417 if added_lo { new_order.push(entry); continue; }
// copy the rest
419 NetworkOrderEntry
::Comment(_
) => {
420 new_order
.push(entry
);
423 new_order
.push(NetworkOrderEntry
::Iface(String
::from("lo")));
425 new_order
.push(entry
);
429 config
.order
= new_order
;