@@ -143,6 +143,27 @@ impl VpcManifest {
143143 ..Default :: default ( )
144144 }
145145 }
146+ fn validate_expose_collisions ( & self ) -> ConfigResult {
147+ // Check that prefixes in each expose don't overlap with prefixes in other exposes
148+ for ( index, expose_left) in self . exposes . iter ( ) . enumerate ( ) {
149+ // Loop over the remaining exposes in the list
150+ for expose_right in self . exposes . iter ( ) . skip ( index + 1 ) {
151+ validate_overlapping (
152+ & expose_left. ips ,
153+ & expose_left. nots ,
154+ & expose_right. ips ,
155+ & expose_right. nots ,
156+ ) ?;
157+ validate_overlapping (
158+ & expose_left. as_range ,
159+ & expose_left. not_as ,
160+ & expose_right. as_range ,
161+ & expose_right. not_as ,
162+ ) ?;
163+ }
164+ }
165+ Ok ( ( ) )
166+ }
146167 pub fn add_expose ( & mut self , expose : VpcExpose ) -> ConfigResult {
147168 self . exposes . push ( expose) ;
148169 Ok ( ( ) )
@@ -157,6 +178,7 @@ impl VpcManifest {
157178 for expose in & self . exposes {
158179 expose. validate ( ) ?;
159180 }
181+ self . validate_expose_collisions ( ) ?;
160182 Ok ( ( ) )
161183 }
162184}
@@ -230,6 +252,125 @@ impl VpcPeeringTable {
230252 }
231253}
232254
255+ // Validate that two sets of prefixes, with their exclusion prefixes applied, don't overlap
256+ fn validate_overlapping (
257+ prefixes_left : & BTreeSet < Prefix > ,
258+ excludes_left : & BTreeSet < Prefix > ,
259+ prefixes_right : & BTreeSet < Prefix > ,
260+ excludes_right : & BTreeSet < Prefix > ,
261+ ) -> Result < ( ) , ConfigError > {
262+ // Find colliding prefixes
263+ let mut colliding = Vec :: new ( ) ;
264+ for prefix_left in prefixes_left. iter ( ) {
265+ for prefix_right in prefixes_right. iter ( ) {
266+ if prefix_left. covers ( prefix_right) || prefix_right. covers ( prefix_left) {
267+ colliding. push ( ( prefix_left. clone ( ) , prefix_right. clone ( ) ) ) ;
268+ }
269+ }
270+ }
271+ // If not prefixes collide, we're good - exit.
272+ if colliding. is_empty ( ) {
273+ return Ok ( ( ) ) ;
274+ }
275+
276+ // How do we determine whether there is a collision between the set of available addresses on
277+ // the left side, and the set of available addresses on the right side? A collision means:
278+ //
279+ // - Prefixes collide, in other words, they have a non-empty intersection (we've checked that
280+ // earlier)
281+ //
282+ // - This intersection is not fully covered by exclusion prefixes
283+ //
284+ // The idea in the loop below is that for each pair of colliding prefixes:
285+ //
286+ // - We retrieve the size of the intersection of the colliding prefixes. This is easy, because
287+ // they're "prefixes", so if they collide we have necessarily one that is contained within the
288+ // other, and the size of the intersection is the size of the smallest one.
289+ //
290+ // - We retrieve the size of the union of all the exclusion prefixes (from left and right sides)
291+ // covering part of this intersection (which we know is the smallest of the two colliding
292+ // prefixes). The union of the exclusion prefixes is the set of non-overlapping exclusion
293+ // prefixes that cover the intersection of allowed prefixes, such that if exclusion prefixes
294+ // collide, we always keep the largest prefix.
295+ //
296+ // - If the size of the intersection of colliding allowed prefixes is bigger than the size of
297+ // the union of the exclusion prefixes applying to them, then it needs that some addresses are
298+ // effectively allowed in both the left-side and the right-side set of available addresses,
299+ // and this is an error. If the sizes are identical, then all addresses in the intersection of
300+ // the prefixes are excluded on at least one side, so it's all good.
301+ for ( prefix_left, prefix_right) in colliding {
302+ // If prefixes collide, there's necessarily one prefix that is contained inside of the
303+ // other. Find the intersection of the two colliding prefixes, which is the smallest of the
304+ // two prefixes.
305+ let intersection_prefix = if prefix_left. covers ( & prefix_right) {
306+ & prefix_right
307+ } else {
308+ & prefix_left
309+ } ;
310+
311+ // Retrieve the union of all exclusion prefixes covering the intersection of the colliding
312+ // prefixes
313+ let mut union_excludes = BTreeSet :: new ( ) ;
314+
315+ // Consider exclusion prefixes from excludes_left
316+ ' outer: for exclude_left in excludes_left. iter ( ) . filter ( |exclude| {
317+ exclude. covers ( intersection_prefix) || intersection_prefix. covers ( exclude)
318+ } ) {
319+ for exclude_right in excludes_right. iter ( ) . filter ( |exclude| {
320+ exclude. covers ( intersection_prefix) || intersection_prefix. covers ( exclude)
321+ } ) {
322+ if exclude_left. covers ( exclude_right) {
323+ // exclude_left contains exclude_right, and given that exclusion prefixes in
324+ // list excludes_right don't overlap there's no exclusion prefix containing
325+ // exclude_left. We want to keep exclude_left as part of the union.
326+ union_excludes. insert ( exclude_left. clone ( ) ) ;
327+ continue ' outer;
328+ } else if exclude_right. covers ( exclude_left) {
329+ // exclude_left is contained within exclude_right, don't keep it as part of the
330+ // union. Process next exclusion prefix from list excludes_left.
331+ continue ' outer;
332+ }
333+ }
334+ // No collision for this exclude_left, add it to the union
335+ union_excludes. insert ( exclude_left. clone ( ) ) ;
336+ }
337+ // Consider exclusion prefixes from excludes_right
338+ ' outer: for exclude_right in excludes_right. iter ( ) . filter ( |exclude| {
339+ exclude. covers ( intersection_prefix) || intersection_prefix. covers ( exclude)
340+ } ) {
341+ for exclude_left in excludes_left. iter ( ) . filter ( |exclude| {
342+ exclude. covers ( intersection_prefix) || intersection_prefix. covers ( exclude)
343+ } ) {
344+ if exclude_right. covers ( exclude_left) {
345+ // exclude_right contains exclude_left, and given that exclusions prefixes in
346+ // list excludes_left don't overlap there's no exclusion prefix containing
347+ // exclude_right. We want to keep exclude_right as part of the union.
348+ union_excludes. insert ( exclude_right. clone ( ) ) ;
349+ continue ' outer;
350+ } else if exclude_left. covers ( exclude_right) {
351+ // exclude_right is contained within exclude_left, don't keep it as part of the
352+ // union. Process next exclusion prefix from list excludes_right.
353+ continue ' outer;
354+ }
355+ }
356+ // No collision for this exclude_right, add it to the union
357+ union_excludes. insert ( exclude_right. clone ( ) ) ;
358+ }
359+
360+ let union_size = union_excludes
361+ . iter ( )
362+ . map ( |exclude| exclude. size ( ) )
363+ . sum :: < u128 > ( ) ;
364+ if union_size < intersection_prefix. size ( ) {
365+ // Some addresses at the intersection of both prefixes are not covered by the union of
366+ // all exclusion prefixes, in other words, they are available from both prefixes. This
367+ // is an error.
368+ return Err ( ConfigError :: OverlappingPrefixes ( prefix_left, prefix_right) ) ;
369+ }
370+ }
371+ Ok ( ( ) )
372+ }
373+
233374#[ cfg( test) ]
234375mod tests {
235376 use super :: * ;
0 commit comments