97 lines
No EOL
3 KiB
TypeScript
97 lines
No EOL
3 KiB
TypeScript
interface IPRange {
|
|
start: bigint;
|
|
end: bigint;
|
|
}
|
|
|
|
/**
|
|
* Converts IP address string to BigInt for numerical operations
|
|
*/
|
|
function ipToBigInt(ip: string): bigint {
|
|
return ip.split('.')
|
|
.reduce((acc, octet) => BigInt.asUintN(64, (acc << BigInt(8)) + BigInt(parseInt(octet))), BigInt(0));
|
|
}
|
|
|
|
/**
|
|
* Converts BigInt to IP address string
|
|
*/
|
|
function bigIntToIp(num: bigint): string {
|
|
const octets: number[] = [];
|
|
for (let i = 0; i < 4; i++) {
|
|
octets.unshift(Number(num & BigInt(255)));
|
|
num = num >> BigInt(8);
|
|
}
|
|
return octets.join('.');
|
|
}
|
|
|
|
/**
|
|
* Converts CIDR to IP range
|
|
*/
|
|
function cidrToRange(cidr: string): IPRange {
|
|
const [ip, prefix] = cidr.split('/');
|
|
const prefixBits = parseInt(prefix);
|
|
const ipBigInt = ipToBigInt(ip);
|
|
const mask = BigInt.asUintN(64, (BigInt(1) << BigInt(32 - prefixBits)) - BigInt(1));
|
|
const start = ipBigInt & ~mask;
|
|
const end = start | mask;
|
|
return { start, end };
|
|
}
|
|
|
|
/**
|
|
* Finds the next available CIDR block given existing allocations
|
|
* @param existingCidrs Array of existing CIDR blocks
|
|
* @param blockSize Desired prefix length for the new block (e.g., 24 for /24)
|
|
* @param startCidr Optional CIDR to start searching from (default: "0.0.0.0/0")
|
|
* @returns Next available CIDR block or null if none found
|
|
*/
|
|
export function findNextAvailableCidr(
|
|
existingCidrs: string[],
|
|
blockSize: number,
|
|
startCidr: string = "0.0.0.0/0"
|
|
): string | null {
|
|
// 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 blockSizeBigInt = BigInt(1) << BigInt(32 - blockSize);
|
|
|
|
// Start from the beginning of the given CIDR
|
|
let current = cidrToRange(startCidr).start;
|
|
const maxIp = cidrToRange(startCidr).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)}/${blockSize}`;
|
|
}
|
|
|
|
// Move current pointer to after the current range
|
|
current = nextRange.end + BigInt(1);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks if a given IP address is within a CIDR range
|
|
* @param ip IP address to check
|
|
* @param cidr CIDR range to check against
|
|
* @returns boolean indicating if IP is within the CIDR range
|
|
*/
|
|
export function isIpInCidr(ip: string, cidr: string): boolean {
|
|
const ipBigInt = ipToBigInt(ip);
|
|
const range = cidrToRange(cidr);
|
|
return ipBigInt >= range.start && ipBigInt <= range.end;
|
|
} |