]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/network/helper.rs
typo fixes all over the place
[proxmox-backup.git] / src / config / network / helper.rs
CommitLineData
2eefd9ae
DM
1use std::path::Path;
2use std::process::Command;
f34d4401 3use std::collections::HashMap;
99e98f60 4use std::os::unix::io::{AsRawFd, FromRawFd};
1ec7f8a0
DM
5
6use anyhow::{Error, bail, format_err};
f34d4401 7use lazy_static::lazy_static;
1ec7f8a0 8use nix::ioctl_read_bad;
99e98f60 9use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
1ec7f8a0 10use regex::Regex;
f34d4401 11
8b57cd44 12use proxmox::*; // for IP macros
99e98f60 13use proxmox::tools::fd::Fd;
8b57cd44 14
20813274 15pub static IPV4_REVERSE_MASK: &[&str] = &[
f34d4401
DM
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
51lazy_static! {
52 pub static ref IPV4_MASK_HASH_LOCALNET: HashMap<&'static str, u8> = {
53 let mut map = HashMap::new();
382f10a0 54 #[allow(clippy::needless_range_loop)]
f34d4401
DM
55 for i in 8..32 {
56 map.insert(IPV4_REVERSE_MASK[i], i as u8);
57 }
58 map
59 };
60}
1ec7f8a0 61
8b57cd44 62pub fn parse_cidr(cidr: &str) -> Result<(String, u8, bool), Error> {
645a47ff
DM
63 let (address, mask, is_v6) = parse_address_or_cidr(cidr)?;
64 if let Some(mask) = mask {
38556bf6 65 Ok((address, mask, is_v6))
645a47ff
DM
66 } else {
67 bail!("missing netmask in '{}'", cidr);
68 }
69}
70
71pub fn check_netmask(mask: u8, is_v6: bool) -> Result<(), Error> {
47ea98e0
FG
72 let (ver, min, max) = if is_v6 {
73 ("IPv6", 1, 128)
645a47ff 74 } else {
47ea98e0
FG
75 ("IPv4", 1, 32)
76 };
77
78 if !(mask >= min && mask <= max) {
79 bail!("{} mask '{}' is out of range ({}..{}).", ver, mask, min, max);
645a47ff 80 }
47ea98e0 81
645a47ff
DM
82 Ok(())
83}
84
d1d74c43 85// parse ip address with optional cidr mask
645a47ff 86pub fn parse_address_or_cidr(cidr: &str) -> Result<(String, Option<u8>, bool), Error> {
8b57cd44
DM
87
88 lazy_static! {
89 pub static ref CIDR_V4_REGEX: Regex = Regex::new(
645a47ff 90 concat!(r"^(", IPV4RE!(), r")(?:/(\d{1,2}))?$")
8b57cd44
DM
91 ).unwrap();
92 pub static ref CIDR_V6_REGEX: Regex = Regex::new(
645a47ff 93 concat!(r"^(", IPV6RE!(), r")(?:/(\d{1,3}))?$")
8b57cd44
DM
94 ).unwrap();
95 }
96
97 if let Some(caps) = CIDR_V4_REGEX.captures(&cidr) {
98 let address = &caps[1];
645a47ff
DM
99 if let Some(mask) = caps.get(2) {
100 let mask = u8::from_str_radix(mask.as_str(), 10)?;
101 check_netmask(mask, false)?;
38556bf6 102 Ok((address.to_string(), Some(mask), false))
645a47ff 103 } else {
38556bf6 104 Ok((address.to_string(), None, false))
645a47ff 105 }
8b57cd44
DM
106 } else if let Some(caps) = CIDR_V6_REGEX.captures(&cidr) {
107 let address = &caps[1];
645a47ff
DM
108 if let Some(mask) = caps.get(2) {
109 let mask = u8::from_str_radix(mask.as_str(), 10)?;
110 check_netmask(mask, true)?;
38556bf6 111 Ok((address.to_string(), Some(mask), true))
645a47ff 112 } else {
38556bf6 113 Ok((address.to_string(), None, true))
645a47ff 114 }
8b57cd44
DM
115 } else {
116 bail!("invalid address/mask '{}'", cidr);
117 }
118}
119
1ec7f8a0
DM
120pub 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
99e98f60
WB
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 };
1ec7f8a0
DM
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 }
99e98f60 169 let res = unsafe { get_interface_flags(sock.as_raw_fd(), &mut req)? };
1ec7f8a0
DM
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}
2eefd9ae
DM
180
181pub fn compute_file_diff(filename: &str, shadow: &str) -> Result<String, Error> {
182
cbef49bf 183 let output = Command::new("diff")
2eefd9ae
DM
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
e64b9f92 191 let diff = crate::tools::command_output_as_string(output, Some(|c| c == 0 || c == 1))
97fab7aa 192 .map_err(|err| format_err!("diff failed: {}", err))?;
2eefd9ae
DM
193
194 Ok(diff)
195}
196
197pub 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
205pub fn network_reload() -> Result<(), Error> {
206
cbef49bf 207 let output = Command::new("ifreload")
2eefd9ae 208 .arg("-a")
143b6545 209 .output()
cbef49bf 210 .map_err(|err| format_err!("failed to execute 'ifreload' - {}", err))?;
143b6545
DM
211
212 crate::tools::command_output(output, None)
213 .map_err(|err| format_err!("ifreload failed: {}", err))?;
2eefd9ae 214
2eefd9ae
DM
215
216 Ok(())
217}