]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/network/helper.rs
ui: acl view: only update if component is activated
[proxmox-backup.git] / src / config / network / helper.rs
CommitLineData
2eefd9ae
DM
1use std::path::Path;
2use std::process::Command;
f34d4401 3use std::collections::HashMap;
1ec7f8a0
DM
4
5use anyhow::{Error, bail, format_err};
f34d4401 6use lazy_static::lazy_static;
1ec7f8a0
DM
7use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
8use nix::ioctl_read_bad;
9use regex::Regex;
f34d4401 10
8b57cd44
DM
11use proxmox::*; // for IP macros
12
f34d4401
DM
13pub static IPV4_REVERSE_MASK: &[&'static str] = &[
14 "0.0.0.0",
15 "128.0.0.0",
16 "192.0.0.0",
17 "224.0.0.0",
18 "240.0.0.0",
19 "248.0.0.0",
20 "252.0.0.0",
21 "254.0.0.0",
22 "255.0.0.0",
23 "255.128.0.0",
24 "255.192.0.0",
25 "255.224.0.0",
26 "255.240.0.0",
27 "255.248.0.0",
28 "255.252.0.0",
29 "255.254.0.0",
30 "255.255.0.0",
31 "255.255.128.0",
32 "255.255.192.0",
33 "255.255.224.0",
34 "255.255.240.0",
35 "255.255.248.0",
36 "255.255.252.0",
37 "255.255.254.0",
38 "255.255.255.0",
39 "255.255.255.128",
40 "255.255.255.192",
41 "255.255.255.224",
42 "255.255.255.240",
43 "255.255.255.248",
44 "255.255.255.252",
45 "255.255.255.254",
46 "255.255.255.255",
47];
48
49lazy_static! {
50 pub static ref IPV4_MASK_HASH_LOCALNET: HashMap<&'static str, u8> = {
51 let mut map = HashMap::new();
52 for i in 8..32 {
53 map.insert(IPV4_REVERSE_MASK[i], i as u8);
54 }
55 map
56 };
57}
1ec7f8a0 58
8b57cd44
DM
59pub fn parse_cidr(cidr: &str) -> Result<(String, u8, bool), Error> {
60
61 lazy_static! {
62 pub static ref CIDR_V4_REGEX: Regex = Regex::new(
0c226bc1 63 concat!(r"^(", IPV4RE!(), r")(?:/(\d{1,2}))$")
8b57cd44
DM
64 ).unwrap();
65 pub static ref CIDR_V6_REGEX: Regex = Regex::new(
0c226bc1 66 concat!(r"^(", IPV6RE!(), r")(?:/(\d{1,3}))$")
8b57cd44
DM
67 ).unwrap();
68 }
69
70 if let Some(caps) = CIDR_V4_REGEX.captures(&cidr) {
71 let address = &caps[1];
72 let mask = &caps[2];
73 let mask = u8::from_str_radix(mask, 10)
74 .map(|mask| {
75 if !(mask > 0 && mask <= 32) {
76 bail!("IPv4 mask '{}' is out of range (1..32).", mask);
77 }
78 Ok(mask)
79 })?;
80 return Ok((address.to_string(), mask.unwrap(), false));
81 } else if let Some(caps) = CIDR_V6_REGEX.captures(&cidr) {
82 let address = &caps[1];
83 let mask = &caps[2];
84 let mask = u8::from_str_radix(mask, 10)
85 .map(|mask| {
86 if !(mask >= 1 && mask <= 128) {
87 bail!("IPv6 mask '{}' is out of range (1..128).", mask);
88 }
89 Ok(mask)
90 })?;
91 return Ok((address.to_string(), mask.unwrap(), true));
92 } else {
93 bail!("invalid address/mask '{}'", cidr);
94 }
95}
96
1ec7f8a0
DM
97pub fn get_network_interfaces() -> Result<HashMap<String, bool>, Error> {
98
99 const PROC_NET_DEV: &str = "/proc/net/dev";
100
101 #[repr(C)]
102 pub struct ifreq {
103 ifr_name: [libc::c_uchar; libc::IFNAMSIZ],
104 ifru_flags: libc::c_short,
105 }
106
107 ioctl_read_bad!(get_interface_flags, libc::SIOCGIFFLAGS, ifreq);
108
109 lazy_static!{
110 static ref IFACE_LINE_REGEX: Regex = Regex::new(r"^\s*([^:\s]+):").unwrap();
111 }
112 let raw = std::fs::read_to_string(PROC_NET_DEV)
113 .map_err(|err| format_err!("unable to read {} - {}", PROC_NET_DEV, err))?;
114
115 let lines = raw.lines();
116
117 let sock = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None)
118 .or_else(|_| socket(AddressFamily::Inet6, SockType::Datagram, SockFlag::empty(), None))?;
119
120 let mut interface_list = HashMap::new();
121
122 for line in lines {
123 if let Some(cap) = IFACE_LINE_REGEX.captures(line) {
124 let ifname = &cap[1];
125
126 let mut req = ifreq { ifr_name: *b"0000000000000000", ifru_flags: 0 };
127 for (i, b) in std::ffi::CString::new(ifname)?.as_bytes_with_nul().iter().enumerate() {
128 if i < (libc::IFNAMSIZ-1) { req.ifr_name[i] = *b as libc::c_uchar; }
129 }
130 let res = unsafe { get_interface_flags(sock, &mut req)? };
131 if res != 0 {
132 bail!("ioctl get_interface_flags for '{}' failed ({})", ifname, res);
133 }
134 let is_up = (req.ifru_flags & (libc::IFF_UP as libc::c_short)) != 0;
135 interface_list.insert(ifname.to_string(), is_up);
136 }
137 }
138
139 Ok(interface_list)
140}
2eefd9ae
DM
141
142pub fn compute_file_diff(filename: &str, shadow: &str) -> Result<String, Error> {
143
144 let output = Command::new("/usr/bin/diff")
145 .arg("-b")
146 .arg("-u")
147 .arg(filename)
148 .arg(shadow)
149 .output()
150 .map_err(|err| format_err!("failed to execute diff - {}", err))?;
151
152 if !output.status.success() {
153 match output.status.code() {
154 Some(code) => {
155 if code == 0 { return Ok(String::new()); }
156 if code != 1 {
157 let msg = String::from_utf8(output.stderr)
158 .map(|m| if m.is_empty() { String::from("no error message") } else { m })
159 .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
160
161 bail!("diff failed with status code: {} - {}", code, msg);
162 }
163 }
164 None => bail!("diff terminated by signal"),
165 }
166 }
167
168 let diff = String::from_utf8(output.stdout)?;
169
170 Ok(diff)
171}
172
173pub fn assert_ifupdown2_installed() -> Result<(), Error> {
174 if !Path::new("/usr/share/ifupdown2").exists() {
175 bail!("ifupdown2 is not installed.");
176 }
177
178 Ok(())
179}
180
181pub fn network_reload() -> Result<(), Error> {
182
183 let status = Command::new("/sbin/ifreload")
184 .arg("-a")
185 .status()
186 .map_err(|err| format_err!("failed to execute ifreload: - {}", err))?;
187
188 if !status.success() {
189 match status.code() {
190 Some(code) => bail!("ifreload failed with status code: {}", code),
191 None => bail!("ifreload terminated by signal")
192 }
193 }
194
195 Ok(())
196}