2020-04-24 07:55:46 +00:00
|
|
|
use std::path::Path;
|
|
|
|
use std::process::Command;
|
2020-04-20 12:15:57 +00:00
|
|
|
use std::collections::HashMap;
|
2020-11-30 10:10:32 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
2020-04-21 08:32:54 +00:00
|
|
|
|
|
|
|
use anyhow::{Error, bail, format_err};
|
2020-04-20 12:15:57 +00:00
|
|
|
use lazy_static::lazy_static;
|
2020-04-21 08:32:54 +00:00
|
|
|
use nix::ioctl_read_bad;
|
2020-11-30 10:10:32 +00:00
|
|
|
use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
|
2020-04-21 08:32:54 +00:00
|
|
|
use regex::Regex;
|
2020-04-20 12:15:57 +00:00
|
|
|
|
2020-04-22 06:45:13 +00:00
|
|
|
use proxmox::*; // for IP macros
|
2020-11-30 10:10:32 +00:00
|
|
|
use proxmox::tools::fd::Fd;
|
2020-04-22 06:45:13 +00:00
|
|
|
|
2020-10-14 12:22:38 +00:00
|
|
|
pub static IPV4_REVERSE_MASK: &[&str] = &[
|
2020-04-20 12:15:57 +00:00
|
|
|
"0.0.0.0",
|
|
|
|
"128.0.0.0",
|
|
|
|
"192.0.0.0",
|
|
|
|
"224.0.0.0",
|
|
|
|
"240.0.0.0",
|
|
|
|
"248.0.0.0",
|
|
|
|
"252.0.0.0",
|
|
|
|
"254.0.0.0",
|
|
|
|
"255.0.0.0",
|
|
|
|
"255.128.0.0",
|
|
|
|
"255.192.0.0",
|
|
|
|
"255.224.0.0",
|
|
|
|
"255.240.0.0",
|
|
|
|
"255.248.0.0",
|
|
|
|
"255.252.0.0",
|
|
|
|
"255.254.0.0",
|
|
|
|
"255.255.0.0",
|
|
|
|
"255.255.128.0",
|
|
|
|
"255.255.192.0",
|
|
|
|
"255.255.224.0",
|
|
|
|
"255.255.240.0",
|
|
|
|
"255.255.248.0",
|
|
|
|
"255.255.252.0",
|
|
|
|
"255.255.254.0",
|
|
|
|
"255.255.255.0",
|
|
|
|
"255.255.255.128",
|
|
|
|
"255.255.255.192",
|
|
|
|
"255.255.255.224",
|
|
|
|
"255.255.255.240",
|
|
|
|
"255.255.255.248",
|
|
|
|
"255.255.255.252",
|
|
|
|
"255.255.255.254",
|
|
|
|
"255.255.255.255",
|
|
|
|
];
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
pub static ref IPV4_MASK_HASH_LOCALNET: HashMap<&'static str, u8> = {
|
|
|
|
let mut map = HashMap::new();
|
2021-01-19 14:03:04 +00:00
|
|
|
#[allow(clippy::needless_range_loop)]
|
2020-04-20 12:15:57 +00:00
|
|
|
for i in 8..32 {
|
|
|
|
map.insert(IPV4_REVERSE_MASK[i], i as u8);
|
|
|
|
}
|
|
|
|
map
|
|
|
|
};
|
|
|
|
}
|
2020-04-21 08:32:54 +00:00
|
|
|
|
2020-04-22 06:45:13 +00:00
|
|
|
pub fn parse_cidr(cidr: &str) -> Result<(String, u8, bool), Error> {
|
2020-11-02 13:32:35 +00:00
|
|
|
let (address, mask, is_v6) = parse_address_or_cidr(cidr)?;
|
|
|
|
if let Some(mask) = mask {
|
2021-01-19 09:50:42 +00:00
|
|
|
Ok((address, mask, is_v6))
|
2020-11-02 13:32:35 +00:00
|
|
|
} else {
|
|
|
|
bail!("missing netmask in '{}'", cidr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_netmask(mask: u8, is_v6: bool) -> Result<(), Error> {
|
2021-01-15 13:10:24 +00:00
|
|
|
let (ver, min, max) = if is_v6 {
|
|
|
|
("IPv6", 1, 128)
|
2020-11-02 13:32:35 +00:00
|
|
|
} else {
|
2021-01-15 13:10:24 +00:00
|
|
|
("IPv4", 1, 32)
|
|
|
|
};
|
|
|
|
|
|
|
|
if !(mask >= min && mask <= max) {
|
|
|
|
bail!("{} mask '{}' is out of range ({}..{}).", ver, mask, min, max);
|
2020-11-02 13:32:35 +00:00
|
|
|
}
|
2021-01-15 13:10:24 +00:00
|
|
|
|
2020-11-02 13:32:35 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-10 15:37:09 +00:00
|
|
|
// parse ip address with optional cidr mask
|
2020-11-02 13:32:35 +00:00
|
|
|
pub fn parse_address_or_cidr(cidr: &str) -> Result<(String, Option<u8>, bool), Error> {
|
2020-04-22 06:45:13 +00:00
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
pub static ref CIDR_V4_REGEX: Regex = Regex::new(
|
2020-11-02 13:32:35 +00:00
|
|
|
concat!(r"^(", IPV4RE!(), r")(?:/(\d{1,2}))?$")
|
2020-04-22 06:45:13 +00:00
|
|
|
).unwrap();
|
|
|
|
pub static ref CIDR_V6_REGEX: Regex = Regex::new(
|
2020-11-02 13:32:35 +00:00
|
|
|
concat!(r"^(", IPV6RE!(), r")(?:/(\d{1,3}))?$")
|
2020-04-22 06:45:13 +00:00
|
|
|
).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(caps) = CIDR_V4_REGEX.captures(&cidr) {
|
|
|
|
let address = &caps[1];
|
2020-11-02 13:32:35 +00:00
|
|
|
if let Some(mask) = caps.get(2) {
|
|
|
|
let mask = u8::from_str_radix(mask.as_str(), 10)?;
|
|
|
|
check_netmask(mask, false)?;
|
2021-01-19 09:50:42 +00:00
|
|
|
Ok((address.to_string(), Some(mask), false))
|
2020-11-02 13:32:35 +00:00
|
|
|
} else {
|
2021-01-19 09:50:42 +00:00
|
|
|
Ok((address.to_string(), None, false))
|
2020-11-02 13:32:35 +00:00
|
|
|
}
|
2020-04-22 06:45:13 +00:00
|
|
|
} else if let Some(caps) = CIDR_V6_REGEX.captures(&cidr) {
|
|
|
|
let address = &caps[1];
|
2020-11-02 13:32:35 +00:00
|
|
|
if let Some(mask) = caps.get(2) {
|
|
|
|
let mask = u8::from_str_radix(mask.as_str(), 10)?;
|
|
|
|
check_netmask(mask, true)?;
|
2021-01-19 09:50:42 +00:00
|
|
|
Ok((address.to_string(), Some(mask), true))
|
2020-11-02 13:32:35 +00:00
|
|
|
} else {
|
2021-01-19 09:50:42 +00:00
|
|
|
Ok((address.to_string(), None, true))
|
2020-11-02 13:32:35 +00:00
|
|
|
}
|
2020-04-22 06:45:13 +00:00
|
|
|
} else {
|
|
|
|
bail!("invalid address/mask '{}'", cidr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 08:32:54 +00:00
|
|
|
pub fn get_network_interfaces() -> Result<HashMap<String, bool>, Error> {
|
|
|
|
|
|
|
|
const PROC_NET_DEV: &str = "/proc/net/dev";
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct ifreq {
|
|
|
|
ifr_name: [libc::c_uchar; libc::IFNAMSIZ],
|
|
|
|
ifru_flags: libc::c_short,
|
|
|
|
}
|
|
|
|
|
|
|
|
ioctl_read_bad!(get_interface_flags, libc::SIOCGIFFLAGS, ifreq);
|
|
|
|
|
|
|
|
lazy_static!{
|
|
|
|
static ref IFACE_LINE_REGEX: Regex = Regex::new(r"^\s*([^:\s]+):").unwrap();
|
|
|
|
}
|
|
|
|
let raw = std::fs::read_to_string(PROC_NET_DEV)
|
|
|
|
.map_err(|err| format_err!("unable to read {} - {}", PROC_NET_DEV, err))?;
|
|
|
|
|
|
|
|
let lines = raw.lines();
|
|
|
|
|
2020-11-30 10:10:32 +00:00
|
|
|
let sock = unsafe {
|
|
|
|
Fd::from_raw_fd(
|
|
|
|
socket(
|
|
|
|
AddressFamily::Inet,
|
|
|
|
SockType::Datagram,
|
|
|
|
SockFlag::empty(),
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.or_else(|_| {
|
|
|
|
socket(
|
|
|
|
AddressFamily::Inet6,
|
|
|
|
SockType::Datagram,
|
|
|
|
SockFlag::empty(),
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
})?,
|
|
|
|
)
|
|
|
|
};
|
2020-04-21 08:32:54 +00:00
|
|
|
|
|
|
|
let mut interface_list = HashMap::new();
|
|
|
|
|
|
|
|
for line in lines {
|
|
|
|
if let Some(cap) = IFACE_LINE_REGEX.captures(line) {
|
|
|
|
let ifname = &cap[1];
|
|
|
|
|
|
|
|
let mut req = ifreq { ifr_name: *b"0000000000000000", ifru_flags: 0 };
|
|
|
|
for (i, b) in std::ffi::CString::new(ifname)?.as_bytes_with_nul().iter().enumerate() {
|
|
|
|
if i < (libc::IFNAMSIZ-1) { req.ifr_name[i] = *b as libc::c_uchar; }
|
|
|
|
}
|
2020-11-30 10:10:32 +00:00
|
|
|
let res = unsafe { get_interface_flags(sock.as_raw_fd(), &mut req)? };
|
2020-04-21 08:32:54 +00:00
|
|
|
if res != 0 {
|
|
|
|
bail!("ioctl get_interface_flags for '{}' failed ({})", ifname, res);
|
|
|
|
}
|
|
|
|
let is_up = (req.ifru_flags & (libc::IFF_UP as libc::c_short)) != 0;
|
|
|
|
interface_list.insert(ifname.to_string(), is_up);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(interface_list)
|
|
|
|
}
|
2020-04-24 07:55:46 +00:00
|
|
|
|
|
|
|
pub fn compute_file_diff(filename: &str, shadow: &str) -> Result<String, Error> {
|
|
|
|
|
2020-06-15 09:16:11 +00:00
|
|
|
let output = Command::new("diff")
|
2020-04-24 07:55:46 +00:00
|
|
|
.arg("-b")
|
|
|
|
.arg("-u")
|
|
|
|
.arg(filename)
|
|
|
|
.arg(shadow)
|
|
|
|
.output()
|
|
|
|
.map_err(|err| format_err!("failed to execute diff - {}", err))?;
|
|
|
|
|
2020-09-30 07:28:48 +00:00
|
|
|
let diff = crate::tools::command_output_as_string(output, Some(|c| c == 0 || c == 1))
|
2020-05-27 04:52:21 +00:00
|
|
|
.map_err(|err| format_err!("diff failed: {}", err))?;
|
2020-04-24 07:55:46 +00:00
|
|
|
|
|
|
|
Ok(diff)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn assert_ifupdown2_installed() -> Result<(), Error> {
|
|
|
|
if !Path::new("/usr/share/ifupdown2").exists() {
|
|
|
|
bail!("ifupdown2 is not installed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn network_reload() -> Result<(), Error> {
|
|
|
|
|
2020-06-15 09:16:11 +00:00
|
|
|
let output = Command::new("ifreload")
|
2020-04-24 07:55:46 +00:00
|
|
|
.arg("-a")
|
2020-05-27 05:25:39 +00:00
|
|
|
.output()
|
2020-06-15 09:16:11 +00:00
|
|
|
.map_err(|err| format_err!("failed to execute 'ifreload' - {}", err))?;
|
2020-05-27 05:25:39 +00:00
|
|
|
|
|
|
|
crate::tools::command_output(output, None)
|
|
|
|
.map_err(|err| format_err!("ifreload failed: {}", err))?;
|
2020-04-24 07:55:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|