ip.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. interface IPRange {
  2. start: bigint;
  3. end: bigint;
  4. }
  5. /**
  6. * Converts IP address string to BigInt for numerical operations
  7. */
  8. function ipToBigInt(ip: string): bigint {
  9. return ip.split('.')
  10. .reduce((acc, octet) => BigInt.asUintN(64, (acc << BigInt(8)) + BigInt(parseInt(octet))), BigInt(0));
  11. }
  12. /**
  13. * Converts BigInt to IP address string
  14. */
  15. function bigIntToIp(num: bigint): string {
  16. const octets: number[] = [];
  17. for (let i = 0; i < 4; i++) {
  18. octets.unshift(Number(num & BigInt(255)));
  19. num = num >> BigInt(8);
  20. }
  21. return octets.join('.');
  22. }
  23. /**
  24. * Converts CIDR to IP range
  25. */
  26. function cidrToRange(cidr: string): IPRange {
  27. const [ip, prefix] = cidr.split('/');
  28. const prefixBits = parseInt(prefix);
  29. const ipBigInt = ipToBigInt(ip);
  30. const mask = BigInt.asUintN(64, (BigInt(1) << BigInt(32 - prefixBits)) - BigInt(1));
  31. const start = ipBigInt & ~mask;
  32. const end = start | mask;
  33. return { start, end };
  34. }
  35. /**
  36. * Finds the next available CIDR block given existing allocations
  37. * @param existingCidrs Array of existing CIDR blocks
  38. * @param blockSize Desired prefix length for the new block (e.g., 24 for /24)
  39. * @param startCidr Optional CIDR to start searching from (default: "0.0.0.0/0")
  40. * @returns Next available CIDR block or null if none found
  41. */
  42. export function findNextAvailableCidr(
  43. existingCidrs: string[],
  44. blockSize: number,
  45. startCidr: string = "0.0.0.0/0"
  46. ): string | null {
  47. // Convert existing CIDRs to ranges and sort them
  48. const existingRanges = existingCidrs
  49. .map(cidr => cidrToRange(cidr))
  50. .sort((a, b) => (a.start < b.start ? -1 : 1));
  51. // Calculate block size
  52. const blockSizeBigInt = BigInt(1) << BigInt(32 - blockSize);
  53. // Start from the beginning of the given CIDR
  54. let current = cidrToRange(startCidr).start;
  55. const maxIp = cidrToRange(startCidr).end;
  56. // Iterate through existing ranges
  57. for (let i = 0; i <= existingRanges.length; i++) {
  58. const nextRange = existingRanges[i];
  59. // Align current to block size
  60. const alignedCurrent = current + ((blockSizeBigInt - (current % blockSizeBigInt)) % blockSizeBigInt);
  61. // Check if we've gone beyond the maximum allowed IP
  62. if (alignedCurrent + blockSizeBigInt - BigInt(1) > maxIp) {
  63. return null;
  64. }
  65. // If we're at the end of existing ranges or found a gap
  66. if (!nextRange || alignedCurrent + blockSizeBigInt - BigInt(1) < nextRange.start) {
  67. return `${bigIntToIp(alignedCurrent)}/${blockSize}`;
  68. }
  69. // Move current pointer to after the current range
  70. current = nextRange.end + BigInt(1);
  71. }
  72. return null;
  73. }
  74. /**
  75. * Checks if a given IP address is within a CIDR range
  76. * @param ip IP address to check
  77. * @param cidr CIDR range to check against
  78. * @returns boolean indicating if IP is within the CIDR range
  79. */
  80. export function isIpInCidr(ip: string, cidr: string): boolean {
  81. const ipBigInt = ipToBigInt(ip);
  82. const range = cidrToRange(cidr);
  83. return ipBigInt >= range.start && ipBigInt <= range.end;
  84. }