calculate-subnets
Advanced tools
Comparing version 1.1.0 to 2.0.0
export interface UniformlyDistributedSubnetsArguments { | ||
neededBlocks: number; | ||
neededSubnets: number; | ||
cidr: string; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -6,9 +6,8 @@ import { Cidr } from 'cidr-calc'; | ||
*/ | ||
export declare function uniformlyDistributedSubnets({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
cidrBlocks: Cidr[]; | ||
cidrNumber: number; | ||
maxIpsPerBlock: number; | ||
availableSpace: number; | ||
export declare function uniformlyDistributedSubnets({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
subnetCidrs: Cidr[]; | ||
optimalSubnetCidrPrefixLength: number; | ||
maxIpsPerSubnet: number; | ||
parentCidr: Cidr; | ||
}; | ||
//# sourceMappingURL=uniformly-distributed-subnets.d.ts.map |
import { Cidr } from 'cidr-calc'; | ||
import { UniformlyDistributedSubnetsArguments } from './types.js'; | ||
export declare function validate({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
export declare function validate({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
parentCidr: Cidr; | ||
availableSpace: number; | ||
}; | ||
//# sourceMappingURL=validate.d.ts.map |
export interface UniformlyDistributedSubnetsArguments { | ||
neededBlocks: number; | ||
neededSubnets: number; | ||
cidr: string; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -6,9 +6,8 @@ import { Cidr } from 'cidr-calc'; | ||
*/ | ||
export declare function uniformlyDistributedSubnets({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
cidrBlocks: Cidr[]; | ||
cidrNumber: number; | ||
maxIpsPerBlock: number; | ||
availableSpace: number; | ||
export declare function uniformlyDistributedSubnets({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
subnetCidrs: Cidr[]; | ||
optimalSubnetCidrPrefixLength: number; | ||
maxIpsPerSubnet: number; | ||
parentCidr: Cidr; | ||
}; | ||
//# sourceMappingURL=uniformly-distributed-subnets.d.ts.map |
import { Cidr } from 'cidr-calc'; | ||
import { UniformlyDistributedSubnetsArguments } from './types.js'; | ||
export declare function validate({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
export declare function validate({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
parentCidr: Cidr; | ||
availableSpace: number; | ||
}; | ||
//# sourceMappingURL=validate.d.ts.map |
import { Cidr, IpAddress } from 'cidr-calc'; | ||
import assert from 'node:assert'; | ||
function validate({ neededBlocks, cidr, }) { | ||
function validate({ neededSubnets, cidr, }) { | ||
let parentCidr; | ||
@@ -13,3 +13,3 @@ let availableSpace; | ||
parentCidr = new Cidr(IpAddress.of(ipRaw), Number(cidrNumberRaw)); | ||
availableSpace = 32 - Number(cidrNumberRaw); | ||
availableSpace = 32 - parentCidr.prefixLen; | ||
} | ||
@@ -20,3 +20,3 @@ catch { | ||
if (typeof availableSpace !== 'number' || | ||
Number.isNaN(neededBlocks) || | ||
Number.isNaN(availableSpace) || | ||
!Number.isInteger(availableSpace) || | ||
@@ -27,12 +27,13 @@ availableSpace < 0 || | ||
} | ||
if (typeof neededBlocks !== 'number' || | ||
Number.isNaN(neededBlocks) || | ||
!Number.isInteger(neededBlocks) || | ||
neededBlocks < 1 || | ||
neededBlocks > 2 ** availableSpace) { | ||
throw new TypeError(`Expected "neededBlocks" to be a positive integer between 1 and ${(2 ** availableSpace).toLocaleString()} (which is 2^(32-cidrNumber))`); | ||
if (typeof neededSubnets !== 'number' || | ||
Number.isNaN(neededSubnets) || | ||
!Number.isInteger(neededSubnets) || | ||
neededSubnets < 1) { | ||
throw new TypeError(`Expected "neededSubnets" to be a positive integer.`); | ||
} | ||
if (neededSubnets > 2 ** availableSpace) { | ||
throw new TypeError(`The number of subnets needed (${neededSubnets.toLocaleString()}) exceeds the available space in the parent CIDR block (${(2 ** availableSpace).toLocaleString()})`); | ||
} | ||
return { | ||
parentCidr, | ||
availableSpace, | ||
}; | ||
@@ -44,37 +45,36 @@ } | ||
*/ | ||
function uniformlyDistributedSubnets({ neededBlocks, cidr, }) { | ||
const { parentCidr, availableSpace } = validate({ neededBlocks, cidr }); | ||
function uniformlyDistributedSubnets({ neededSubnets, cidr, }) { | ||
const { parentCidr } = validate({ neededSubnets, cidr }); | ||
/** | ||
* Where x is the CIDR number | ||
* y is availableSpace | ||
* z is neededBlocks | ||
* Where x is the optimal subnet CIDR number | ||
* y is parentCidrNumber | ||
* z is neededSubnets | ||
* | ||
* Theoretical max IPs = 2^y / z | ||
* CIDR number IP space = 2^(32-x) | ||
* Theoretical max IPs = 2^(32-y) / z | ||
* Optimal CIDR number IP space = 2^(32-x) | ||
* | ||
* Solving for theoretical `x`: | ||
* 2^(32-x) = 2^(32-y) / z | ||
* x = 32 - log2(2^(32-y) / z) | ||
* | ||
* `2^(32-x) = 2^y / z` | ||
* ... which simplies to x = log( (2^32-y) * z / log(2) ) | ||
* but we can only do the closest power of 2, so we need to round up `x` to get the CIDR number closest to the theoretical max. | ||
* | ||
* but we can only do the closest power of 2, so we need to round up `x` to get our CIDR number. | ||
* x = ceil(32 - log2(2^(32-y) / z)) | ||
*/ | ||
const cidrNumber = Math.ceil(Math.log(2 ** (32 - availableSpace) * neededBlocks) / Math.log(2)); | ||
const maxIpsPerBlock = 2 ** (32 - cidrNumber); | ||
// Get the actual CIDR blocks needed | ||
const cidrBlocks = []; | ||
const optimalSubnetCidrPrefixLength = Math.ceil(32 - Math.log2(2 ** (32 - parentCidr.prefixLen) / neededSubnets)); | ||
// Get the actual subnet CIDR blocks needed | ||
const subnetCidrs = []; | ||
let currentCidr; | ||
let currentRange; | ||
let nextStartIpAddr = parentCidr.toIpRange().startIpAddr; | ||
for (let index = 0; index < neededBlocks; index++) { | ||
currentCidr = new Cidr(nextStartIpAddr, cidrNumber); | ||
for (let index = 0; index < neededSubnets; index++) { | ||
currentCidr = new Cidr(nextStartIpAddr, optimalSubnetCidrPrefixLength); | ||
currentRange = currentCidr.toIpRange(); | ||
cidrBlocks.push(currentCidr); | ||
subnetCidrs.push(currentCidr); | ||
nextStartIpAddr = currentRange.endIpAddr.next(); | ||
} | ||
const maxIpsPerSubnet = 2 ** (32 - optimalSubnetCidrPrefixLength); | ||
return { | ||
cidrBlocks, | ||
cidrNumber, | ||
maxIpsPerBlock, | ||
availableSpace, | ||
subnetCidrs, | ||
optimalSubnetCidrPrefixLength, | ||
maxIpsPerSubnet, | ||
parentCidr, | ||
@@ -81,0 +81,0 @@ }; |
export interface UniformlyDistributedSubnetsArguments { | ||
neededBlocks: number; | ||
neededSubnets: number; | ||
cidr: string; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -6,9 +6,8 @@ import { Cidr } from 'cidr-calc'; | ||
*/ | ||
export declare function uniformlyDistributedSubnets({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
cidrBlocks: Cidr[]; | ||
cidrNumber: number; | ||
maxIpsPerBlock: number; | ||
availableSpace: number; | ||
export declare function uniformlyDistributedSubnets({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
subnetCidrs: Cidr[]; | ||
optimalSubnetCidrPrefixLength: number; | ||
maxIpsPerSubnet: number; | ||
parentCidr: Cidr; | ||
}; | ||
//# sourceMappingURL=uniformly-distributed-subnets.d.ts.map |
import { Cidr } from 'cidr-calc'; | ||
import { UniformlyDistributedSubnetsArguments } from './types.js'; | ||
export declare function validate({ neededBlocks, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
export declare function validate({ neededSubnets, cidr, }: UniformlyDistributedSubnetsArguments): { | ||
parentCidr: Cidr; | ||
availableSpace: number; | ||
}; | ||
//# sourceMappingURL=validate.d.ts.map |
{ | ||
"name": "calculate-subnets", | ||
"type": "module", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "TODO", | ||
@@ -25,2 +25,10 @@ "license": "UNLICENSED", | ||
], | ||
"scripts": { | ||
"build": "del dist && rollup -c", | ||
"start": "tsx bin/cli.ts", | ||
"test": "tsx --test test/**.test.ts", | ||
"lint": "concurrently -c auto \"pnpm:lint:*\"", | ||
"lint:prettier": "prettier --check . --log-level warn", | ||
"lint:types": "tsc --noEmit" | ||
}, | ||
"devDependencies": { | ||
@@ -49,11 +57,3 @@ "@eslint/js": "^8.57.1", | ||
"cidr-calc": "^1.0.4" | ||
}, | ||
"scripts": { | ||
"build": "del dist && rollup -c", | ||
"start": "tsx bin/cli.ts", | ||
"test": "tsx --test test/**.test.ts", | ||
"lint": "concurrently -c auto \"pnpm:lint:*\"", | ||
"lint:prettier": "prettier --check . --log-level warn", | ||
"lint:types": "tsc --noEmit" | ||
} | ||
} | ||
} |
# Calculate Subnet CIDRs | ||
Calculates the optimal subnet CIDRs for a network. Uses [`cidr-calc`](https://github.com/arineng/cidr-calc) for IP ranges. | ||
Split a CIDR into the largest possible subnet CIDRs. Uses [`cidr-calc`](https://github.com/arineng/cidr-calc) for IP ranges. | ||
@@ -10,3 +10,3 @@ ## Usage | ||
```console | ||
$ npx calculate-subnets --needed-blocks=9 --cidr='10.113.0.0/16' | ||
$ npx calculate-subnets --needed-subnets=9 --cidr='10.113.0.0/16' | ||
@@ -17,6 +17,6 @@ Network IP range: 10.113.0.0 - 10.113.255.255 | ||
Subnets' CIDR number: /20 | ||
Subnets' optimal CIDR number: /20 | ||
Max IPs per subnet: 4,096 | ||
CIDR blocks: | ||
Subnet CIDR blocks: | ||
Subnet 1: 10.113.0.0/20 (IP range: 10.113.0.0 - 10.113.15.255) | ||
@@ -42,10 +42,10 @@ Subnet 2: 10.113.16.0/20 (IP range: 10.113.16.0 - 10.113.31.255) | ||
const { cidrBlocks } = uniformlyDistributedSubnets({ | ||
neededBlocks: 9, | ||
const { subnetCidrs } = uniformlyDistributedSubnets({ | ||
neededSubnets: 9, | ||
cidr: '10.113.0.0/16', | ||
}); | ||
for (const cidrBlock of cidrBlocks) { | ||
console.log(cidrBlock.toString()); | ||
for (const subnetCidr of subnetCidrs) { | ||
console.log(subnetCidr.toString()); | ||
} | ||
``` |
export interface UniformlyDistributedSubnetsArguments { | ||
neededBlocks: number; | ||
neededSubnets: number; | ||
cidr: string; | ||
} |
@@ -9,53 +9,51 @@ import { Cidr, IpRange } from 'cidr-calc'; | ||
export function uniformlyDistributedSubnets({ | ||
neededBlocks, | ||
neededSubnets, | ||
cidr, | ||
}: UniformlyDistributedSubnetsArguments): { | ||
cidrBlocks: Cidr[]; | ||
cidrNumber: number; | ||
maxIpsPerBlock: number; | ||
availableSpace: number; | ||
subnetCidrs: Cidr[]; | ||
optimalSubnetCidrPrefixLength: number; | ||
maxIpsPerSubnet: number; | ||
parentCidr: Cidr; | ||
} { | ||
const { parentCidr, availableSpace } = validate({ neededBlocks, cidr }); | ||
const { parentCidr } = validate({ neededSubnets, cidr }); | ||
/** | ||
* Where x is the CIDR number | ||
* y is availableSpace | ||
* z is neededBlocks | ||
* Where x is the optimal subnet CIDR number | ||
* y is parentCidrNumber | ||
* z is neededSubnets | ||
* | ||
* Theoretical max IPs = 2^y / z | ||
* CIDR number IP space = 2^(32-x) | ||
* Theoretical max IPs = 2^(32-y) / z | ||
* Optimal CIDR number IP space = 2^(32-x) | ||
* | ||
* Solving for theoretical `x`: | ||
* 2^(32-x) = 2^(32-y) / z | ||
* x = 32 - log2(2^(32-y) / z) | ||
* | ||
* `2^(32-x) = 2^y / z` | ||
* ... which simplies to x = log( (2^32-y) * z / log(2) ) | ||
* but we can only do the closest power of 2, so we need to round up `x` to get the CIDR number closest to the theoretical max. | ||
* | ||
* but we can only do the closest power of 2, so we need to round up `x` to get our CIDR number. | ||
* x = ceil(32 - log2(2^(32-y) / z)) | ||
*/ | ||
const cidrNumber = Math.ceil( | ||
Math.log(2 ** (32 - availableSpace) * neededBlocks) / Math.log(2), | ||
const optimalSubnetCidrPrefixLength = Math.ceil( | ||
32 - Math.log2(2 ** (32 - parentCidr.prefixLen) / neededSubnets), | ||
); | ||
const maxIpsPerBlock = 2 ** (32 - cidrNumber); | ||
// Get the actual CIDR blocks needed | ||
const cidrBlocks: Cidr[] = []; | ||
// Get the actual subnet CIDR blocks needed | ||
const subnetCidrs: Cidr[] = []; | ||
let currentCidr: Cidr; | ||
let currentRange: IpRange; | ||
let nextStartIpAddr = parentCidr.toIpRange().startIpAddr; | ||
for (let index = 0; index < neededBlocks; index++) { | ||
currentCidr = new Cidr(nextStartIpAddr, cidrNumber); | ||
for (let index = 0; index < neededSubnets; index++) { | ||
currentCidr = new Cidr(nextStartIpAddr, optimalSubnetCidrPrefixLength); | ||
currentRange = currentCidr.toIpRange(); | ||
cidrBlocks.push(currentCidr); | ||
subnetCidrs.push(currentCidr); | ||
nextStartIpAddr = currentRange.endIpAddr.next(); | ||
} | ||
const maxIpsPerSubnet = 2 ** (32 - optimalSubnetCidrPrefixLength); | ||
return { | ||
cidrBlocks, | ||
cidrNumber, | ||
maxIpsPerBlock, | ||
availableSpace, | ||
subnetCidrs, | ||
optimalSubnetCidrPrefixLength, | ||
maxIpsPerSubnet, | ||
parentCidr, | ||
}; | ||
} |
@@ -6,7 +6,6 @@ import assert from 'node:assert'; | ||
export function validate({ | ||
neededBlocks, | ||
neededSubnets, | ||
cidr, | ||
}: UniformlyDistributedSubnetsArguments): { | ||
parentCidr: Cidr; | ||
availableSpace: number; | ||
} { | ||
@@ -23,3 +22,3 @@ let parentCidr: Cidr; | ||
parentCidr = new Cidr(IpAddress.of(ipRaw), Number(cidrNumberRaw)); | ||
availableSpace = 32 - Number(cidrNumberRaw); | ||
availableSpace = 32 - parentCidr.prefixLen; | ||
} catch { | ||
@@ -31,3 +30,3 @@ throw new TypeError(`Invalid CIDR provided: ${cidr}`); | ||
typeof availableSpace !== 'number' || | ||
Number.isNaN(neededBlocks) || | ||
Number.isNaN(availableSpace) || | ||
!Number.isInteger(availableSpace) || | ||
@@ -43,17 +42,18 @@ availableSpace < 0 || | ||
if ( | ||
typeof neededBlocks !== 'number' || | ||
Number.isNaN(neededBlocks) || | ||
!Number.isInteger(neededBlocks) || | ||
neededBlocks < 1 || | ||
neededBlocks > 2 ** availableSpace | ||
typeof neededSubnets !== 'number' || | ||
Number.isNaN(neededSubnets) || | ||
!Number.isInteger(neededSubnets) || | ||
neededSubnets < 1 | ||
) { | ||
throw new TypeError(`Expected "neededSubnets" to be a positive integer.`); | ||
} | ||
if (neededSubnets > 2 ** availableSpace) { | ||
throw new TypeError( | ||
`Expected "neededBlocks" to be a positive integer between 1 and ${(2 ** availableSpace).toLocaleString()} (which is 2^(32-cidrNumber))`, | ||
`The number of subnets needed (${neededSubnets.toLocaleString()}) exceeds the available space in the parent CIDR block (${(2 ** availableSpace).toLocaleString()})`, | ||
); | ||
} | ||
return { | ||
parentCidr, | ||
availableSpace, | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47904
480