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