use super::super::nm_dbus::{NmConnection, NmIpRoute, NmSettingIp};
const NM_IP_SETTING_ROUTE_TABLE_DEFAULT: u32 = 0;
const NM_IP_SETTING_ROUTE_METRIC_DEFAULT: i64 = -1;
const IPV6_METRIC_COHERCED_DEFAULT: u32 = 1024;
pub(crate) fn is_route_removed(
new_nm_conn: &NmConnection,
cur_nm_conn: &NmConnection,
) -> bool {
nm_setting_is_route_removed(
new_nm_conn.ipv4.as_ref(),
cur_nm_conn.ipv4.as_ref(),
false,
) || nm_setting_is_route_removed(
new_nm_conn.ipv6.as_ref(),
cur_nm_conn.ipv6.as_ref(),
true,
)
}
pub(crate) fn is_route_table_changed(
new_nm_conn: &NmConnection,
cur_nm_conn: &NmConnection,
) -> bool {
is_nm_ip_route_table_changed(
new_nm_conn.ipv4.as_ref(),
cur_nm_conn.ipv4.as_ref(),
) || is_nm_ip_route_table_changed(
new_nm_conn.ipv6.as_ref(),
cur_nm_conn.ipv6.as_ref(),
)
}
fn is_nm_ip_route_table_changed(
new_nm_sett: Option<&NmSettingIp>,
cur_nm_sett: Option<&NmSettingIp>,
) -> bool {
if let (Some(new_nm_sett), Some(cur_nm_sett)) = (new_nm_sett, cur_nm_sett) {
normalize_nm_route_table(new_nm_sett.route_table)
!= normalize_nm_route_table(cur_nm_sett.route_table)
} else {
false
}
}
fn normalize_nm_route_table(route_table: Option<u32>) -> u32 {
route_table.unwrap_or(NM_IP_SETTING_ROUTE_TABLE_DEFAULT)
}
fn nm_setting_is_route_removed(
new_nm_sett: Option<&NmSettingIp>,
cur_nm_sett: Option<&NmSettingIp>,
is_ipv6: bool,
) -> bool {
let new_routes = clone_normalized_routes(new_nm_sett, is_ipv6);
let cur_routes = clone_normalized_routes(cur_nm_sett, is_ipv6);
cur_routes
.iter()
.any(|cur_route| !new_routes.contains(cur_route))
}
fn clone_normalized_routes(
ip_sett: Option<&NmSettingIp>,
is_ipv6: bool,
) -> Vec<NmIpRoute> {
let default_table = ip_sett
.and_then(|ip| ip.route_table)
.filter(|tbl| *tbl != NM_IP_SETTING_ROUTE_TABLE_DEFAULT);
let mut default_metric = ip_sett
.and_then(|ip| ip.route_metric)
.filter(|mtr| *mtr != NM_IP_SETTING_ROUTE_METRIC_DEFAULT)
.map(|mtr| mtr as u32);
if is_ipv6 && default_metric == Some(0) {
default_metric = Some(IPV6_METRIC_COHERCED_DEFAULT);
}
let routes = ip_sett.map(|ip| ip.routes.as_slice()).unwrap_or(&[]);
routes
.iter()
.map(|rt| {
let mut new_rt = rt.clone();
new_rt.table = rt.table.or(default_table);
new_rt.metric = rt.metric.or(default_metric);
new_rt
})
.collect()
}
#[cfg(test)]
mod tests {
use super::{
NmConnection, NmSettingIp, is_route_table_changed,
normalize_nm_route_table,
};
#[test]
fn test_normalize_nm_route_table_treats_missing_as_default() {
assert_eq!(normalize_nm_route_table(None), 0);
assert_eq!(normalize_nm_route_table(Some(0)), 0);
assert_eq!(normalize_nm_route_table(Some(1000)), 1000);
}
#[test]
fn test_is_route_table_changed_ignores_default_representation() {
let mut new_nm_sett = NmSettingIp::default();
new_nm_sett.route_table = None;
let mut cur_nm_sett = NmSettingIp::default();
cur_nm_sett.route_table = Some(0);
let mut new_nm_conn = NmConnection::default();
new_nm_conn.ipv6 = Some(new_nm_sett);
let mut cur_nm_conn = NmConnection::default();
cur_nm_conn.ipv6 = Some(cur_nm_sett);
assert!(!is_route_table_changed(&new_nm_conn, &cur_nm_conn));
}
#[test]
fn test_is_route_table_changed_detects_table_id_change() {
let mut new_nm_sett = NmSettingIp::default();
new_nm_sett.route_table = Some(2000);
let mut cur_nm_sett = NmSettingIp::default();
cur_nm_sett.route_table = Some(1000);
let mut new_nm_conn = NmConnection::default();
new_nm_conn.ipv6 = Some(new_nm_sett);
let mut cur_nm_conn = NmConnection::default();
cur_nm_conn.ipv6 = Some(cur_nm_sett);
assert!(is_route_table_changed(&new_nm_conn, &cur_nm_conn));
}
#[test]
fn test_is_route_table_changed_detects_ipv4_table_id_change() {
let mut new_nm_sett = NmSettingIp::default();
new_nm_sett.route_table = Some(2000);
let mut cur_nm_sett = NmSettingIp::default();
cur_nm_sett.route_table = Some(1000);
let mut new_nm_conn = NmConnection::default();
new_nm_conn.ipv4 = Some(new_nm_sett);
let mut cur_nm_conn = NmConnection::default();
cur_nm_conn.ipv4 = Some(cur_nm_sett);
assert!(is_route_table_changed(&new_nm_conn, &cur_nm_conn));
}
}