]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/network/parser.rs
src/config/network.rs: use a simple String for comments
[proxmox-backup.git] / src / config / network / parser.rs
CommitLineData
904e9886 1use std::io::{BufRead};
f34d4401 2use std::iter::{Peekable, Iterator};
a9bb491e 3use std::collections::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
f34d4401
DM
140 fn parse_to_eol(&mut self) -> Result<String, Error> {
141 let mut line = String::new();
142 loop {
143 match self.next()? {
144 (Token::Newline, _) => return Ok(line),
145 (_, text) => {
146 if !line.is_empty() { line.push(' '); }
147 line.push_str(&text);
148 }
149 }
150 }
151 }
152
1d9a68c2
DM
153 fn parse_iface_list(&mut self) -> Result<Vec<String>, Error> {
154 let mut list = Vec::new();
155
156 loop {
157 let (token, text) = self.next()?;
158 match token {
159 Token::Newline => break,
160 Token::Text => {
161 if &text != "none" {
162 list.push(text);
163 }
164 }
165 _ => bail!("unable to parse interface list - unexpected token '{:?}'", token),
166 }
167 }
168
169 Ok(list)
170 }
c38b4bb8 171
5f60a58f
DM
172 fn parse_iface_attributes(
173 &mut self,
174 interface: &mut Interface,
175 address_family_v4: bool,
176 address_family_v6: bool,
177 ) -> Result<(), Error> {
f34d4401
DM
178
179 loop {
180 match self.peek()? {
5f60a58f
DM
181 Token::Attribute => { self.eat(Token::Attribute)?; },
182 Token::Comment => {
183 let comment = self.eat(Token::Comment)?;
184 if !address_family_v4 && address_family_v6 {
8a6b86b8
DM
185 let mut comments = interface.comments_v6.take().unwrap_or(String::new());
186 if !comments.is_empty() { comments.push('\n'); }
187 comments.push_str(&comment);
188 interface.comments_v6 = Some(comments);
5f60a58f 189 } else {
8a6b86b8
DM
190 let mut comments = interface.comments_v4.take().unwrap_or(String::new());
191 if !comments.is_empty() { comments.push('\n'); }
192 comments.push_str(&comment);
193 interface.comments_v4 = Some(comments);
5f60a58f
DM
194 }
195 self.eat(Token::Newline)?;
196 continue;
197 }
f34d4401 198 Token::Newline => break,
5f60a58f
DM
199 Token::EOF => break,
200 unexpected => bail!("unexpected token {:?} (expected iface attribute)", unexpected),
f34d4401
DM
201 }
202
203 match self.peek()? {
204 Token::Address => self.parse_iface_address(interface)?,
205 Token::Gateway => self.parse_iface_gateway(interface)?,
3fce3bc3
DM
206 Token::MTU => {
207 let mtu = self.parse_iface_mtu()?;
2c18efd9 208 interface.mtu = Some(mtu);
3fce3bc3 209 }
1d9a68c2
DM
210 Token::BridgePorts => {
211 self.eat(Token::BridgePorts)?;
212 let ports = self.parse_iface_list()?;
213 interface.bridge_ports = Some(ports);
c38b4bb8 214 interface.set_interface_type(NetworkInterfaceType::Bridge)?;
1d9a68c2 215 }
42fbe91a
DM
216 Token::BondSlaves => {
217 self.eat(Token::BondSlaves)?;
218 let slaves = self.parse_iface_list()?;
219 interface.bond_slaves = Some(slaves);
c38b4bb8 220 interface.set_interface_type(NetworkInterfaceType::Bond)?;
42fbe91a 221 }
8b57cd44 222 Token::Netmask => bail!("netmask is deprecated and no longer supported"),
5f60a58f
DM
223
224 _ => { // parse addon attributes
225 let option = self.parse_to_eol()?;
226 if !option.is_empty() {
227 if !address_family_v4 && address_family_v6 {
228 interface.options_v6.push(option);
229 } else {
230 interface.options_v4.push(option);
231 }
232 };
233 },
f34d4401
DM
234 }
235 }
236
237 Ok(())
238 }
239
240 fn parse_iface(&mut self, config: &mut NetworkConfig) -> Result<(), Error> {
241 self.eat(Token::Iface)?;
242 let iface = self.next_text()?;
243
92310d58
DM
244 let mut address_family_v4 = false;
245 let mut address_family_v6 = false;
f34d4401
DM
246 let mut config_method = None;
247
248 loop {
249 let (token, text) = self.next()?;
250 match token {
251 Token::Newline => break,
92310d58
DM
252 Token::Inet => address_family_v4 = true,
253 Token::Inet6 => address_family_v6 = true,
7e02d08c
DM
254 Token::Loopback => config_method = Some(NetworkConfigMethod::Loopback),
255 Token::Static => config_method = Some(NetworkConfigMethod::Static),
256 Token::Manual => config_method = Some(NetworkConfigMethod::Manual),
257 Token::DHCP => config_method = Some(NetworkConfigMethod::DHCP),
f34d4401
DM
258 _ => bail!("unknown iface option {}", text),
259 }
260 }
261
7e02d08c 262 let config_method = config_method.unwrap_or(NetworkConfigMethod::Static);
92310d58
DM
263
264 if !(address_family_v4 || address_family_v6) {
265 address_family_v4 = true;
266 address_family_v6 = true;
267 }
f34d4401
DM
268
269 if let Some(mut interface) = config.interfaces.get_mut(&iface) {
92310d58
DM
270 if address_family_v4 {
271 interface.set_method_v4(config_method)?;
272 }
273 if address_family_v6 {
274 interface.set_method_v6(config_method)?;
f34d4401 275 }
92310d58 276
5f60a58f 277 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401 278 } else {
92310d58
DM
279 let mut interface = Interface::new(iface.clone());
280 if address_family_v4 {
281 interface.set_method_v4(config_method)?;
282 }
283 if address_family_v6 {
284 interface.set_method_v6(config_method)?;
285 }
f34d4401 286
5f60a58f 287 self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?;
f34d4401
DM
288
289 config.interfaces.insert(interface.name.clone(), interface);
92310d58 290
f34d4401
DM
291 config.order.push(NetworkOrderEntry::Iface(iface));
292 }
293
294 Ok(())
295 }
296
297 pub fn parse_interfaces(&mut self) -> Result<NetworkConfig, Error> {
298 self._parse_interfaces()
299 .map_err(|err| format_err!("line {}: {}", self.line_nr, err))
300 }
301
302 pub fn _parse_interfaces(&mut self) -> Result<NetworkConfig, Error> {
303 let mut config = NetworkConfig::new();
304
a9bb491e
DM
305 let mut auto_flag: HashSet<String> = HashSet::new();
306
f34d4401 307 loop {
e2d940b9 308 match self.peek()? {
f34d4401 309 Token::EOF => {
a9bb491e 310 break;
f34d4401
DM
311 }
312 Token::Newline => {
e2d940b9 313 // skip empty lines
f34d4401 314 self.eat(Token::Newline)?;
f34d4401
DM
315 }
316 Token::Comment => {
317 let (_, text) = self.next()?;
f34d4401
DM
318 config.order.push(NetworkOrderEntry::Comment(text));
319 self.eat(Token::Newline)?;
320 }
321 Token::Auto => {
a9bb491e 322 self.parse_auto(&mut auto_flag)?;
f34d4401
DM
323 }
324 Token::Iface => {
325 self.parse_iface(&mut config)?;
326 }
327 _ => {
328 let option = self.parse_to_eol()?;
329 if !option.is_empty() {
330 config.order.push(NetworkOrderEntry::Option(option));
331 }
332 }
333 }
334 }
a9bb491e
DM
335
336 for iface in auto_flag.iter() {
337 if let Some(interface) = config.interfaces.get_mut(iface) {
f1026a5a 338 interface.auto = true;
a9bb491e
DM
339 }
340 }
341
3f129233
DM
342 let existing_interfaces = get_network_interfaces()?;
343
344 lazy_static!{
02269f3d
DM
345 static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
346 static ref INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap();
347 static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+$").unwrap();
3f129233
DM
348 }
349
350 for (iface, active) in existing_interfaces.iter() {
3f129233 351 if let Some(interface) = config.interfaces.get_mut(iface) {
3f129233 352 interface.active = *active;
96d94786
DM
353 if interface.interface_type == NetworkInterfaceType::Unknown {
354 interface.interface_type = NetworkInterfaceType::Ethernet;
355 }
356 } else if PHYSICAL_NIC_REGEX.is_match(iface) { // also add all physical NICs
3f129233 357 let mut interface = Interface::new(iface.clone());
7e02d08c 358 interface.set_method_v4(NetworkConfigMethod::Manual)?;
02269f3d 359 interface.interface_type = NetworkInterfaceType::Ethernet;
3f129233
DM
360 interface.active = *active;
361 config.interfaces.insert(interface.name.clone(), interface);
362 config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
363 }
364 }
365
02269f3d
DM
366 for (name, interface) in config.interfaces.iter_mut() {
367 if interface.interface_type != NetworkInterfaceType::Unknown { continue; }
368 if name == "lo" {
369 interface.interface_type = NetworkInterfaceType::Loopback;
370 continue;
371 }
372 if INTERFACE_ALIAS_REGEX.is_match(name) {
373 interface.interface_type = NetworkInterfaceType::Alias;
374 continue;
375 }
376 if VLAN_INTERFACE_REGEX.is_match(name) {
377 interface.interface_type = NetworkInterfaceType::Vlan;
378 continue;
379 }
380 if PHYSICAL_NIC_REGEX.is_match(name) {
96d94786 381 interface.interface_type = NetworkInterfaceType::Vanished;
02269f3d
DM
382 continue;
383 }
384 }
385
a9bb491e 386 Ok(config)
f34d4401
DM
387 }
388}