2 use std
::process
::Command
;
3 use std
::collections
::HashMap
;
4 use std
::os
::unix
::io
::{AsRawFd, FromRawFd}
;
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}
;
12 use proxmox
::*; // for IP macros
13 use proxmox
::tools
::fd
::Fd
;
15 pub static IPV4_REVERSE_MASK
: &[&str] = &[
52 pub static ref IPV4_MASK_HASH_LOCALNET
: HashMap
<&'
static str, u8> = {
53 let mut map
= HashMap
::new();
54 #[allow(clippy::needless_range_loop)]
56 map
.insert(IPV4_REVERSE_MASK
[i
], i
as u8);
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
))
67 bail
!("missing netmask in '{}'", cidr
);
71 pub fn check_netmask(mask
: u8, is_v6
: bool
) -> Result
<(), Error
> {
72 let (ver
, min
, max
) = if is_v6
{
78 if !(mask
>= min
&& mask
<= max
) {
79 bail
!("{} mask '{}' is out of range ({}..{}).", ver
, mask
, min
, max
);
85 // parse ip address with otional cidr mask
86 pub fn parse_address_or_cidr(cidr
: &str) -> Result
<(String
, Option
<u8>, bool
), Error
> {
89 pub static ref CIDR_V4_REGEX
: Regex
= Regex
::new(
90 concat
!(r
"^(", IPV4RE
!(), r
")(?:/(\d{1,2}))?$")
92 pub static ref CIDR_V6_REGEX
: Regex
= Regex
::new(
93 concat
!(r
"^(", IPV6RE
!(), r
")(?:/(\d{1,3}))?$")
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))
104 Ok((address
.to_string(), None
, false))
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))
113 Ok((address
.to_string(), None
, true))
116 bail
!("invalid address/mask '{}'", cidr
);
120 pub fn get_network_interfaces() -> Result
<HashMap
<String
, bool
>, Error
> {
122 const PROC_NET_DEV
: &str = "/proc/net/dev";
126 ifr_name
: [libc
::c_uchar
; libc
::IFNAMSIZ
],
127 ifru_flags
: libc
::c_short
,
130 ioctl_read_bad
!(get_interface_flags
, libc
::SIOCGIFFLAGS
, ifreq
);
133 static ref IFACE_LINE_REGEX
: Regex
= Regex
::new(r
"^\s*([^:\s]+):").unwrap();
135 let raw
= std
::fs
::read_to_string(PROC_NET_DEV
)
136 .map_err(|err
| format_err
!("unable to read {} - {}", PROC_NET_DEV
, err
))?
;
138 let lines
= raw
.lines();
150 AddressFamily
::Inet6
,
159 let mut interface_list
= HashMap
::new();
162 if let Some(cap
) = IFACE_LINE_REGEX
.captures(line
) {
163 let ifname
= &cap
[1];
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; }
169 let res
= unsafe { get_interface_flags(sock.as_raw_fd(), &mut req)? }
;
171 bail
!("ioctl get_interface_flags for '{}' failed ({})", ifname
, res
);
173 let is_up
= (req
.ifru_flags
& (libc
::IFF_UP
as libc
::c_short
)) != 0;
174 interface_list
.insert(ifname
.to_string(), is_up
);
181 pub fn compute_file_diff(filename
: &str, shadow
: &str) -> Result
<String
, Error
> {
183 let output
= Command
::new("diff")
189 .map_err(|err
| format_err
!("failed to execute diff - {}", err
))?
;
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
))?
;
197 pub fn assert_ifupdown2_installed() -> Result
<(), Error
> {
198 if !Path
::new("/usr/share/ifupdown2").exists() {
199 bail
!("ifupdown2 is not installed.");
205 pub fn network_reload() -> Result
<(), Error
> {
207 let output
= Command
::new("ifreload")
210 .map_err(|err
| format_err
!("failed to execute 'ifreload' - {}", err
))?
;
212 crate::tools
::command_output(output
, None
)
213 .map_err(|err
| format_err
!("ifreload failed: {}", err
))?
;