1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// SPDX-License-Identifier: Apache-2.0
use crate::{ErrorKind, MergedRoutes, NmstateError, RouteEntry, Routes};
impl MergedRoutes {
fn routes_for_verify(&self) -> Vec<RouteEntry> {
let mut desired_routes = Vec::new();
if let Some(rts) = self.desired.config.as_ref() {
for rt in rts {
let mut rt = rt.clone();
rt.sanitize().ok();
desired_routes.push(rt);
}
}
desired_routes.sort_unstable();
desired_routes.dedup();
// Remove the absent route if matching normal route is also desired.
let mut new_desired_routes = Vec::new();
for rt in desired_routes.as_slice() {
if (!rt.is_absent())
|| desired_routes.as_slice().iter().any(|r| rt.is_match(r))
{
new_desired_routes.push(rt.clone());
}
}
new_desired_routes
}
// Kernel might append additional routes. For example, IPv6 default
// gateway will generate /128 static direct route.
// Hence, we only check:
// * desired absent route is removed unless another matching route been
// added.
// * desired static route exists.
pub(crate) fn verify(
&self,
current: &Routes,
ignored_ifaces: &[&str],
) -> Result<(), NmstateError> {
let mut cur_routes: Vec<&RouteEntry> = Vec::new();
if let Some(cur_rts) = current.config.as_ref() {
for cur_rt in cur_rts {
if let Some(via) = cur_rt.next_hop_iface.as_ref() {
if ignored_ifaces.contains(&via.as_str())
&& cur_rt.route_type.is_none()
{
continue;
}
}
cur_routes.push(cur_rt);
}
}
cur_routes.dedup();
let routes_for_verify = self.routes_for_verify();
for rt in routes_for_verify.as_slice() {
if rt.is_absent() {
// We do not valid absent route if desire has a match there.
// For example, user is changing a gateway.
if routes_for_verify
.as_slice()
.iter()
.any(|r| !r.is_absent() && rt.is_match(r))
{
continue;
}
if let Some(cur_rt) = cur_routes
.as_slice()
.iter()
.find(|cur_rt| rt.is_match(cur_rt))
{
return Err(NmstateError::new(
ErrorKind::VerificationError,
format!(
"Desired absent route {rt} still found \
after apply: {cur_rt}",
),
));
}
} else if rt.route_type.is_some() {
// In nispor, the IPv4 route with route type `Blackhole`,
// `Unreachable`, `Prohibit` does not have the route oif
// setting.
let mut route_type_rt = rt.clone();
if !route_type_rt.is_ipv6() {
route_type_rt.next_hop_iface = None;
}
if !cur_routes
.as_slice()
.iter()
.any(|cur_rt| route_type_rt.is_match(cur_rt))
{
return Err(NmstateError::new(
ErrorKind::VerificationError,
format!("Desired route {rt} not found after apply"),
));
}
} else if !cur_routes
.as_slice()
.iter()
.any(|cur_rt| rt.is_match(cur_rt))
{
return Err(NmstateError::new(
ErrorKind::VerificationError,
format!("Desired route {rt} not found after apply"),
));
}
}
Ok(())
}
}