]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-config/src/network/parser.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / pbs-config / src / network / parser.rs
CommitLineData
904e9886 1use std::io::{BufRead};
f34d4401 2use std::iter::{Peekable, Iterator};
1ca540a6 3use std::collections::{HashMap, HashSet};
f34d4401
DM
4
5use anyhow::{Error, bail, format_err};
6use lazy_static::lazy_static;
7use regex::Regex;
8
f34d4401
DM
9use super::helper::*;
10use super::lexer::*;
11
8f2f3dd7 12use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType, bond_mode_from_str, bond_xmit_hash_policy_from_str};
f34d4401 13
6f422880
DM
14fn set_method_v4(iface: &mut Interface, method: NetworkConfigMethod) -> Result<(), Error> {
15 if iface.method.is_none() {
16 iface.method = Some(method);
17 } else {
18 bail!("inet configuration method already set.");
19 }
20 Ok(())
21}
22
23fn set_method_v6(iface: &mut Interface, method: NetworkConfigMethod) -> Result<(), Error> {
24 if iface.method6.is_none() {
25 iface.method6 = Some(method);
26 } else {
27 bail!("inet6 configuration method already set.");
28 }
29 Ok(())
30}
31
32fn set_cidr_v4(iface: &mut Interface, address: String) -> Result<(), Error> {
33 if iface.cidr.is_none() {
34 iface.cidr = Some(address);
35 } else {
36 bail!("duplicate IPv4 address.");
37 }
38 Ok(())
39}
40
41fn set_gateway_v4(iface: &mut Interface, gateway: String) -> Result<(), Error> {
42 if iface.gateway.is_none() {
43 iface.gateway = Some(gateway);
44 } else {
45 bail!("duplicate IPv4 gateway.");
46 }
47 Ok(())
48}
49
50fn set_cidr_v6(iface: &mut Interface, address: String) -> Result<(), Error> {
51 if iface.cidr6.is_none() {
52 iface.cidr6 = Some(address);
53 } else {
54 bail!("duplicate IPv6 address.");
55 }
56 Ok(())
57}
58
59fn set_gateway_v6(iface: &mut Interface, gateway: String) -> Result<(), Error> {
60 if iface.gateway6.is_none() {
61 iface.gateway6 = Some(gateway);
62 } else {
63 bail!("duplicate IPv4 gateway.");
64 }
65 Ok(())
66}
67
68fn 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);
73 }
74 Ok(())
75}
76
904e9886
DM
77pub struct NetworkParser<R: BufRead> {
78 input: Peekable<Lexer<R>>,
f34d4401
DM
79 line_nr: usize,
80}
81
904e9886 82impl <R: BufRead> NetworkParser<R> {
f34d4401 83
904e9886 84 pub fn new(reader: R) -> Self {
f34d4401
DM
85 let input = Lexer::new(reader).peekable();
86 Self { input, line_nr: 1 }
87 }
88
89 fn peek(&mut self) -> Result<Token, Error> {
90 match self.input.peek() {
91 Some(Err(err)) => {
92 bail!("input error - {}", err);
93 }
94 Some(Ok((token, _))) => {
38556bf6 95 Ok(*token)
f34d4401
DM
96 }
97 None => {
98 bail!("got unexpected end of stream (inside peek)");
99 }
100 }
101 }
102
103 fn next(&mut self) -> Result<(Token, String), Error> {
104 match self.input.next() {
105 Some(Err(err)) => {
106 bail!("input error - {}", err);
107 }
108 Some(Ok((token, text))) => {
109 if token == Token::Newline { self.line_nr += 1; }
38556bf6 110 Ok((token, text))
f34d4401
DM
111 }
112 None => {
113 bail!("got unexpected end of stream (inside peek)");
114 }
115 }
116 }
117
118 fn next_text(&mut self) -> Result<String, Error> {
119 match self.next()? {
120 (Token::Text, text) => Ok(text),
121 (unexpected, _) => bail!("got unexpected token {:?} (expecting Text)", unexpected),
122 }
123 }
124
5f60a58f
DM
125 fn eat(&mut self, expected: Token) -> Result<String, Error> {
126 let (next, text) = self.next()?;
f34d4401
DM
127 if next != expected {
128 bail!("expected {:?}, got {:?}", expected, next);
129 }
5f60a58f 130 Ok(text)
f34d4401
DM
131 }
132
a9bb491e 133 fn parse_auto(&mut self, auto_flag: &mut HashSet<String>) -> Result<(), Error> {
f34d4401
DM
134 self.eat(Token::Auto)?;
135
a9bb491e
DM
136 loop {
137 match self.next()? {
138 (Token::Text, iface) => {
e2d940b9 139 auto_flag.insert(iface.to_string());
a9bb491e
DM
140 }
141 (Token::Newline, _) => break,
142 unexpected => {
143 bail!("expected {:?}, got {:?}", Token::Text, unexpected);
144 }
145 }
f34d4401 146
a9bb491e 147 }
f34d4401
DM
148
149 Ok(())
150 }
151
645a47ff
DM
152 fn parse_netmask(&mut self) -> Result<u8, Error> {
153 self.eat(Token::Netmask)?;
154 let netmask = self.next_text()?;
155
156 let mask = if let Some(mask) = IPV4_MASK_HASH_LOCALNET.get(netmask.as_str()) {
157 *mask
158 } else {
159 match u8::from_str_radix(netmask.as_str(), 10) {
160 Ok(mask) => mask,
161 Err(err) => {
056ee785 162 bail!("unable to parse netmask '{}' - {}", netmask, err);
645a47ff
DM
163 }
164 }
165 };
166
167 self.eat(Token::Newline)?;
168
169 Ok(mask)
170 }
171
172 fn parse_iface_address(&mut self) -> Result<(String, Option<u8>, bool), Error> {
f34d4401 173 self.eat(Token::Address)?;
8b57cd44 174 let cidr = self.next_text()?;
f34d4401 175
645a47ff 176 let (_address, mask, ipv6) = parse_address_or_cidr(&cidr)?;
f34d4401
DM
177
178 self.eat(Token::Newline)?;
179
645a47ff 180 Ok((cidr, mask, ipv6))
f34d4401
DM
181 }
182
183 fn parse_iface_gateway(&mut self, interface: &mut Interface) -> Result<(), Error> {
184 self.eat(Token::Gateway)?;
185 let gateway = self.next_text()?;
186
25877d05 187 if pbs_api_types::common_regex::IP_REGEX.is_match(&gateway) {
f34d4401 188 if gateway.contains(':') {
6f422880 189 set_gateway_v6(interface, gateway)?;
f34d4401 190 } else {
6f422880 191 set_gateway_v4(interface, gateway)?;
f34d4401
DM
192 }
193 } else {
194 bail!("unable to parse gateway address");
195 }
196
197 self.eat(Token::Newline)?;
198
199 Ok(())
200 }
201
3fce3bc3
DM
202 fn parse_iface_mtu(&mut self) -> Result<u64, Error> {
203 self.eat(Token::MTU)?;
204
205 let mtu = self.next_text()?;
206 let mtu = match u64::from_str_radix(&mtu, 10) {
207 Ok(mtu) => mtu,
208 Err(err) => {
209 bail!("unable to parse mtu value '{}' - {}", mtu, err);
210 }
211 };
212
213 self.eat(Token::Newline)?;
214
215 Ok(mtu)
216 }
217
7b22acd0
DM
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() {
221 "yes" => true,
222 "no" => false,
223 _ => {
224 bail!("unable to bool value '{}' - (expected yes/no)", text);
225 }
226 };
227
228 self.eat(Token::Newline)?;
229
230 Ok(value)
231 }
232
f34d4401
DM
233 fn parse_to_eol(&mut self) -> Result<String, Error> {
234 let mut line = String::new();
235 loop {
236 match self.next()? {
237 (Token::Newline, _) => return Ok(line),
238 (_, text) => {
239 if !line.is_empty() { line.push(' '); }
240 line.push_str(&text);
241 }
242 }
243 }
244 }
245
1d9a68c2
DM
246 fn parse_iface_list(&mut self) -> Result<Vec<String>, Error> {
247 let mut list = Vec::new();
248
249 loop {
250 let (token, text) = self.next()?;
251 match token {
252 Token::Newline => break,
253 Token::Text => {
254 if &text != "none" {
255 list.push(text);
256 }
257 }
258 _ => bail!("unable to parse interface list - unexpected token '{:?}'", token),
259 }
260 }
261
262 Ok(list)
263 }
c38b4bb8 264
5f60a58f
DM
265 fn parse_iface_attributes(
266 &mut self,
267 interface: &mut Interface,
268 address_family_v4: bool,
269 address_family_v6: bool,
270 ) -> Result<(), Error> {
f34d4401 271
645a47ff
DM
272 let mut netmask = None;
273 let mut address_list = Vec::new();
274
f34d4401
DM
275 loop {
276 match self.peek()? {
5f60a58f
DM
277 Token::Attribute => { self.eat(Token::Attribute)?; },
278 Token::Comment => {
279 let comment = self.eat(Token::Comment)?;
280 if !address_family_v4 && address_family_v6 {
17c7b46a 281 let mut comments = interface.comments6.take().unwrap_or_default();
8a6b86b8
DM
282 if !comments.is_empty() { comments.push('\n'); }
283 comments.push_str(&comment);
7b22acd0 284 interface.comments6 = Some(comments);
5f60a58f 285 } else {
17c7b46a 286 let mut comments = interface.comments.take().unwrap_or_default();
8a6b86b8
DM
287 if !comments.is_empty() { comments.push('\n'); }
288 comments.push_str(&comment);
7b22acd0 289 interface.comments = Some(comments);
5f60a58f
DM
290 }
291 self.eat(Token::Newline)?;
292 continue;
293 }
87c4cb74 294 _ => break,
f34d4401
DM
295 }
296
297 match self.peek()? {
645a47ff
DM
298 Token::Address => {
299 let (cidr, mask, is_v6) = self.parse_iface_address()?;
300 address_list.push((cidr, mask, is_v6));
301 }
f34d4401 302 Token::Gateway => self.parse_iface_gateway(interface)?,
645a47ff
DM
303 Token::Netmask => {
304 //Note: netmask is deprecated, but we try to do our best
305 netmask = Some(self.parse_netmask()?);
306 }
3fce3bc3
DM
307 Token::MTU => {
308 let mtu = self.parse_iface_mtu()?;
2c18efd9 309 interface.mtu = Some(mtu);
3fce3bc3 310 }
7b22acd0
DM
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);
315 }
1d9a68c2
DM
316 Token::BridgePorts => {
317 self.eat(Token::BridgePorts)?;
318 let ports = self.parse_iface_list()?;
319 interface.bridge_ports = Some(ports);
6f422880 320 set_interface_type(interface, NetworkInterfaceType::Bridge)?;
1d9a68c2 321 }
42fbe91a
DM
322 Token::BondSlaves => {
323 self.eat(Token::BondSlaves)?;
324 let slaves = self.parse_iface_list()?;
bab5d18c 325 interface.slaves = Some(slaves);
6f422880 326 set_interface_type(interface, NetworkInterfaceType::Bond)?;
42fbe91a 327 }
bab5d18c
DM
328 Token::BondMode => {
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)?;
333 }
85959a99
DC
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)?;
339 }
8f2f3dd7
DC
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)?;
345 }
5f60a58f
DM
346 _ => { // parse addon attributes
347 let option = self.parse_to_eol()?;
348 if !option.is_empty() {
349 if !address_family_v4 && address_family_v6 {
7b22acd0 350 interface.options6.push(option);
5f60a58f 351 } else {
7b22acd0 352 interface.options.push(option);
5f60a58f
DM
353 }
354 };
355 },
f34d4401
DM
356 }
357 }
358
43313c2e 359 #[allow(clippy::comparison_chain)]
645a47ff
DM
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();
365 if mask.is_some() {
366 // address already has a mask - ignore netmask
367 } else {
368 check_netmask(netmask, is_v6)?;
369 cidr.push_str(&format!("/{}", netmask));
370 }
371 if is_v6 {
6f422880 372 set_cidr_v6(interface, cidr)?;
645a47ff 373 } else {
6f422880 374 set_cidr_v4(interface, cidr)?;
645a47ff
DM
375 }
376 } else {
377 // no address - simply ignore useless netmask
378 }
379 } else {
380 for (cidr, mask, is_v6) in address_list {
381 if mask.is_none() {
382 bail!("missing netmask in '{}'", cidr);
383 }
384 if is_v6 {
6f422880 385 set_cidr_v6(interface, cidr)?;
645a47ff 386 } else {
6f422880 387 set_cidr_v4(interface, cidr)?;
645a47ff
DM
388 }
389 }
390 }
391
f34d4401
DM
392 Ok(())
393 }
394
395 fn parse_iface(&mut self, config: &mut NetworkConfig) -> Result<(), Error> {
396 self.eat(Token::Iface)?;
397 let iface = self.next_text()?;
398
92310d58
DM
399 let mut address_family_v4 = false;
400 let mut address_family_v6 = false;
f34d4401
DM
401 let mut config_method = None;
402
403 loop {
404 let (token, text) = self.next()?;
405 match token {
406 Token::Newline => break,
92310d58
DM
407 Token::Inet => address_family_v4 = true,
408 Token::Inet6 => address_family_v6 = true,
7e02d08c
DM
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),
f34d4401
DM
413 _ => bail!("unknown iface option {}", text),
414 }
415 }
416
7e02d08c 417 let config_method = config_method.unwrap_or(NetworkConfigMethod::Static);
92310d58
DM
418
419 if !(address_family_v4 || address_family_v6) {
420 address_family_v4 = true;
421 address_family_v6 = true;
422 }
f34d4401
DM
423
424 if let Some(mut interface) = config.interfaces.get_mut(&iface) {
92310d58 425 if address_family_v4 {
6f422880 426 set_method_v4(interface, config_method)?;
92310d58
DM
427 }
428 if address_family_v6 {
6f422880 429 set_method_v6(interface, config_method)?;
f34d4401 430 }
92310d58 431
5f60a58f 432 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401 433 } else {
92310d58
DM
434 let mut interface = Interface::new(iface.clone());
435 if address_family_v4 {
6f422880 436 set_method_v4(&mut interface, config_method)?;
92310d58
DM
437 }
438 if address_family_v6 {
6f422880 439 set_method_v6(&mut interface, config_method)?;
92310d58 440 }
f34d4401 441
5f60a58f 442 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401
DM
443
444 config.interfaces.insert(interface.name.clone(), interface);
92310d58 445
f34d4401
DM
446 config.order.push(NetworkOrderEntry::Iface(iface));
447 }
448
449 Ok(())
450 }
451
1ca540a6
DM
452 pub fn parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
453 self._parse_interfaces(existing_interfaces)
f34d4401
DM
454 .map_err(|err| format_err!("line {}: {}", self.line_nr, err))
455 }
456
1ca540a6 457 pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
f34d4401
DM
458 let mut config = NetworkConfig::new();
459
a9bb491e
DM
460 let mut auto_flag: HashSet<String> = HashSet::new();
461
f34d4401 462 loop {
e2d940b9 463 match self.peek()? {
f34d4401 464 Token::EOF => {
a9bb491e 465 break;
f34d4401
DM
466 }
467 Token::Newline => {
e2d940b9 468 // skip empty lines
f34d4401 469 self.eat(Token::Newline)?;
f34d4401
DM
470 }
471 Token::Comment => {
472 let (_, text) = self.next()?;
f34d4401
DM
473 config.order.push(NetworkOrderEntry::Comment(text));
474 self.eat(Token::Newline)?;
475 }
476 Token::Auto => {
a9bb491e 477 self.parse_auto(&mut auto_flag)?;
f34d4401
DM
478 }
479 Token::Iface => {
480 self.parse_iface(&mut config)?;
481 }
482 _ => {
483 let option = self.parse_to_eol()?;
484 if !option.is_empty() {
485 config.order.push(NetworkOrderEntry::Option(option));
486 }
487 }
488 }
489 }
a9bb491e
DM
490
491 for iface in auto_flag.iter() {
492 if let Some(interface) = config.interfaces.get_mut(iface) {
7b22acd0 493 interface.autostart = true;
a9bb491e
DM
494 }
495 }
496
3f129233 497 lazy_static!{
02269f3d
DM
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();
3f129233
DM
500 }
501
1ca540a6
DM
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;
0ed9a2b3 506 if interface.interface_type == NetworkInterfaceType::Unknown && super::is_physical_nic(iface) {
7b22acd0 507 interface.interface_type = NetworkInterfaceType::Eth;
1ca540a6 508 }
0ed9a2b3 509 } else if super::is_physical_nic(iface) { // also add all physical NICs
1ca540a6 510 let mut interface = Interface::new(iface.clone());
6f422880 511 set_method_v4(&mut interface, NetworkConfigMethod::Manual)?;
7b22acd0 512 interface.interface_type = NetworkInterfaceType::Eth;
1ca540a6
DM
513 interface.active = *active;
514 config.interfaces.insert(interface.name.clone(), interface);
515 config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
96d94786 516 }
3f129233
DM
517 }
518 }
519
02269f3d
DM
520 for (name, interface) in config.interfaces.iter_mut() {
521 if interface.interface_type != NetworkInterfaceType::Unknown { continue; }
522 if name == "lo" {
523 interface.interface_type = NetworkInterfaceType::Loopback;
524 continue;
525 }
526 if INTERFACE_ALIAS_REGEX.is_match(name) {
527 interface.interface_type = NetworkInterfaceType::Alias;
528 continue;
529 }
530 if VLAN_INTERFACE_REGEX.is_match(name) {
531 interface.interface_type = NetworkInterfaceType::Vlan;
532 continue;
533 }
0ed9a2b3 534 if super::is_physical_nic(name) {
7b22acd0 535 interface.interface_type = NetworkInterfaceType::Eth;
02269f3d
DM
536 continue;
537 }
538 }
539
1ca540a6
DM
540 if config.interfaces.get("lo").is_none() {
541 let mut interface = Interface::new(String::from("lo"));
6f422880 542 set_method_v4(&mut interface, NetworkConfigMethod::Loopback)?;
1ca540a6 543 interface.interface_type = NetworkInterfaceType::Loopback;
7b22acd0 544 interface.autostart = true;
1ca540a6
DM
545 config.interfaces.insert(interface.name.clone(), interface);
546
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
552 match entry {
553 NetworkOrderEntry::Comment(_) => {
554 new_order.push(entry);
555 }
556 _ => {
557 new_order.push(NetworkOrderEntry::Iface(String::from("lo")));
558 added_lo = true;
559 new_order.push(entry);
560 }
561 }
562 }
563 config.order = new_order;
564 }
565
a9bb491e 566 Ok(config)
f34d4401
DM
567 }
568}