From 4b6985718a1f7afeb622a842872f80097c3f2972 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 27 Mar 2025 22:13:39 -0400 Subject: [PATCH] Fix ip picking from subnet in exclusion --- server/lib/ip.test.ts | 15 ++++++++++++++- server/lib/ip.ts | 32 +++++++++++++++++++------------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/server/lib/ip.test.ts b/server/lib/ip.test.ts index 2c2dd057..67a2faaa 100644 --- a/server/lib/ip.test.ts +++ b/server/lib/ip.test.ts @@ -4,7 +4,14 @@ import { assertEquals } from "@test/assert"; // Test cases function testFindNextAvailableCidr() { console.log("Running findNextAvailableCidr tests..."); - + + // Test 0: Basic IPv4 allocation with a subnet in the wrong range + { + const existing = ["100.90.130.1/30", "100.90.128.4/30"]; + const result = findNextAvailableCidr(existing, 30, "100.90.130.1/24"); + assertEquals(result, "100.90.130.4/30", "Basic IPv4 allocation failed"); + } + // Test 1: Basic IPv4 allocation { const existing = ["10.0.0.0/16", "10.1.0.0/16"]; @@ -26,6 +33,12 @@ function testFindNextAvailableCidr() { assertEquals(result, null, "No available space test failed"); } + // Test 4: Empty existing + { + const existing: string[] = []; + const result = findNextAvailableCidr(existing, 30, "10.0.0.0/8"); + assertEquals(result, "10.0.0.0/30", "Empty existing test failed"); + } // // Test 4: IPv6 allocation // { // const existing = ["2001:db8::/32", "2001:db8:1::/32"]; diff --git a/server/lib/ip.ts b/server/lib/ip.ts index 86fe1169..a4f05126 100644 --- a/server/lib/ip.ts +++ b/server/lib/ip.ts @@ -132,7 +132,6 @@ export function findNextAvailableCidr( blockSize: number, startCidr?: string ): string | null { - if (!startCidr && existingCidrs.length === 0) { return null; } @@ -150,40 +149,47 @@ export function findNextAvailableCidr( existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) { throw new Error('All CIDRs must be of the same IP version'); } - + + // Extract the network part from startCidr to ensure we stay in the right subnet + const startCidrRange = cidrToRange(startCidr); + // Convert existing CIDRs to ranges and sort them const existingRanges = existingCidrs .map(cidr => cidrToRange(cidr)) .sort((a, b) => (a.start < b.start ? -1 : 1)); - + // Calculate block size const maxPrefix = version === 4 ? 32 : 128; const blockSizeBigInt = BigInt(1) << BigInt(maxPrefix - blockSize); - + // Start from the beginning of the given CIDR - let current = cidrToRange(startCidr).start; - const maxIp = cidrToRange(startCidr).end; - + let current = startCidrRange.start; + const maxIp = startCidrRange.end; + // Iterate through existing ranges for (let i = 0; i <= existingRanges.length; i++) { const nextRange = existingRanges[i]; + // Align current to block size const alignedCurrent = current + ((blockSizeBigInt - (current % blockSizeBigInt)) % blockSizeBigInt); - + // Check if we've gone beyond the maximum allowed IP if (alignedCurrent + blockSizeBigInt - BigInt(1) > maxIp) { return null; } - + // If we're at the end of existing ranges or found a gap if (!nextRange || alignedCurrent + blockSizeBigInt - BigInt(1) < nextRange.start) { return `${bigIntToIp(alignedCurrent, version)}/${blockSize}`; } - - // Move current pointer to after the current range - current = nextRange.end + BigInt(1); + + // If next range overlaps with our search space, move past it + if (nextRange.end >= startCidrRange.start && nextRange.start <= maxIp) { + // Move current pointer to after the current range + current = nextRange.end + BigInt(1); + } } - + return null; }