]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-config/src/network/parser.rs
7cb081ae7569cf316fcd666a7c2644e3a678d9ab
[proxmox-backup.git] / pbs-config / src / network / parser.rs
1 use std::io::{BufRead};
2 use std::iter::{Peekable, Iterator};
3 use std::collections::{HashMap, HashSet};
4
5 use anyhow::{Error, bail, format_err};
6 use lazy_static::lazy_static;
7 use regex::Regex;
8
9 use super::helper::*;
10 use super::lexer::*;
11
12 use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType, bond_mode_from_str, bond_xmit_hash_policy_from_str};
13
14 fn 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
23 fn 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
32 fn 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
41 fn 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
50 fn 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
59 fn 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
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);
73 }
74 Ok(())
75 }
76
77 pub struct NetworkParser<R: BufRead> {
78 input: Peekable<Lexer<R>>,
79 line_nr: usize,
80 }
81
82 impl <R: BufRead> NetworkParser<R> {
83
84 pub fn new(reader: R) -> Self {
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, _))) => {
95 Ok(*token)
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; }
110 Ok((token, text))
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
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);
129 }
130 Ok(text)
131 }
132
133 fn parse_auto(&mut self, auto_flag: &mut HashSet<String>) -> Result<(), Error> {
134 self.eat(Token::Auto)?;
135
136 loop {
137 match self.next()? {
138 (Token::Text, iface) => {
139 auto_flag.insert(iface.to_string());
140 }
141 (Token::Newline, _) => break,
142 unexpected => {
143 bail!("expected {:?}, got {:?}", Token::Text, unexpected);
144 }
145 }
146
147 }
148
149 Ok(())
150 }
151
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) => {
162 bail!("unable to parse netmask '{}' - {}", netmask, err);
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> {
173 self.eat(Token::Address)?;
174 let cidr = self.next_text()?;
175
176 let (_address, mask, ipv6) = parse_address_or_cidr(&cidr)?;
177
178 self.eat(Token::Newline)?;
179
180 Ok((cidr, mask, ipv6))
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
187 if proxmox::tools::common_regex::IP_REGEX.is_match(&gateway) {
188 if gateway.contains(':') {
189 set_gateway_v6(interface, gateway)?;
190 } else {
191 set_gateway_v4(interface, gateway)?;
192 }
193 } else {
194 bail!("unable to parse gateway address");
195 }
196
197 self.eat(Token::Newline)?;
198
199 Ok(())
200 }
201
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
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
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
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 }
264
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> {
271
272 let mut netmask = None;
273 let mut address_list = Vec::new();
274
275 loop {
276 match self.peek()? {
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 {
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);
285 } else {
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);
290 }
291 self.eat(Token::Newline)?;
292 continue;
293 }
294 _ => break,
295 }
296
297 match self.peek()? {
298 Token::Address => {
299 let (cidr, mask, is_v6) = self.parse_iface_address()?;
300 address_list.push((cidr, mask, is_v6));
301 }
302 Token::Gateway => self.parse_iface_gateway(interface)?,
303 Token::Netmask => {
304 //Note: netmask is deprecated, but we try to do our best
305 netmask = Some(self.parse_netmask()?);
306 }
307 Token::MTU => {
308 let mtu = self.parse_iface_mtu()?;
309 interface.mtu = Some(mtu);
310 }
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 }
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)?;
321 }
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)?;
327 }
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 }
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 }
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 }
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);
351 } else {
352 interface.options.push(option);
353 }
354 };
355 },
356 }
357 }
358
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();
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 {
372 set_cidr_v6(interface, cidr)?;
373 } else {
374 set_cidr_v4(interface, cidr)?;
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 {
385 set_cidr_v6(interface, cidr)?;
386 } else {
387 set_cidr_v4(interface, cidr)?;
388 }
389 }
390 }
391
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
399 let mut address_family_v4 = false;
400 let mut address_family_v6 = false;
401 let mut config_method = None;
402
403 loop {
404 let (token, text) = self.next()?;
405 match token {
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),
414 }
415 }
416
417 let config_method = config_method.unwrap_or(NetworkConfigMethod::Static);
418
419 if !(address_family_v4 || address_family_v6) {
420 address_family_v4 = true;
421 address_family_v6 = true;
422 }
423
424 if let Some(mut interface) = config.interfaces.get_mut(&iface) {
425 if address_family_v4 {
426 set_method_v4(interface, config_method)?;
427 }
428 if address_family_v6 {
429 set_method_v6(interface, config_method)?;
430 }
431
432 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
433 } else {
434 let mut interface = Interface::new(iface.clone());
435 if address_family_v4 {
436 set_method_v4(&mut interface, config_method)?;
437 }
438 if address_family_v6 {
439 set_method_v6(&mut interface, config_method)?;
440 }
441
442 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
443
444 config.interfaces.insert(interface.name.clone(), interface);
445
446 config.order.push(NetworkOrderEntry::Iface(iface));
447 }
448
449 Ok(())
450 }
451
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))
455 }
456
457 pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
458 let mut config = NetworkConfig::new();
459
460 let mut auto_flag: HashSet<String> = HashSet::new();
461
462 loop {
463 match self.peek()? {
464 Token::EOF => {
465 break;
466 }
467 Token::Newline => {
468 // skip empty lines
469 self.eat(Token::Newline)?;
470 }
471 Token::Comment => {
472 let (_, text) = self.next()?;
473 config.order.push(NetworkOrderEntry::Comment(text));
474 self.eat(Token::Newline)?;
475 }
476 Token::Auto => {
477 self.parse_auto(&mut auto_flag)?;
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 }
490
491 for iface in auto_flag.iter() {
492 if let Some(interface) = config.interfaces.get_mut(iface) {
493 interface.autostart = true;
494 }
495 }
496
497 lazy_static!{
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();
500 }
501
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;
508 }
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()));
516 }
517 }
518 }
519
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 }
534 if super::is_physical_nic(name) {
535 interface.interface_type = NetworkInterfaceType::Eth;
536 continue;
537 }
538 }
539
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);
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
566 Ok(config)
567 }
568 }