v9.1.1
Menu

Detect Overlapping VPC CIDR Blocks

mediumFREENetworking & AddressingSystems & InfrastructureML Systems & Infrastructure 1

Problem Statement

Two VPCs whose CIDR ranges overlap cannot be peered, and a subnet that overlaps its own VPC's other subnets will be rejected outright. Given the address plan for a cluster, find every colliding pair before Terraform does.

Background

Every CIDR block is a contiguous, power-of-two-sized range of 32-bit integers. Convert each block to [low, high]:

size    = 2 ** (32 - prefix)
network = ip - (ip % size)        # clear the host bits
low, high = network, network + size - 1

Two ranges overlap when low_i <= high_j and low_j <= high_i. That single test covers all three cases that matter: partial overlap, one block fully containing another (10.0.0.0/16 and 10.0.1.0/24), and two identical blocks.

Comparing the strings, or only comparing prefixes, misses containment — which is the case that actually shows up in a VPC plan.

Note the input may carry host bits (10.0.5.7/24 means the block 10.0.5.0/24), so normalise before comparing.

Your Task

Implement:

def find_overlaps(cidrs):

Return a list of [cidrs[i], cidrs[j]] pairs with i < j, in increasing order of i then j, for every overlapping pair. Return [] when the plan is clean.

Input Format

  • cidrs: list of CIDR strings.

Output Format

  • A list of two-element lists of the original CIDR strings.

Sample

print(find_overlaps(["10.0.0.0/16", "10.0.1.0/24", "10.1.0.0/16", "192.168.0.0/16"]))

Output:

[['10.0.0.0/16', '10.0.1.0/24']]

10.0.1.0/24 sits entirely inside 10.0.0.0/16, so the pair collides; the other two blocks are disjoint from everything.

Example

Input
print(find_overlaps(["10.0.0.0/16", "10.0.1.0/24", "10.1.0.0/16", "192.168.0.0/16"]))
Output
[['10.0.0.0/16', '10.0.1.0/24']]
Reasoning
  • Input: four CIDR strings. Each is a dotted address plus a prefix length pp, naming a block of 232−p2^{32-p} consecutive 32-bit addresses: 216=655362^{16} = 65536 for a /16, 28=2562^{8} = 256 for a /24.
  • Read as one 32-bit number, 10.0.0.0 is 10⋅224=16777216010 \cdot 2^{24} = 167772160, so 10.0.0.0/16 spans [167772160, 167837695][167772160,\ 167837695]. 10.0.1.0/24 starts 256256 addresses later and spans [167772416, 167772671][167772416,\ 167772671].
  • Overlap test for this pair: 167772160≤167772671167772160 \le 167772671 and 167772416≤167837695167772416 \le 167837695. Both hold, so they collide; the /24 sits wholly inside the /16.
  • 10.1.0.0/16 spans [167837696, 167903231][167837696,\ 167903231]. Its low end is exactly one above the first block's high end 167837695167837695, so the two touch but share no address. It also clears the /24, since 167772671<167837696167772671 < 167837696. 192.168.0.0/16 spans [3232235520, 3232301055][3232235520,\ 3232301055], far above everything.
  • Output: a single pair, earlier entry first: [['10.0.0.0/16', '10.0.1.0/24']]. Notice that adjacent blocks are not an overlap, and the pair holds the original strings, not the normalised ranges.

Constraints

  • 0 <= len(cidrs) <= 200
  • Each entry is a valid IPv4 CIDR, prefix length 0 to 32
  • Inputs may have host bits set and must be normalised to the network address first
  • Containment counts as an overlap, as do two identical blocks
  • Pairs are returned as [cidrs[i], cidrs[j]] with i < j, ordered by i then j

Learn this in

More from ML Systems & Infrastructure 1

solution.py

Test results

Sign in to run your code against the test cases.