]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/network/parser.rs
src/config/network.rs: make it compatible with pve
[proxmox-backup.git] / src / config / 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
02269f3d 12use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType};
f34d4401 13
904e9886
DM
14pub struct NetworkParser<R: BufRead> {
15 input: Peekable<Lexer<R>>,
f34d4401
DM
16 line_nr: usize,
17}
18
904e9886 19impl <R: BufRead> NetworkParser<R> {
f34d4401 20
904e9886 21 pub fn new(reader: R) -> Self {
f34d4401
DM
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
5f60a58f
DM
62 fn eat(&mut self, expected: Token) -> Result<String, Error> {
63 let (next, text) = self.next()?;
f34d4401
DM
64 if next != expected {
65 bail!("expected {:?}, got {:?}", expected, next);
66 }
5f60a58f 67 Ok(text)
f34d4401
DM
68 }
69
a9bb491e 70 fn parse_auto(&mut self, auto_flag: &mut HashSet<String>) -> Result<(), Error> {
f34d4401
DM
71 self.eat(Token::Auto)?;
72
a9bb491e
DM
73 loop {
74 match self.next()? {
75 (Token::Text, iface) => {
e2d940b9 76 auto_flag.insert(iface.to_string());
a9bb491e
DM
77 }
78 (Token::Newline, _) => break,
79 unexpected => {
80 bail!("expected {:?}, got {:?}", Token::Text, unexpected);
81 }
82 }
f34d4401 83
a9bb491e 84 }
f34d4401
DM
85
86 Ok(())
87 }
88
89 fn parse_iface_address(&mut self, interface: &mut Interface) -> Result<(), Error> {
90 self.eat(Token::Address)?;
8b57cd44 91 let cidr = self.next_text()?;
f34d4401 92
8b57cd44
DM
93 let (_address, _mask, ipv6) = parse_cidr(&cidr)?;
94 if ipv6 {
95 interface.set_cidr_v6(cidr)?;
f34d4401 96 } else {
8b57cd44 97 interface.set_cidr_v4(cidr)?;
f34d4401
DM
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
3fce3bc3
DM
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
7b22acd0
DM
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
f34d4401
DM
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
1d9a68c2
DM
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 }
c38b4bb8 186
5f60a58f
DM
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> {
f34d4401
DM
193
194 loop {
195 match self.peek()? {
5f60a58f
DM
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 {
7b22acd0 200 let mut comments = interface.comments6.take().unwrap_or(String::new());
8a6b86b8
DM
201 if !comments.is_empty() { comments.push('\n'); }
202 comments.push_str(&comment);
7b22acd0 203 interface.comments6 = Some(comments);
5f60a58f 204 } else {
7b22acd0 205 let mut comments = interface.comments.take().unwrap_or(String::new());
8a6b86b8
DM
206 if !comments.is_empty() { comments.push('\n'); }
207 comments.push_str(&comment);
7b22acd0 208 interface.comments = Some(comments);
5f60a58f
DM
209 }
210 self.eat(Token::Newline)?;
211 continue;
212 }
f34d4401 213 Token::Newline => break,
5f60a58f
DM
214 Token::EOF => break,
215 unexpected => bail!("unexpected token {:?} (expected iface attribute)", unexpected),
f34d4401
DM
216 }
217
218 match self.peek()? {
219 Token::Address => self.parse_iface_address(interface)?,
220 Token::Gateway => self.parse_iface_gateway(interface)?,
3fce3bc3
DM
221 Token::MTU => {
222 let mtu = self.parse_iface_mtu()?;
2c18efd9 223 interface.mtu = Some(mtu);
3fce3bc3 224 }
7b22acd0
DM
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 }
1d9a68c2
DM
230 Token::BridgePorts => {
231 self.eat(Token::BridgePorts)?;
232 let ports = self.parse_iface_list()?;
233 interface.bridge_ports = Some(ports);
c38b4bb8 234 interface.set_interface_type(NetworkInterfaceType::Bridge)?;
1d9a68c2 235 }
42fbe91a
DM
236 Token::BondSlaves => {
237 self.eat(Token::BondSlaves)?;
238 let slaves = self.parse_iface_list()?;
239 interface.bond_slaves = Some(slaves);
c38b4bb8 240 interface.set_interface_type(NetworkInterfaceType::Bond)?;
42fbe91a 241 }
8b57cd44 242 Token::Netmask => bail!("netmask is deprecated and no longer supported"),
5f60a58f
DM
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 {
7b22acd0 248 interface.options6.push(option);
5f60a58f 249 } else {
7b22acd0 250 interface.options.push(option);
5f60a58f
DM
251 }
252 };
253 },
f34d4401
DM
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
92310d58
DM
264 let mut address_family_v4 = false;
265 let mut address_family_v6 = false;
f34d4401
DM
266 let mut config_method = None;
267
268 loop {
269 let (token, text) = self.next()?;
270 match token {
271 Token::Newline => break,
92310d58
DM
272 Token::Inet => address_family_v4 = true,
273 Token::Inet6 => address_family_v6 = true,
7e02d08c
DM
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),
f34d4401
DM
278 _ => bail!("unknown iface option {}", text),
279 }
280 }
281
7e02d08c 282 let config_method = config_method.unwrap_or(NetworkConfigMethod::Static);
92310d58
DM
283
284 if !(address_family_v4 || address_family_v6) {
285 address_family_v4 = true;
286 address_family_v6 = true;
287 }
f34d4401
DM
288
289 if let Some(mut interface) = config.interfaces.get_mut(&iface) {
92310d58
DM
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)?;
f34d4401 295 }
92310d58 296
5f60a58f 297 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401 298 } else {
92310d58
DM
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 }
f34d4401 306
5f60a58f 307 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401
DM
308
309 config.interfaces.insert(interface.name.clone(), interface);
92310d58 310
f34d4401
DM
311 config.order.push(NetworkOrderEntry::Iface(iface));
312 }
313
314 Ok(())
315 }
316
1ca540a6
DM
317 pub fn parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
318 self._parse_interfaces(existing_interfaces)
f34d4401
DM
319 .map_err(|err| format_err!("line {}: {}", self.line_nr, err))
320 }
321
1ca540a6 322 pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
f34d4401
DM
323 let mut config = NetworkConfig::new();
324
a9bb491e
DM
325 let mut auto_flag: HashSet<String> = HashSet::new();
326
f34d4401 327 loop {
e2d940b9 328 match self.peek()? {
f34d4401 329 Token::EOF => {
a9bb491e 330 break;
f34d4401
DM
331 }
332 Token::Newline => {
e2d940b9 333 // skip empty lines
f34d4401 334 self.eat(Token::Newline)?;
f34d4401
DM
335 }
336 Token::Comment => {
337 let (_, text) = self.next()?;
f34d4401
DM
338 config.order.push(NetworkOrderEntry::Comment(text));
339 self.eat(Token::Newline)?;
340 }
341 Token::Auto => {
a9bb491e 342 self.parse_auto(&mut auto_flag)?;
f34d4401
DM
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 }
a9bb491e
DM
355
356 for iface in auto_flag.iter() {
357 if let Some(interface) = config.interfaces.get_mut(iface) {
7b22acd0 358 interface.autostart = true;
a9bb491e
DM
359 }
360 }
361
3f129233 362 lazy_static!{
02269f3d
DM
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();
3f129233
DM
366 }
367
1ca540a6
DM
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;
7b22acd0
DM
372 if interface.interface_type == NetworkInterfaceType::Unknown && PHYSICAL_NIC_REGEX.is_match(iface) {
373 interface.interface_type = NetworkInterfaceType::Eth;
1ca540a6
DM
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)?;
7b22acd0 378 interface.interface_type = NetworkInterfaceType::Eth;
1ca540a6
DM
379 interface.active = *active;
380 config.interfaces.insert(interface.name.clone(), interface);
381 config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
96d94786 382 }
3f129233
DM
383 }
384 }
385
02269f3d
DM
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) {
7b22acd0 401 interface.interface_type = NetworkInterfaceType::Eth;
02269f3d
DM
402 continue;
403 }
404 }
405
1ca540a6
DM
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;
7b22acd0 410 interface.autostart = true;
1ca540a6
DM
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
a9bb491e 432 Ok(config)
f34d4401
DM
433 }
434}