Skip to content

Commit db6bb9f

Browse files
committed
feat(vpcpeering): Introduce validation for VpcPeeringTable
In previous commits, we introduced validation for VpcExpose and VpcManifest objects; now, we need the same for a VpcPeeringTable. We had checks for overlap between prefixes of a same VpcExpose, or between prefixes of different VpcExpose within a VpcManifest object. It turns out we also need to detect overlap between the prefixes of the exposes from the manifests for a peering related to a given VPC and the existing exposes from manifests from all other peerings in the VpcPeeringTable object, when they relate to the same VPC. To detect overlap, we reuse the dedicated function introduced in a previous commit. On the tests side: - Add dedicated unit tests, and call the validation function. - Fix some tests that would incorrectly reuse VpcExpose objects for multiple peerings about the same VPC. Signed-off-by: Quentin Monnet <[email protected]>
1 parent c7f282b commit db6bb9f

File tree

3 files changed

+99
-14
lines changed

3 files changed

+99
-14
lines changed

mgmt/src/models/external/overlay/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ impl Overlay {
3939
}
4040
pub fn validate(&mut self) -> ConfigResult {
4141
debug!("Validating overlay configuration...");
42+
43+
self.peering_table.validate()?;
44+
4245
/* check if the VPCs referred in a peering exist */
4346
for peering in self.peering_table.values() {
4447
self.check_peering_vpc(&peering.name, &peering.left)?;

mgmt/src/models/external/overlay/tests.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,29 +216,35 @@ pub mod test {
216216
}
217217
fn man_vpc1_with_vpc4() -> VpcManifest {
218218
let mut m1 = VpcManifest::new("VPC-1");
219-
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.60.0", 24)));
219+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.70.0", 24)));
220220
m1.add_expose(expose).expect("Should succeed");
221221
m1
222222
}
223-
fn man_vpc2() -> VpcManifest {
223+
fn man_vpc2_with_vpc1() -> VpcManifest {
224224
let mut m1 = VpcManifest::new("VPC-2");
225225
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.80.0", 24)));
226226
m1.add_expose(expose).expect("Should succeed");
227227
m1
228228
}
229229
fn man_vpc2_with_vpc3() -> VpcManifest {
230230
let mut m1 = VpcManifest::new("VPC-2");
231-
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.80.0", 24)));
231+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.90.0", 24)));
232232
m1.add_expose(expose).expect("Should succeed");
233233
m1
234234
}
235-
fn man_vpc3() -> VpcManifest {
235+
fn man_vpc3_with_vpc1() -> VpcManifest {
236236
let mut m1 = VpcManifest::new("VPC-3");
237237
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.128.0", 27)));
238238
m1.add_expose(expose).expect("Should succeed");
239239
m1
240240
}
241-
fn man_vpc4() -> VpcManifest {
241+
fn man_vpc3_with_vpc2() -> VpcManifest {
242+
let mut m1 = VpcManifest::new("VPC-3");
243+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.128.32", 27)));
244+
m1.add_expose(expose).expect("Should succeed");
245+
m1
246+
}
247+
fn man_vpc4_with_vpc1() -> VpcManifest {
242248
let mut m1 = VpcManifest::new("VPC-4");
243249
let expose = VpcExpose::empty()
244250
.ip(Prefix::from(("192.168.201.1", 32)))
@@ -272,31 +278,31 @@ pub mod test {
272278
.add(VpcPeering::new(
273279
"VPC-1--VPC-2",
274280
man_vpc1_with_vpc2(),
275-
man_vpc2(),
281+
man_vpc2_with_vpc1(),
276282
))
277283
.expect("Should succeed");
278284

279285
peering_table
280286
.add(VpcPeering::new(
281287
"VPC-1--VPC-3",
282288
man_vpc1_with_vpc3(),
283-
man_vpc3(),
289+
man_vpc3_with_vpc1(),
284290
))
285291
.expect("Should succeed");
286292

287293
peering_table
288294
.add(VpcPeering::new(
289295
"VPC-1--VPC-4",
290296
man_vpc1_with_vpc4(),
291-
man_vpc4(),
297+
man_vpc4_with_vpc1(),
292298
))
293299
.expect("Should succeed");
294300

295301
peering_table
296302
.add(VpcPeering::new(
297303
"VPC-2--VPC-3",
298304
man_vpc2_with_vpc3(),
299-
man_vpc3(),
305+
man_vpc3_with_vpc2(),
300306
))
301307
.expect("Should succeed");
302308

mgmt/src/models/external/overlay/vpcpeering.rs

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use routing::prefix::Prefix;
77
use std::collections::BTreeMap;
88
use std::collections::BTreeSet;
9+
use std::collections::HashSet;
910
use std::ops::Bound::{Excluded, Unbounded};
1011

1112
use crate::models::external::{ConfigError, ConfigResult};
@@ -233,8 +234,6 @@ impl VpcPeeringTable {
233234

234235
/// Add a [`VpcPeering`] to a [`VpcPeeringTable`]
235236
pub fn add(&mut self, peering: VpcPeering) -> ConfigResult {
236-
peering.validate()?;
237-
238237
// First look for an existing entry, to avoid inserting a duplicate peering
239238
if self.0.contains_key(&peering.name) {
240239
return Err(ConfigError::DuplicateVpcPeeringId(peering.name.clone()));
@@ -258,6 +257,60 @@ impl VpcPeeringTable {
258257
.values()
259258
.filter(move |p| p.left.name == vpc || p.right.name == vpc)
260259
}
260+
261+
fn vpcs(&self) -> HashSet<&String> {
262+
let mut vpcs = HashSet::new();
263+
self.0.values().for_each(|p| {
264+
vpcs.insert(&p.left.name);
265+
vpcs.insert(&p.right.name);
266+
});
267+
vpcs
268+
}
269+
fn validate_peering_collisions(&self) -> ConfigResult {
270+
// For each VPC, check that exposes in manifests for this VPC in the different peerings do
271+
// not collide with the exposes in manifests for the same VPC in other peerings from the
272+
// table.
273+
274+
// Loop on VPCs covered by the peerings from the table
275+
for vpc in self.vpcs() {
276+
// Loop on peerings for this VPC
277+
for (index, peering) in self.peerings_vpc(vpc).enumerate() {
278+
// Only process the manifest related to the current VPC
279+
let (manifest_left, _) = peering.get_peering_manifests(vpc);
280+
// Compare with remaining peerings for this VPC
281+
for other_peering in self.peerings_vpc(vpc).skip(index + 1) {
282+
// Only process the manifest related to the current VPC
283+
let (manifest_right, _) = other_peering.get_peering_manifests(vpc);
284+
// Compare each expose from one manifest with all exposes from the other
285+
// manifest
286+
for expose_left in &manifest_left.exposes {
287+
for expose_right in &manifest_right.exposes {
288+
validate_overlapping(
289+
&expose_left.ips,
290+
&expose_left.nots,
291+
&expose_right.ips,
292+
&expose_right.nots,
293+
)?;
294+
validate_overlapping(
295+
&expose_left.as_range,
296+
&expose_left.not_as,
297+
&expose_right.as_range,
298+
&expose_right.not_as,
299+
)?;
300+
}
301+
}
302+
}
303+
}
304+
}
305+
Ok(())
306+
}
307+
pub fn validate(&self) -> ConfigResult {
308+
for peering in self.0.values() {
309+
peering.validate()?;
310+
}
311+
self.validate_peering_collisions()?;
312+
Ok(())
313+
}
261314
}
262315

263316
// Validate that two sets of prefixes, with their exclusion prefixes applied, don't overlap
@@ -631,9 +684,29 @@ mod tests {
631684
.as_range(prefix_v4("192.168.8.0/24")),
632685
)
633686
.expect("Failed to add expose");
687+
let mut manifest_empty = VpcManifest::new("VPC-empty");
688+
manifest_empty
689+
.add_expose(VpcExpose::empty())
690+
.expect("Failed to add expose");
691+
634692
let mut table = VpcPeeringTable::new();
635-
let peering = VpcPeering::new("test_peering1", manifest1.clone(), manifest2.clone());
636-
assert_eq!(table.add(peering.clone()), Ok(()));
693+
let peering = VpcPeering::new("test_peering1", manifest1.clone(), manifest_empty.clone());
694+
table.add(peering.clone()).expect("Failed to add peering");
695+
assert_eq!(table.validate(), Ok(()));
696+
assert_eq!(table.len(), 1);
697+
698+
// Incorrect: Overlapping prefixes
699+
let mut table_clone = table.clone();
700+
let peering2 = VpcPeering::new("test_peering2", manifest1.clone(), manifest2.clone());
701+
table_clone.add(peering2).expect("Failed to add peering");
702+
assert_eq!(table_clone.len(), 2);
703+
assert_eq!(
704+
table_clone.validate(),
705+
Err(ConfigError::OverlappingPrefixes(
706+
prefix_v4("10.0.0.0/16"),
707+
prefix_v4("10.0.0.0/16")
708+
))
709+
);
637710
assert_eq!(table.len(), 1);
638711

639712
// Incorrect: Duplicate peering name
@@ -661,9 +734,12 @@ mod tests {
661734
"test_peering1".to_string()
662735
))
663736
);
737+
assert_eq!(table.validate(), Ok(()));
738+
assert_eq!(table.len(), 1);
664739

665740
let peering4 = VpcPeering::new("test_peering4", manifest3.clone(), manifest4.clone());
666-
assert_eq!(table.add(peering4), Ok(()));
741+
table.add(peering4).expect("Failed to add peering");
742+
assert_eq!(table.validate(), Ok(()));
667743
assert_eq!(table.len(), 2);
668744
}
669745
}

0 commit comments

Comments
 (0)