Let's consider the following peering (simplified syntax):
peerings:
vpc1-to-vpc2:
vpc1:
ips: 10.0.2.0/24
vpc2:
ips: 20.0.0.0/24
vpc1-to-vpc3:
vpc1:
ips:
- 10.0.2.2/32
- 10.0.2.3/32 # <- contiguous with 10.0.2.2/32, covered by 10.0.2.0/24
vpc3:
ips: 30.0.0.0/24
In the old flow-filter table we would end up with rules for both 10.0.2.2/31, and 10.0.2.2/32+10.0.2.3/32. Why? The bug is in the function that processes these prefixes for building the flow-filter context for vpc1. We call add_peering(), which in turns calls get_prefixes_for_processing(), which contains something like this:
// Determine overlapping prefixes accross peerings
let (local_manifests_overlap, remote_manifests_overlap) =
get_manifests_overlap(overlay, vpc, peering, dst_vpcd, skip_ports);
// Merge overlapping prefixes, when possible
let overlap_trie = consolidate_overlap_list(local_manifests_overlap);
// Now generate list of non-overlapping prefixes
let local_prefixes = get_split_prefixes_for_manifest(
&peering.local,
&dst_vpcd,
|expose| &expose.ips,
overlap_trie,
skip_ports,
);
In other words:
- Get the list of all overlapping prefixes exposed to vpc1. In our example, overlapping prefixes are 10.0.2.2/32 and 10.0.2.3/32.
- Merge these prefixes together if possible, as an optimisation for the follow-up step: the
overlap_trie list becomes 10.0.2.2/31, which makes sense.
- We compute the list of prefixes to add to the context table: for every exposed prefix.
Step 3 compares to entries in the set of overlapping prefixes, and split if necessary; then add split prefixes to the list. This is relevant when building the context for vpc2, because 10.0.2.0/24 needs to be split into smaller subprefixes, but not for the context in vpc1.
What matters, however, is that function get_split_prefixes_for_manifest() does not do any merging of the exposed prefixes. So 10.0.2.2/32 and 10.0.2.3/32 are compared with the entries in overlap_trie, 10.0.2.2/31, and this leads to the creation of the entries for destination vpc3; but 10.0.2.2/32 and 10.0.2.3/32 are never merged together before creating this entry (only overlap_trie entries were merged, and they don't get added straight away to the list of processed prefixes), they're added directly to the list of processed prefixes.
By contrast, vpc3's prefix 10.0.2.0/24 gets split against 10.0.2.2/31, and this produces 10.0.2.2/31 (with multiple matches), and 10.0.2.0/31, 10.0.2.4/30, 10.0.2.8/29, ..., 10.0.2.128/25 (all single match) in the list of processed prefixes. That's where the 10.0.2.2/31 rule get added.
Workarounds:
- Instead of writing 10.0.2.2/32 and 10.0.2.3/32 in the config, use 10.0.2.2/31. This works because then the prefixes are merged and there's no discrepancy.
- Remove the merge step:
overlap_trie gets bigger, resulting in more prefixes in the end (here, context for vpc3 will contain 10.0.2.2/32 and 10.0.2.3/32 as well instead of just 10.0.2.2/31).
- Make sure everything is merged: this is the opposite way as 2). Merge prefixes when possible, either when validating the configuration (preferably) or when building the configuration.
In the end we didn't fix, and moved to a new implementation with
#1638
Note that we can also reproduce with the following setup:
peerings:
vpc1-to-vpc2:
vpc1:
ips: 10.0.2.0/24
vpc2:
ips: 20.0.0.0/24
vpc1-to-vpc3:
vpc1:
ips:
- 10.0.2.4/31
- 10.0.2.6/31
vpc3:
ips: 30.0.0.0/24
No /32, but consecutive /31s in the config. In the flow-filter context table:
source: 10.0.2.4/30
destination: 20.0.1.0/24, data:
destination VPC: VNI(200), source NAT: -, destination NAT: -
no remote default
source: 10.0.2.4/31
destination: 40.0.1.0/24, data:
destination VPC: VNI(400), source NAT: -, destination NAT: -
no remote default
source: 10.0.2.6/31
destination: 40.0.1.0/24, data:
destination VPC: VNI(400), source NAT: -, destination NAT: -
no remote default
So the issue happens when
- Two VPCs
b and c expose overlapping prefixes to one given VPC a
- VPC
b exposes mergeable (same-length, consecutive, and aligned) prefixes (10.0.2.2/32 and 10.0.2.3/32 work; 10.0.2.3/32 and 10.0.2.4/32 aren't in the same parent CIDR)
- VPC
c's exposed prefix completely overlaps both consecutive prefixes exposed from b
Let's consider the following peering (simplified syntax):
In the old flow-filter table we would end up with rules for both 10.0.2.2/31, and 10.0.2.2/32+10.0.2.3/32. Why? The bug is in the function that processes these prefixes for building the flow-filter context for vpc1. We call
add_peering(), which in turns callsget_prefixes_for_processing(), which contains something like this:In other words:
overlap_trielist becomes 10.0.2.2/31, which makes sense.Step 3 compares to entries in the set of overlapping prefixes, and split if necessary; then add split prefixes to the list. This is relevant when building the context for vpc2, because 10.0.2.0/24 needs to be split into smaller subprefixes, but not for the context in vpc1.
What matters, however, is that function
get_split_prefixes_for_manifest()does not do any merging of the exposed prefixes. So 10.0.2.2/32 and 10.0.2.3/32 are compared with the entries inoverlap_trie, 10.0.2.2/31, and this leads to the creation of the entries for destination vpc3; but 10.0.2.2/32 and 10.0.2.3/32 are never merged together before creating this entry (onlyoverlap_trieentries were merged, and they don't get added straight away to the list of processed prefixes), they're added directly to the list of processed prefixes.By contrast, vpc3's prefix 10.0.2.0/24 gets split against 10.0.2.2/31, and this produces 10.0.2.2/31 (with multiple matches), and 10.0.2.0/31, 10.0.2.4/30, 10.0.2.8/29, ..., 10.0.2.128/25 (all single match) in the list of processed prefixes. That's where the 10.0.2.2/31 rule get added.
Workarounds:
overlap_triegets bigger, resulting in more prefixes in the end (here, context for vpc3 will contain 10.0.2.2/32 and 10.0.2.3/32 as well instead of just 10.0.2.2/31).In the end we didn't fix, and moved to a new implementation with
#1638
Note that we can also reproduce with the following setup:
No /32, but consecutive /31s in the config. In the flow-filter context table:
So the issue happens when
bandcexpose overlapping prefixes to one given VPCabexposes mergeable (same-length, consecutive, and aligned) prefixes (10.0.2.2/32 and 10.0.2.3/32 work; 10.0.2.3/32 and 10.0.2.4/32 aren't in the same parent CIDR)c's exposed prefix completely overlaps both consecutive prefixes exposed fromb