]> git.proxmox.com Git - proxmox-backup.git/blob - src/config/network/parser.rs
f9f950b47bad3c5763bef62989506e465e10c8a7
[proxmox-backup.git] / src / config / 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};
13
14 pub struct NetworkParser<R: BufRead> {
15 input: Peekable<Lexer<R>>,
16 line_nr: usize,
17 }
18
19 impl <R: BufRead> NetworkParser<R> {
20
21 pub fn new(reader: R) -> Self {
22 let input = Lexer::new(reader).peekable();
23 Self { input, line_nr: 1 }
24 }
25
26 fn peek(&mut self) -> Result<Token, Error> {
27 match self.input.peek() {
28 Some(Err(err)) => {
29 bail!("input error - {}", err);
30 }
31 Some(Ok((token, _))) => {
32 return Ok(*token);
33 }
34 None => {
35 bail!("got unexpected end of stream (inside peek)");
36 }
37 }
38 }
39
40 fn next(&mut self) -> Result<(Token, String), Error> {
41 match self.input.next() {
42 Some(Err(err)) => {
43 bail!("input error - {}", err);
44 }
45 Some(Ok((token, text))) => {
46 if token == Token::Newline { self.line_nr += 1; }
47 return Ok((token, text));
48 }
49 None => {
50 bail!("got unexpected end of stream (inside peek)");
51 }
52 }
53 }
54
55 fn next_text(&mut self) -> Result<String, Error> {
56 match self.next()? {
57 (Token::Text, text) => Ok(text),
58 (unexpected, _) => bail!("got unexpected token {:?} (expecting Text)", unexpected),
59 }
60 }
61
62 fn eat(&mut self, expected: Token) -> Result<String, Error> {
63 let (next, text) = self.next()?;
64 if next != expected {
65 bail!("expected {:?}, got {:?}", expected, next);
66 }
67 Ok(text)
68 }
69
70 fn parse_auto(&mut self, auto_flag: &mut HashSet<String>) -> Result<(), Error> {
71 self.eat(Token::Auto)?;
72
73 loop {
74 match self.next()? {
75 (Token::Text, iface) => {
76 auto_flag.insert(iface.to_string());
77 }
78 (Token::Newline, _) => break,
79 unexpected => {
80 bail!("expected {:?}, got {:?}", Token::Text, unexpected);
81 }
82 }
83
84 }
85
86 Ok(())
87 }
88
89 fn parse_iface_address(&mut self, interface: &mut Interface) -> Result<(), Error> {
90 self.eat(Token::Address)?;
91 let cidr = self.next_text()?;
92
93 let (_address, _mask, ipv6) = parse_cidr(&cidr)?;
94 if ipv6 {
95 interface.set_cidr_v6(cidr)?;
96 } else {
97 interface.set_cidr_v4(cidr)?;
98 }
99
100 self.eat(Token::Newline)?;
101
102 Ok(())
103 }
104
105 fn parse_iface_gateway(&mut self, interface: &mut Interface) -> Result<(), Error> {
106 self.eat(Token::Gateway)?;
107 let gateway = self.next_text()?;
108
109 if proxmox::tools::common_regex::IP_REGEX.is_match(&gateway) {
110 if gateway.contains(':') {
111 interface.set_gateway_v6(gateway)?;
112 } else {
113 interface.set_gateway_v4(gateway)?;
114 }
115 } else {
116 bail!("unable to parse gateway address");
117 }
118
119 self.eat(Token::Newline)?;
120
121 Ok(())
122 }
123
124 fn parse_iface_mtu(&mut self) -> Result<u64, Error> {
125 self.eat(Token::MTU)?;
126
127 let mtu = self.next_text()?;
128 let mtu = match u64::from_str_radix(&mtu, 10) {
129 Ok(mtu) => mtu,
130 Err(err) => {
131 bail!("unable to parse mtu value '{}' - {}", mtu, err);
132 }
133 };
134
135 self.eat(Token::Newline)?;
136
137 Ok(mtu)
138 }
139
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() {
143 "yes" => true,
144 "no" => false,
145 _ => {
146 bail!("unable to bool value '{}' - (expected yes/no)", text);
147 }
148 };
149
150 self.eat(Token::Newline)?;
151
152 Ok(value)
153 }
154
155 fn parse_to_eol(&mut self) -> Result<String, Error> {
156 let mut line = String::new();
157 loop {
158 match self.next()? {
159 (Token::Newline, _) => return Ok(line),
160 (_, text) => {
161 if !line.is_empty() { line.push(' '); }
162 line.push_str(&text);
163 }
164 }
165 }
166 }
167
168 fn parse_iface_list(&mut self) -> Result<Vec<String>, Error> {
169 let mut list = Vec::new();
170
171 loop {
172 let (token, text) = self.next()?;
173 match token {
174 Token::Newline => break,
175 Token::Text => {
176 if &text != "none" {
177 list.push(text);
178 }
179 }
180 _ => bail!("unable to parse interface list - unexpected token '{:?}'", token),
181 }
182 }
183
184 Ok(list)
185 }
186
187 fn parse_iface_attributes(
188 &mut self,
189 interface: &mut Interface,
190 address_family_v4: bool,
191 address_family_v6: bool,
192 ) -> Result<(), Error> {
193
194 loop {
195 match self.peek()? {
196 Token::Attribute => { self.eat(Token::Attribute)?; },
197 Token::Comment => {
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);
204 } else {
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);
209 }
210 self.eat(Token::Newline)?;
211 continue;
212 }
213 Token::Newline => break,
214 Token::EOF => break,
215 unexpected => bail!("unexpected token {:?} (expected iface attribute)", unexpected),
216 }
217
218 match self.peek()? {
219 Token::Address => self.parse_iface_address(interface)?,
220 Token::Gateway => self.parse_iface_gateway(interface)?,
221 Token::MTU => {
222 let mtu = self.parse_iface_mtu()?;
223 interface.mtu = Some(mtu);
224 }
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);
229 }
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)?;
235 }
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)?;
241 }
242 Token::Netmask => bail!("netmask is deprecated and no longer supported"),
243
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);
249 } else {
250 interface.options.push(option);
251 }
252 };
253 },
254 }
255 }
256
257 Ok(())
258 }
259
260 fn parse_iface(&mut self, config: &mut NetworkConfig) -> Result<(), Error> {
261 self.eat(Token::Iface)?;
262 let iface = self.next_text()?;
263
264 let mut address_family_v4 = false;
265 let mut address_family_v6 = false;
266 let mut config_method = None;
267
268 loop {
269 let (token, text) = self.next()?;
270 match token {
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),
279 }
280 }
281
282 let config_method = config_method.unwrap_or(NetworkConfigMethod::Static);
283
284 if !(address_family_v4 || address_family_v6) {
285 address_family_v4 = true;
286 address_family_v6 = true;
287 }
288
289 if let Some(mut interface) = config.interfaces.get_mut(&iface) {
290 if address_family_v4 {
291 interface.set_method_v4(config_method)?;
292 }
293 if address_family_v6 {
294 interface.set_method_v6(config_method)?;
295 }
296
297 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
298 } else {
299 let mut interface = Interface::new(iface.clone());
300 if address_family_v4 {
301 interface.set_method_v4(config_method)?;
302 }
303 if address_family_v6 {
304 interface.set_method_v6(config_method)?;
305 }
306
307 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
308
309 config.interfaces.insert(interface.name.clone(), interface);
310
311 config.order.push(NetworkOrderEntry::Iface(iface));
312 }
313
314 Ok(())
315 }
316
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))
320 }
321
322 pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
323 let mut config = NetworkConfig::new();
324
325 let mut auto_flag: HashSet<String> = HashSet::new();
326
327 loop {
328 match self.peek()? {
329 Token::EOF => {
330 break;
331 }
332 Token::Newline => {
333 // skip empty lines
334 self.eat(Token::Newline)?;
335 }
336 Token::Comment => {
337 let (_, text) = self.next()?;
338 config.order.push(NetworkOrderEntry::Comment(text));
339 self.eat(Token::Newline)?;
340 }
341 Token::Auto => {
342 self.parse_auto(&mut auto_flag)?;
343 }
344 Token::Iface => {
345 self.parse_iface(&mut config)?;
346 }
347 _ => {
348 let option = self.parse_to_eol()?;
349 if !option.is_empty() {
350 config.order.push(NetworkOrderEntry::Option(option));
351 }
352 }
353 }
354 }
355
356 for iface in auto_flag.iter() {
357 if let Some(interface) = config.interfaces.get_mut(iface) {
358 interface.autostart = true;
359 }
360 }
361
362 lazy_static!{
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();
366 }
367
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;
374 }
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()));
382 }
383 }
384 }
385
386 for (name, interface) in config.interfaces.iter_mut() {
387 if interface.interface_type != NetworkInterfaceType::Unknown { continue; }
388 if name == "lo" {
389 interface.interface_type = NetworkInterfaceType::Loopback;
390 continue;
391 }
392 if INTERFACE_ALIAS_REGEX.is_match(name) {
393 interface.interface_type = NetworkInterfaceType::Alias;
394 continue;
395 }
396 if VLAN_INTERFACE_REGEX.is_match(name) {
397 interface.interface_type = NetworkInterfaceType::Vlan;
398 continue;
399 }
400 if PHYSICAL_NIC_REGEX.is_match(name) {
401 interface.interface_type = NetworkInterfaceType::Eth;
402 continue;
403 }
404 }
405
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);
412
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
418 match entry {
419 NetworkOrderEntry::Comment(_) => {
420 new_order.push(entry);
421 }
422 _ => {
423 new_order.push(NetworkOrderEntry::Iface(String::from("lo")));
424 added_lo = true;
425 new_order.push(entry);
426 }
427 }
428 }
429 config.order = new_order;
430 }
431
432 Ok(config)
433 }
434 }