What is an IP address and how does subnet math work?
An IP address (Internet Protocol address) is the numerical identifier assigned to every device on a network. It has two roles fused together: identifying which network a device is on, and identifying which device on that network. The split between "network part" and "host part" is determined by the subnet mask (in IPv4) or prefix length (in IPv6 and modern CIDR notation).
The internet runs on two parallel address systems:
- IPv4 — 32-bit addresses, written as four decimal octets (
192.168.1.42). 4.3 billion possible addresses, exhausted at the ARIN/RIPE level since 2011. Still dominant on the public internet (~75% of traffic in 2026). - IPv6 — 128-bit addresses, written as eight hexadecimal groups (
2001:db8::1). 340 undecillion addresses — effectively unlimited. Native on most ISPs, mobile networks, and AWS/GCP/Azure default subnets in 2026.
Subnet calculations let network engineers split a large address block into smaller, isolated networks. CIDR notation (192.168.0.0/24) is the modern way to express this: the number after the slash is the prefix length — how many bits of the address identify the network. Higher number = smaller subnet.
CIDR cheat sheet — common subnet sizes
| CIDR | Subnet mask (IPv4) | Total addresses | Usable hosts | Common use |
|---|---|---|---|---|
/8 | 255.0.0.0 | 16,777,216 | 16,777,214 | Massive ISP allocation; 10.0.0.0/8 private range |
/12 | 255.240.0.0 | 1,048,576 | 1,048,574 | 172.16.0.0/12 private range |
/16 | 255.255.0.0 | 65,536 | 65,534 | Class B; 192.168.0.0/16 private range |
/20 | 255.255.240.0 | 4,096 | 4,094 | Mid-size enterprise subnet |
/24 | 255.255.255.0 | 256 | 254 | Standard LAN — most home/office networks |
/27 | 255.255.255.224 | 32 | 30 | Small department, AWS subnet minimum |
/28 | 255.255.255.240 | 16 | 14 | Tiny VLAN, small DMZ |
/30 | 255.255.255.252 | 4 | 2 | Point-to-point links between routers |
/31 | 255.255.255.254 | 2 | 2 (RFC 3021) | Modern point-to-point — uses both addresses |
/32 | 255.255.255.255 | 1 | 1 | Single host (e.g. firewall rule for one server) |
Why "usable hosts" is 2 less than total: the first address in any subnet is the network address (not assignable to a host) and the last is the broadcast address. A /24 has 256 addresses but only 254 are usable on hosts. Modern /31 subnets are an exception — RFC 3021 lets both addresses be used on point-to-point links.
Private vs public IP ranges (RFC 1918)
Three IPv4 ranges are reserved for private use — they're not routed on the public internet, so multiple organizations can use them simultaneously without conflict. Defined in RFC 1918:
| Range | CIDR | Address count | Common use |
|---|---|---|---|
10.0.0.0 – 10.255.255.255 | 10.0.0.0/8 | 16,777,216 | Big enterprises, AWS default VPC, Kubernetes pods |
172.16.0.0 – 172.31.255.255 | 172.16.0.0/12 | 1,048,576 | Docker default bridge network, mid-size companies |
192.168.0.0 – 192.168.255.255 | 192.168.0.0/16 | 65,536 | Almost every home router (especially 192.168.0.0/24 and 192.168.1.0/24) |
Other reserved IPv4 ranges to know
| Range | Purpose |
|---|---|
0.0.0.0/8 | "This network" — wildcard binding |
127.0.0.0/8 | Loopback (localhost). 127.0.0.1 is the most famous IP. |
169.254.0.0/16 | Link-local / APIPA — auto-assigned when DHCP fails |
224.0.0.0/4 | Multicast (one-to-many) |
100.64.0.0/10 | Carrier-grade NAT (CGNAT) — used by mobile/cable ISPs |
How subnet math actually works (with examples)
Example: parsing 192.168.50.130/27
Address (decimal): 192 . 168 . 50 . 130
Address (binary): 11000000.10101000.00110010.10000010
Prefix /27: 11111111.11111111.11111111.11100000 (27 ones)
└────────── network ──────────┘└host┘
Network address (AND): 11000000.10101000.00110010.10000000 = 192.168.50.128
Broadcast (OR): 11000000.10101000.00110010.10011111 = 192.168.50.159
First usable host: 192.168.50.129
Last usable host: 192.168.50.158
Usable hosts: 30 (32 total − network − broadcast)
Subnet mask: 255.255.255.224
Wildcard mask: 0.0.0.31
Quick mental shortcuts
- Subnet size doubles every −1 to the prefix.
/24= 256,/23= 512,/22= 1024. - The prefix tells you the boundary. A
/27aligns to multiples of 32 (256/8 = 32). Subnets start at .0, .32, .64, .96, .128... - Wildcard mask = inverse of subnet mask. Subnet
255.255.255.224↔ wildcard0.0.0.31. Cisco ACLs use wildcard masks. - Two adjacent subnets can be merged.
192.168.0.0/25+192.168.0.128/25=192.168.0.0/24. This is "supernetting."
IPv6 — what changes from IPv4
IPv6 fixes IPv4's address-exhaustion problem with 128-bit addresses. The mental model is similar but the notation and conventions differ:
Full form: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
Compressed: 2001:db8:85a3::8a2e:370:7334
└ "::" replaces consecutive zeros (only once per address)
Loopback: ::1
Unspecified: ::
Link-local: fe80::/10 (auto-configured per-interface)
Unique local: fc00::/7 (private — IPv6 equivalent of RFC 1918)
Multicast: ff00::/8
Standard host subnet: /64 (always /64 for hosts; SLAAC requires it)
Standard site allocation: /48 to /56 from ISP
Common gotchas migrating from IPv4
- NAT is not used. IPv6 networks don't typically NAT — every device gets a globally routable address.
- Subnet sizes are huge. Even a tiny /64 subnet has 18 quintillion addresses. Don't think small.
- Both addresses on a /127 work. Like IPv4 /31, point-to-point links use /127 in modern practice (RFC 6164).
- Privacy extensions matter. Modern OSes use temporary addresses (privacy addresses) for outbound connections to prevent tracking via MAC-derived addresses.
IP & subnet operations in 8 languages
JavaScript / Node.js
// Built-in net.isIP (Node.js)
import { isIP, isIPv4, isIPv6 } from 'node:net';
isIP('192.168.1.1'); // 4
isIP('::1'); // 6
isIP('not-an-ip'); // 0
// CIDR with the 'ip' package (npm i ip)
import ip from 'ip';
ip.cidrSubnet('192.168.0.1/24').firstAddress; // '192.168.0.1'
ip.toLong('192.168.0.1'); // 3232235521
ip.fromLong(3232235521); // '192.168.0.1'
Python
from ipaddress import ip_address, ip_network, IPv4Network
# Parse + classify
addr = ip_address('192.168.1.1')
addr.is_private # True
addr.is_loopback # False
addr.version # 4
# Subnet math
net = ip_network('192.168.1.0/24')
net.network_address # IPv4Address('192.168.1.0')
net.broadcast_address # IPv4Address('192.168.1.255')
net.num_addresses # 256
list(net.hosts()) # [IPv4Address('192.168.1.1'), ..., IPv4Address('192.168.1.254')]
# IPv6 also works
ip_network('2001:db8::/32').num_addresses # huge number
Go
import "net/netip"
// Modern netip package (Go 1.18+)
addr := netip.MustParseAddr("192.168.1.1")
addr.Is4() // true
addr.IsLoopback() // false
addr.IsPrivate() // true (Go 1.20+)
// Prefix / CIDR
prefix := netip.MustParsePrefix("192.168.0.0/24")
prefix.Contains(addr) // true
prefix.Bits() // 24
// Iterate over hosts
for a := prefix.Addr(); prefix.Contains(a); a = a.Next() {
fmt.Println(a)
}
Rust
use std::net::{IpAddr, Ipv4Addr};
use ipnet::Ipv4Net;
let addr: Ipv4Addr = "192.168.1.1".parse().unwrap();
addr.is_private(); // true
addr.is_loopback(); // false
let net: Ipv4Net = "192.168.0.0/24".parse().unwrap();
net.network(); // 192.168.0.0
net.broadcast(); // 192.168.0.255
net.contains(&addr); // true
PHP
// Validation
filter_var('192.168.1.1', FILTER_VALIDATE_IP); // string or false
filter_var('::1', FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); // ::1 or false
filter_var('192.168.1.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // false (it's private)
// CIDR matching (manual)
function ipInCidr(string $ip, string $cidr): bool {
[$subnet, $bits] = explode('/', $cidr);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - (int)$bits);
return ($ip & $mask) === ($subnet & $mask);
}
Java
// Built-in InetAddress
import java.net.InetAddress;
InetAddress addr = InetAddress.getByName("192.168.1.1");
addr.isLoopbackAddress();
addr.isLinkLocalAddress();
addr.isSiteLocalAddress(); // RFC 1918 private
// CIDR — use Apache Commons Net's SubnetUtils
import org.apache.commons.net.util.SubnetUtils;
SubnetUtils utils = new SubnetUtils("192.168.1.0/24");
utils.getInfo().getNetworkAddress(); // "192.168.1.0"
utils.getInfo().getBroadcastAddress(); // "192.168.1.255"
utils.getInfo().isInRange("192.168.1.42"); // true
Bash / shell
# Basic IPv4 validation
ip="192.168.1.1"
if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo "valid"; fi
# subnet math via 'ipcalc' (install via apt/brew)
ipcalc 192.168.1.0/24
# Network: 192.168.1.0/24
# HostMin: 192.168.1.1
# HostMax: 192.168.1.254
# Broadcast: 192.168.1.255
# Modern alternative: 'sipcalc' or Python one-liner
python3 -c "from ipaddress import ip_network; n=ip_network('192.168.1.0/24'); print(n.broadcast_address)"
SQL (PostgreSQL native types)
-- Postgres has native inet and cidr types
SELECT '192.168.1.1'::inet; -- 192.168.1.1
SELECT '192.168.0.0/24'::cidr; -- 192.168.0.0/24
SELECT '192.168.1.42'::inet <<= '192.168.0.0/24'::cidr; -- true (contained)
SELECT host('192.168.0.5/24'::inet); -- '192.168.0.5'
SELECT broadcast('192.168.0.0/24'); -- 192.168.0.255
-- Indexed lookups for "what subnet does this IP belong to?"
CREATE INDEX idx_subnet ON subnets USING gist (cidr inet_ops);
Common IP/subnet mistakes
- Confusing /24 with 24 hosts. A
/24has 256 addresses (254 usable). Higher CIDR number = smaller subnet, not larger. - Forgetting RFC 1918 ranges aren't routable. Common cause of "works locally, fails in production" — your laptop on
10.xcan't be reached from the internet. - Using 192.168.1.0/24 in cloud VPCs. Many home routers default to this range. If your laptop's home network and your AWS VPC overlap, your VPN is broken. Use
10.xranges in cloud, save 192.168 for home. - Ignoring the network and broadcast addresses in /30 designs. A point-to-point link wants /31 (RFC 3021), not /30 — saves 2 addresses per link.
- IPv4-mapped IPv6 confusion.
::ffff:192.168.1.1is the same address in IPv6 form. Some firewalls treat them differently. - Forgetting CGNAT. Mobile and cable ISPs increasingly use
100.64.0.0/10for customer addresses. If your IP is in this range, traditional IP-based geo-blocking and reputation systems may misclassify you. - Subnet overlap in firewall rules. Allowing
10.0.0.0/8when you meant10.0.0.0/24exposes 16 million extra addresses.
Best subnet calculator and IP lookup tool for 2026 — what to compare
"Subnet calculator" search results are a sea of ad-heavy 2003-era pages. The columns that actually matter when picking one in 2026:
| Tool | IPv4 | IPv6 | Browser-only | Logs your IP | Notes |
|---|---|---|---|---|---|
| This tool (FreeDevTool) | Yes | Yes | Yes | No — no API call on calculation | RFC 1918 / CGNAT / link-local detection |
| subnetcalc.de | Yes | Limited | Yes | No | Old UI, no IPv6 prefix decomposition |
| iplocation.net / what-is-my-ip-address.com | Lookup only | Yes | Server-side | Yes — every visit is geolocated and logged | Geolocation focus, not subnet math |
ipcalc CLI (apt install ipcalc) | Yes | Yes (sipcalc fork) | Local CLI | Local | The classic. No IPv6 in some distros' default ipcalc. |
Cisco / Juniper IOS show ip interface | Yes | Yes | On device | Audit log on device | Authoritative for actual configured interfaces, not for what-if math |
The single test that catches broken calculators: enter 10.0.0.5/30. The correct output is network 10.0.0.4, broadcast 10.0.0.7, usable hosts 10.0.0.5–10.0.0.6 (2 hosts, not 4). Any tool returning hosts 10.0.0.4 or 10.0.0.7 is including the network/broadcast addresses incorrectly — walk away.
How do I find the network address and broadcast for a CIDR block?
Three steps, deterministic, work for every IPv4 prefix:
- Convert the IP and the subnet mask to binary. For
192.168.1.137/26, the address is11000000.10101000.00000001.10001001and the mask (26 leading 1s) is11111111.11111111.11111111.11000000. - AND them bit-by-bit to get the network address. Result:
11000000.10101000.00000001.10000000=192.168.1.128. - OR the network with the inverted mask (the wildcard) to get the broadcast. Wildcard is
00000000.00000000.00000000.00111111; OR gives192.168.1.191.
Usable host range is network + 1 through broadcast - 1: 192.168.1.129–192.168.1.190 (62 hosts). The calculator above shows all four values plus the binary breakdown; for a one-line scripted version, Python's ipaddress.ip_network('192.168.1.137/26', strict=False) returns the same.
What's the difference between IP lookup, geolocation, and reverse DNS?
Three different operations frequently confused:
- IP lookup (this tool). Validates the address syntactically and classifies it (IPv4/IPv6, public/private, RFC 1918/CGNAT/link-local). No network call needed.
- IP geolocation. Maps an IP to an approximate physical location using a database (MaxMind, IP2Location). Accuracy is country-level usually, city-level sometimes. Always involves a server-side database query — never trust a "geolocation" tool that runs purely in the browser.
- Reverse DNS (PTR record). Looks up the hostname registered for an IP address in DNS. Use the DNS lookup tool with type
PTRfor the proper query (137.1.168.192.in-addr.arpafor the example above).
This tool covers the first only. For PTR/MX/CAA records on the resolved hostname use the DNS lookup tool; for HTTP-level checks on the IP's services use the HTTP status checker.
Subnet calculator alternative to subnet-calculator.com — why people switch
The first-page Google results for "subnet calculator" are dominated by sites built between 2003 and 2010 — they work, but they ship with ads, no IPv6, no copy buttons, and poor mobile layout. Three reasons to prefer this page:
- Full IPv6 support. Most legacy calculators stopped at IPv4. This page handles
2001:db8::/32-style prefixes with the same UI. - RFC 1918 / CGNAT / link-local awareness. Pasting
100.64.0.5tells you immediately it's CGNAT (operator-assigned, not your home network) — most calculators just return network math without classification. - No third-party scripts. Open the page, disconnect from the network, calculations still work. Ad-heavy alternatives 404 on the calculation when you block their tag managers.
For workflows that combine subnet math with packet inspection or DNS resolution, pair this tool with the DNS lookup and the HTTP status checker. Going the other direction — embedding an IPv4 inside a URL or query string for testing — pipe the result through the URL encoder.
IP address best practices for 2026
- Use modern CIDR notation (
192.168.1.0/24) everywhere — it's clearer than separate IP + subnet mask. - Plan IPv4 ranges with overlap-avoidance in mind. Don't use
192.168.1.0/24in cloud VPCs (collides with home routers). Use10.x.y.0/24blocks. - Document subnet allocations. Even a simple Google Sheet with "10.20.0.0/24 = production-eu-west" beats nothing. NetBox or Phpipam for serious environments.
- Reserve ranges for growth. Don't allocate
/24if you'll need/22in two years. Re-numbering is painful. - Use IPv6 when possible. All major cloud providers offer it. Modern apps should work with both.
- Validate IPs at API boundaries. Use your language's standard library (
ip_addressin Python,netipin Go,InetAddressin Java). Don't roll your own regex. - Don't trust client-provided IPs alone.
X-Forwarded-Forheaders are user-controlled until your edge proxy strips them. Always set them server-side at the LB/CDN. - Use Postgres
inet/cidrtypes instead ofVARCHARfor storing IPs. Native types are smaller, indexable, and let you query subnet membership.