Skip to content

brandonburrus/cidr-block

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

IPv4 and IPv6 address and CIDR range utilities for JavaScript and TypeScript.

Installation

npm install cidr-block

Features

  • Full IPv4 and IPv6 address support
  • CIDR block creation and manipulation
  • Address validation and parsing
  • Network calculations (netmask, hostmask, address count, usable addresses)
  • Subnetting operations
  • Address type detection (private, loopback, multicast, etc.)
  • Full TypeScript support with comprehensive type definitions
  • Zero dependencies
  • Works with ESM and CommonJS

Quick Start

import { ipv4, ipv6 } from 'cidr-block';

// Create and work with IPv4 addresses
const addr = ipv4.address('192.168.1.1');
console.log(addr.isPrivateAddress()); // true

// Create and work with CIDR blocks
const cidr = ipv4.cidr('10.0.0.0/16');
console.log(cidr.addressCount()); // 65536

// IPv6 works the same way
const v6addr = ipv6.address('2001:db8::1');
console.log(v6addr.toString()); // "2001:db8::1"

API Reference

Full documentation can be found at cidr-block.com.

IPv4

Creating Addresses

import { ipv4 } from 'cidr-block';

// From string
const addr1 = ipv4.address('192.168.1.1');

// From number
const addr2 = ipv4.address(3232235777);

// From octet array
const addr3 = ipv4.address([192, 168, 1, 1]);

Address Validation

import { ipv4 } from 'cidr-block';

ipv4.isValidAddress('192.168.1.1');     // true
ipv4.isValidAddress('256.1.1.1');       // false
ipv4.isValidAddress([10, 0, 0, 1]);     // true
ipv4.isValidAddress(4294967295);        // true (255.255.255.255)
ipv4.isValidAddress(4294967296);        // false (exceeds max)

Address Conversion

import { ipv4 } from 'cidr-block';

const addr = ipv4.address('192.168.1.1');

addr.toString();       // "192.168.1.1"
addr.toNumber();       // 3232235777
addr.octets();         // [192, 168, 1, 1]
addr.toBinaryString(); // "11000000.10101000.00000001.00000001"

Address Type Detection

import { ipv4 } from 'cidr-block';

// Private addresses (RFC 1918)
ipv4.address('10.0.0.1').isPrivateAddress();      // true
ipv4.address('172.16.0.1').isPrivateAddress();    // true
ipv4.address('192.168.0.1').isPrivateAddress();   // true
ipv4.address('8.8.8.8').isPrivateAddress();       // false

// Loopback addresses
ipv4.address('127.0.0.1').isLoopbackAddress();    // true
ipv4.address('127.255.255.255').isLoopbackAddress(); // true

// Link-local addresses
ipv4.address('169.254.1.1').isLocalLinkAddress(); // true

// Multicast addresses
ipv4.address('224.0.0.1').isMulticastAddress();   // true
ipv4.address('239.255.255.255').isMulticastAddress(); // true

Address Comparison

import { ipv4 } from 'cidr-block';

const addr1 = ipv4.address('192.168.1.1');
const addr2 = ipv4.address('192.168.1.2');

addr1.equals('192.168.1.1');           // true
addr1.equals(addr2);                   // false
addr1.isLessThan(addr2);               // true
addr1.isGreaterThan(addr2);            // false
addr1.isLessThanOrEqual(addr2);        // true
addr1.isGreaterThanOrEqual(addr2);     // false

Address Navigation

import { ipv4 } from 'cidr-block';

const addr = ipv4.address('192.168.1.100');

addr.hasNextAddress();       // true
addr.nextAddress()?.toString();    // "192.168.1.101"

addr.hasPreviousAddress();   // true
addr.previousAddress()?.toString(); // "192.168.1.99"

// Edge cases
const maxAddr = ipv4.address('255.255.255.255');
maxAddr.hasNextAddress();    // false
maxAddr.nextAddress();       // undefined

const minAddr = ipv4.address('0.0.0.0');
minAddr.hasPreviousAddress(); // false
minAddr.previousAddress();    // undefined

Creating CIDR Blocks

import { ipv4 } from 'cidr-block';

// From string
const cidr1 = ipv4.cidr('192.168.0.0/24');

// From object
const cidr2 = ipv4.cidr({ address: '192.168.0.0', range: 24 });

// From tuple
const cidr3 = ipv4.cidr(['192.168.0.0', 24]);

// Mixed formats work too
const cidr4 = ipv4.cidr({ address: [192, 168, 0, 0], range: 24 });

CIDR Validation

import { ipv4 } from 'cidr-block';

ipv4.isValidCIDR('192.168.0.0/24');   // true
ipv4.isValidCIDR('192.168.0.0/33');   // false (range exceeds 32)
ipv4.isValidCIDR('256.0.0.0/24');     // false (invalid address)
ipv4.isValidCIDR(['10.0.0.0', 8]);    // true

CIDR Properties

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.0.0/24');

cidr.toString();                  // "192.168.0.0/24"
cidr.baseAddress().toString();    // "192.168.0.0"
cidr.range();                     // 24
cidr.netmask().toString();        // "255.255.255.0"
cidr.hostmask().toString();       // "0.0.0.255"
cidr.addressCount();              // 256
cidr.rangeParts();                // [Ipv4Address, 24]

// Calculate network address from CIDR with host bits
const hostCidr = ipv4.cidr('192.168.1.5/24');
hostCidr.network().toString();      // "192.168.1.0"
hostCidr.networkCIDR().toString();  // "192.168.1.0/24"

Usable Addresses

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.1.0/24');

// First usable (excludes network address)
cidr.getFirstUsableAddress()?.toString(); // "192.168.1.1"

// Last usable (excludes broadcast address)
cidr.getLastUsableAddress()?.toString();  // "192.168.1.254"

// For /32, there are no usable addresses
const hostCidr = ipv4.cidr('192.168.1.1/32');
hostCidr.getFirstUsableAddress(); // undefined
hostCidr.getLastUsableAddress();  // undefined

Iterating Addresses

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.1.0/30');

// Using the generator
for (const addr of cidr.addresses()) {
  console.log(addr.toString());
}
// Output:
// 192.168.1.0
// 192.168.1.1
// 192.168.1.2
// 192.168.1.3

// Convert to array
const allAddresses = [...cidr.addresses()];
console.log(allAddresses.length); // 4

Address Containment

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.0.0/24');

cidr.includes(ipv4.address('192.168.0.100')); // true
cidr.includes(ipv4.address('192.168.1.1'));   // false
cidr.includes(ipv4.address('192.168.0.0'));   // true
cidr.includes(ipv4.address('192.168.0.255')); // true

CIDR Overlap Detection

import { ipv4 } from 'cidr-block';

const cidr1 = ipv4.cidr('192.168.0.0/24');
const cidr2 = ipv4.cidr('192.168.0.128/25');
const cidr3 = ipv4.cidr('192.168.1.0/24');

cidr1.overlaps(cidr2); // true (cidr2 is a subnet of cidr1)
cidr1.overlaps(cidr3); // false (different networks)

// Also accepts string format
cidr1.overlaps('10.0.0.0/8'); // false

Subnetting

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.0.0/24');

// Split into equal subnets
const subnets = cidr.subnet(26);
subnets.forEach(s => console.log(s.toString()));
// Output:
// 192.168.0.0/26
// 192.168.0.64/26
// 192.168.0.128/26
// 192.168.0.192/26

// Split into variable-sized subnets
const varSubnets = cidr.subnetBy([26, 27, 27, 26]);
varSubnets.forEach(s => console.log(s.toString()));
// Output:
// 192.168.0.0/26
// 192.168.0.64/27
// 192.168.0.96/27
// 192.168.0.128/26

CIDR Navigation

import { ipv4 } from 'cidr-block';

const cidr = ipv4.cidr('192.168.0.0/24');

cidr.hasNextCIDR();                    // true
cidr.nextCIDR()?.toString();           // "192.168.1.0/24"

cidr.hasPreviousCIDR();                // true
cidr.previousCIDR()?.toString();       // "192.167.255.0/24"

IPv6

Creating Addresses

import { ipv6 } from 'cidr-block';

// From string (with :: compression)
const addr1 = ipv6.address('2001:db8::1');

// From full string
const addr2 = ipv6.address('2001:0db8:0000:0000:0000:0000:0000:0001');

// From BigInt
const addr3 = ipv6.address(42540766411282592856903984951653826561n);

// From hextets array
const addr4 = ipv6.address([0x2001, 0x0db8, 0, 0, 0, 0, 0, 1]);

Address Validation

import { ipv6 } from 'cidr-block';

ipv6.isValidAddress('2001:db8::1');           // true
ipv6.isValidAddress('::1');                   // true
ipv6.isValidAddress('::');                    // true
ipv6.isValidAddress('::ffff:192.168.1.1');    // true (IPv4-mapped)
ipv6.isValidAddress('2001:db8::g');           // false (invalid hex)
ipv6.isValidAddress('2001:db8:::1');          // false (multiple ::)

Address Conversion

import { ipv6 } from 'cidr-block';

const addr = ipv6.address('2001:db8::1');

addr.toString();     // "2001:db8::1" (compressed)
addr.toFullString(); // "2001:0db8:0000:0000:0000:0000:0000:0001"
addr.toBigInt();     // 42540766411282592856903984951653826561n
addr.hextets();      // [8193, 3512, 0, 0, 0, 0, 0, 1]
addr.toBinaryString(); // Binary representation with colons

Address Type Detection

import { ipv6 } from 'cidr-block';

// Loopback
ipv6.address('::1').isLoopbackAddress();          // true

// Unspecified
ipv6.address('::').isUnspecifiedAddress();        // true

// Unique local (private equivalent)
ipv6.address('fc00::1').isUniqueLocalAddress();   // true
ipv6.address('fd00::1').isUniqueLocalAddress();   // true

// Link-local
ipv6.address('fe80::1').isLinkLocalAddress();     // true

// Multicast
ipv6.address('ff02::1').isMulticastAddress();     // true

// IPv4-mapped
ipv6.address('::ffff:192.168.1.1').isIPv4MappedAddress(); // true

// Documentation
ipv6.address('2001:db8::1').isDocumentationAddress(); // true

Address Comparison

import { ipv6 } from 'cidr-block';

const addr1 = ipv6.address('2001:db8::1');
const addr2 = ipv6.address('2001:db8::2');

addr1.equals('2001:db8::1');           // true
addr1.equals(addr2);                   // false
addr1.isLessThan(addr2);               // true
addr1.isGreaterThan(addr2);            // false
addr1.isLessThanOrEqual(addr2);        // true
addr1.isGreaterThanOrEqual(addr2);     // false

Address Navigation

import { ipv6 } from 'cidr-block';

const addr = ipv6.address('2001:db8::1');

addr.hasNextAddress();                // true
addr.nextAddress()?.toString();       // "2001:db8::2"

addr.hasPreviousAddress();            // true
addr.previousAddress()?.toString();   // "2001:db8::"

Creating CIDR Blocks

import { ipv6 } from 'cidr-block';

// From string
const cidr1 = ipv6.cidr('2001:db8::/32');

// From object
const cidr2 = ipv6.cidr({ address: '2001:db8::', range: 32 });

// From tuple
const cidr3 = ipv6.cidr(['2001:db8::', 32]);

CIDR Validation

import { ipv6 } from 'cidr-block';

ipv6.isValidCIDR('2001:db8::/32');    // true
ipv6.isValidCIDR('2001:db8::/129');   // false (range exceeds 128)
ipv6.isValidCIDR(['::1', 128]);       // true

CIDR Properties

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/32');

cidr.toString();                  // "2001:db8::/32"
cidr.baseAddress().toString();    // "2001:db8::"
cidr.range();                     // 32
cidr.netmask().toString();        // "ffff:ffff::"
cidr.hostmask().toString();       // "::ffff:ffff:ffff:ffff:ffff:ffff"
cidr.addressCount();              // 79228162514264337593543950336n (BigInt)

// Calculate network address from CIDR with host bits
const hostCidr = ipv6.cidr('2001:db8::1234/32');
hostCidr.network().toString();      // "2001:db8::"
hostCidr.networkCIDR().toString();  // "2001:db8::/32"

Usable Addresses

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/126');

cidr.getFirstUsableAddress()?.toString(); // "2001:db8::1"
cidr.getLastUsableAddress()?.toString();  // "2001:db8::2"

// For /128, there are no usable addresses
const hostCidr = ipv6.cidr('2001:db8::1/128');
hostCidr.getFirstUsableAddress(); // undefined
hostCidr.getLastUsableAddress();  // undefined

Iterating Addresses

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/126');

for (const addr of cidr.addresses()) {
  console.log(addr.toString());
}
// Output:
// 2001:db8::
// 2001:db8::1
// 2001:db8::2
// 2001:db8::3

// With limit (useful for large ranges)
for (const addr of cidr.addresses(2n)) {
  console.log(addr.toString());
}
// Output:
// 2001:db8::
// 2001:db8::1

Address Containment and Overlap

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/32');

cidr.includes(ipv6.address('2001:db8::1'));      // true
cidr.includes(ipv6.address('2001:db9::1'));      // false

const cidr2 = ipv6.cidr('2001:db8:1::/48');
cidr.overlaps(cidr2);  // true

Subnetting

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/32');

// Split into /34 subnets
const subnets = cidr.subnet(34);
subnets.forEach(s => console.log(s.toString()));
// Output:
// 2001:db8::/34
// 2001:db8:4000::/34
// 2001:db8:8000::/34
// 2001:db8:c000::/34

// Variable-sized subnets
const varSubnets = cidr.subnetBy([34, 34, 33]);
varSubnets.forEach(s => console.log(s.toString()));

CIDR Navigation

import { ipv6 } from 'cidr-block';

const cidr = ipv6.cidr('2001:db8::/32');

cidr.hasNextCIDR();                  // true
cidr.nextCIDR()?.toString();         // "2001:db9::/32"

cidr.hasPreviousCIDR();              // true
cidr.previousCIDR()?.toString();     // "2001:db7::/32"

Error Handling

import {
  ipv4,
  ipv6,
  InvalidIpv4AddressError,
  InvalidIpv4CidrError,
  InvalidIpv4CidrRangeError,
  InvalidIpv6AddressError,
  InvalidIpv6CidrError,
  InvalidIpv6CidrRangeError
} from 'cidr-block';

// Invalid address throws error
try {
  ipv4.address('256.0.0.1');
} catch (e) {
  if (e instanceof InvalidIpv4AddressError) {
    console.log(e.message); // "256.0.0.1 is not a valid IPv4 address"
  }
}

// Invalid CIDR throws error
try {
  ipv4.cidr('192.168.0.0/33');
} catch (e) {
  if (e instanceof InvalidIpv4CidrError) {
    console.log(e.message); // "192.168.0.0/33 is not a valid IPv4 CIDR range"
  }
}

// Invalid subnet operation throws error
try {
  const cidr = ipv4.cidr('192.168.0.0/24');
  cidr.subnet(20); // Can't create larger subnets
} catch (e) {
  if (e instanceof InvalidIpv4CidrRangeError) {
    console.log('Invalid subnet range');
  }
}

// Use validation to avoid exceptions
if (ipv4.isValidAddress(userInput)) {
  const addr = ipv4.address(userInput);
  // Safe to use
}

Constants

import { ipv4, ipv6 } from 'cidr-block';

// IPv4 constants
ipv4.MAX_SIZE;   // 0xffffffff (4294967295)
ipv4.MIN_SIZE;   // 0x00000000
ipv4.MAX_RANGE;  // 32
ipv4.MIN_RANGE;  // 0

// IPv6 constants
ipv6.MAX_SIZE;   // (1n << 128n) - 1n
ipv6.MIN_SIZE;   // 0n
ipv6.MAX_RANGE;  // 128
ipv6.MIN_RANGE;  // 0

Type Definitions

import type {
  // IPv4 types
  Ipv4AddressLiteral,  // string | number | number[]
  Ipv4AddressString,   // "${number}.${number}.${number}.${number}"
  Ipv4AddressOctets,   // [number, number, number, number]
  Ipv4CidrLiteral,     // string | { address, range } | [address, range]
  Ipv4CidrString,      // "${number}.${number}.${number}.${number}/${number}"

  // IPv6 types
  Ipv6AddressLiteral,  // string | bigint | number[]
  Ipv6AddressHextets,  // [number, number, number, number, number, number, number, number]
  Ipv6CidrLiteral,     // string | { address, range } | [address, range]
} from 'cidr-block';

Common Use Cases

Checking if an IP is in a Private Network

import { ipv4 } from 'cidr-block';

function isInternalIP(ip: string): boolean {
  if (!ipv4.isValidAddress(ip)) return false;
  const addr = ipv4.address(ip);
  return addr.isPrivateAddress() || addr.isLoopbackAddress();
}

isInternalIP('192.168.1.1');  // true
isInternalIP('10.0.0.1');     // true
isInternalIP('8.8.8.8');      // false

Allocating Subnets from a Pool

import { ipv4 } from 'cidr-block';

function allocateSubnets(poolCidr: string, subnetSize: number, count: number) {
  const pool = ipv4.cidr(poolCidr);
  const subnets = pool.subnet(subnetSize);
  return subnets.slice(0, count);
}

const allocated = allocateSubnets('10.0.0.0/16', 24, 3);
allocated.forEach(s => console.log(s.toString()));
// 10.0.0.0/24
// 10.0.1.0/24
// 10.0.2.0/24

Checking for CIDR Conflicts

import { ipv4, Ipv4Cidr } from 'cidr-block';

function findConflicts(newCidr: string, existing: string[]): string[] {
  const cidr = ipv4.cidr(newCidr);
  return existing.filter(e => cidr.overlaps(e));
}

const existingRanges = ['10.0.0.0/24', '10.0.1.0/24', '192.168.0.0/16'];
const conflicts = findConflicts('10.0.0.0/16', existingRanges);
// ['10.0.0.0/24', '10.0.1.0/24']

Generating IP Addresses in a Range

import { ipv4 } from 'cidr-block';

function getUsableIPs(cidrStr: string): string[] {
  const cidr = ipv4.cidr(cidrStr);
  const ips: string[] = [];

  for (const addr of cidr.addresses()) {
    // Skip network and broadcast addresses for practical use
    if (addr.equals(cidr.baseAddress())) continue;

    const lastOctet = addr.octets()[3];
    if (lastOctet === 255) continue; // Skip broadcast

    ips.push(addr.toString());
  }

  return ips;
}

const usable = getUsableIPs('192.168.1.0/29');
// ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4', '192.168.1.5', '192.168.1.6']

Normalizing CIDR Blocks

import { ipv4, ipv6 } from 'cidr-block';

// Normalize IPv4 CIDR by removing host bits
function normalizeIPv4CIDR(cidrStr: string): string {
  const cidr = ipv4.cidr(cidrStr);
  return cidr.networkCIDR().toString();
}

normalizeIPv4CIDR('192.168.1.5/24');    // "192.168.1.0/24"
normalizeIPv4CIDR('10.5.10.20/8');      // "10.0.0.0/8"
normalizeIPv4CIDR('172.16.50.100/12');  // "172.16.0.0/12"

// Normalize IPv6 CIDR by removing host bits
function normalizeIPv6CIDR(cidrStr: string): string {
  const cidr = ipv6.cidr(cidrStr);
  return cidr.networkCIDR().toString();
}

normalizeIPv6CIDR('2001:db8::1234/32');                   // "2001:db8::/32"
normalizeIPv6CIDR('2001:db8:1:2:3:4:5:6/64');             // "2001:db8:1:2::/64"
normalizeIPv6CIDR('2001:db8:abcd:ef01::/48');             // "2001:db8:abcd::/48"

About

npm package for working with IPv4 and IPv6 addresses and CIDRs πŸ“‘

Topics

Resources

License

Stars

Watchers

Forks

Contributors