Network Tools

IPv4 Range to CIDR

Split an IPv4 start/end range into the minimal exact set of CIDR blocks.

IPv4 range to CIDR
Builds the minimal set of CIDR blocks that exactly covers the range.
64Total addresses
1CIDR blocks
3232238080Start decimal
3232238143End decimal
Exact CIDR cover
CIDRAddresses
192.168.10.0/2664

IPv4 Range to CIDR

Convert an inclusive IPv4 start/end range into the smallest set of CIDR blocks that covers it exactly without including outside addresses.

Use Cases

  • Rewrite firewall or access-control ranges as CIDR
  • Verify address counts and decimal forms
  • This tool handles IPv4 only, not IPv6

Converts an inclusive IPv4 start-and-end range into the smallest set of CIDR prefixes that covers every address inside it and nothing outside. The split runs as greedy 32-bit integer arithmetic in BigInt, so no address value overflows. Use it to rewrite firewall or ACL ranges as prefixes, and to reconcile the address total before a bulk change.

The greedy walk upward from the low address

An IPv4 address is one 32-bit integer, so the split needs no lookup table. Starting at the low end, the code asks for the lowest set bit of the current address, written current & -current; that value is precisely the largest power-of-two block that can begin there while staying aligned. It then halves that size until it fits the addresses still remaining (end - current + 1), turns the size into a prefix by subtracting its bit length from 32, and advances the cursor by the block size. Running 10.20.30.40 to 10.20.30.50 shows both forces at work: 40 is a multiple of 8, so the first block can be a /29, but the three addresses left after it force a /31 and then a /32. The cursor only moves forward, and the loop ends because every step consumes at least one address.

Why 62 blocks is the ceiling, not millions

An arbitrary range cannot demand more prefixes than its two boundaries have bit changes, because the greedy cursor is forced to stop at each one. The worst case inside the 32-bit space is 0.0.0.1 through 255.255.255.254, which comes out as 62 prefixes, from 0.0.0.1/32 to 255.255.255.254/32. The opposite extreme is just as informative: 0.0.0.0 through 255.255.255.255 collapses to the single block 0.0.0.0/0, because the special case for a zero-valued cursor starts the alignment candidate at 2^32. So 4,294,967,295 addresses are either one line or at most 62, never a list you cannot review. The per-block sizes are the audit trail: they must sum to end - start + 1, which is the inclusive count shown above the table, not the difference of the two endpoint decimals.

Strict dotted-quad parsing, and what your shell forgives

Input passes four decimal parts, one to three digits each, no leading zeros, 255 maximum. That is stricter than the parsers most people test against: inet_aton reads 127.1 as shorthand and 192.168.001.1 as octal, and browsers normalise similar forms inside URLs, so a host that pings fine can still be rejected here. The strictness is the point, since an octet like 010 means 8 to libc and 10 to a human, and a prefix list built from the wrong reading widens an allowlist without any visible error. All arithmetic stays in BigInt from 0 to 4294967295, avoiding the classic splitter bug where the same bit maths runs in signed 32-bit integers, turns negative at 128.0.0.0, and produces an empty result.

Worked example

Splitting an 11-address range into three prefixes

Input
10.20.30.40 – 10.20.30.50
Output
10.20.30.40/29 (8 addresses), 10.20.30.48/31 (2), 10.20.30.50/32 (1) · total 11 addresses · decimal 169090600–169090610

The inclusive length is 50 - 40 + 1 = 11, and the block sizes 8 + 2 + 1 add up to exactly that. The octet 40 is a multiple of 8, so a /29 may start there; the remaining three addresses cannot form a /30, because .48 is only 16-aligned and a /30 would need 4 addresses, so the tail degrades to a /31 plus a single /32.

Limits and pitfalls

  • The copy button emits the prefix list only, newline-joined, so the per-block address counts and the two decimal endpoints stay in the UI and must be recorded separately.
  • Leading zeros are rejected, so 192.168.001.1 fails here while inet_aton-based tooling accepts it and reads it as octal; the same string can be valid on one side and invalid on the other.
  • There is no aggregation pass: blocks come out in ascending order and are never merged, so two adjacent runs must be re-entered as one wider range to become a shorter supernet.
  • One start-and-end pair per calculation, with no batch list or file input, and the table renders every block at once with no paging — a worst-case range means 62 rows.
  • Which block a single host belongs to, plus mask, broadcast and usable-host figures, is a different question answered by the subnet calculator, not by this range split.

Related tools

When it goes wrong

The red error appears while the address is still half typed.

CauseBoth fields drive a Vue computed through v-model, so the parser re-runs on every keystroke. Typing a trailing dot leaves an empty fourth part, which fails the one-to-three-digit check and throws before the last octet exists; nothing gates the parse on blur or on a debounce timer.

FixKeep typing — the message clears on the next keystroke that parses. If the flicker is distracting, paste a complete address instead of entering it digit by digit.

I swapped the two addresses by hand and the stats plus the whole CIDR table vanished.

CauseThe range must be ascending. When the start value is larger than the end value the computed value throws, the template renders that error line in place of the stat grid, and the CIDR panel sits behind the same error flag. Both halves disappear together.

FixPress the Swap range button in the panel header; it exchanges the two fields without retyping and the table comes back on the same render.

A range pasted as one string, or an IPv6 address, is refused by a field that looks like it should take it.

CauseEach field holds exactly one dotted quad: the value is trimmed at the outer ends only, split on ., and every part must be one to three digits. A hyphen, en dash or space from an Excel or PDF copy leaves a part that fails that test; an IPv6 literal splits into a single part and misses the four-part count; 10.0.0.0/24 leaves 0/24 in the last part, which fails as well. All three report the same sentence.

FixPaste addresses only and split the pair across the two fields yourself. A prefix belongs in Subnet Calculator; IPv6 is written and summarised as prefixes rather than as a start/end pair, so it is out of scope here.

The address count I copied out of the result table breaks the script that reads it.

CauseThe total and each per-block size are rendered through toLocaleString(), so every value from a thousand upward carries a grouping separator — a /8 shows 16,777,216. That column is a display string, not a number a parser can consume.

FixRecompute from the two decimal endpoints, which are printed with plain toString(): end minus start plus one gives the same total, and 32 minus the prefix length gives the block size in bits.

An address that looks perfect on screen still gets rejected, usually right after an Excel copy or IME input.

CauseThe digit test accepts ASCII 0-9 only and the split looks for the ASCII full stop. Full-width digits produced with a CJK input method, or a full-width full stop in place of the dot, leave parts the parser cannot read. The message names no field, and the start field is parsed first, so a bad start masks a bad end.

FixSwitch the input method to half-width, or paste through a plain-text editor and copy back. Then fix the start field first and re-read the end field, since only one message shows at a time.

Split into CIDRs, keep the range, or grab another tool

SituationThis range splitterWhere to go instead
The consumer already understands a start-and-end rangeEmits disjoint CIDR blocks and nothing else: 10.20.30.40 to 10.20.30.50 comes back as three prefixes, so a rule that could hold one range now holds three lines.Write the range as given — nftables sets accept 10.20.30.40-10.20.30.50, and iptables -m iprange --src-range takes the same pair. Come here only when the consumer is CIDR-only, such as a cloud security group or an ipset of type hash:net.
You have host address plus prefix and want mask, broadcast and usable hostsTakes two dotted quads only, so 10.0.0.14/26 is rejected; the result has no column for a mask or a usable-host count either.Subnet Calculator accepts 10.0.0.14/26 and answers with network address, mask and 62 usable hosts. Return here only once both ends of the block are known and the output has to be a prefix list.
You need to know who owns the addresses before you split themPure 32-bit arithmetic: prefixes, block sizes and two decimal endpoints, with no registry, ASN or country anywhere in the output.IP Lookup returns country, ISP, organisation and ASN for one address. Use it to decide whether the block is worth rewriting at all, then split the range here.
The same conversion has to run in a pipeline, or over dozens of rangesOne start/end pair per page, typed or pasted by hand; no file input, no URL parameter, and no result a script can read.Four lines of Python standard library: ipaddress.summarize_address_range(ip_address(a), ip_address(b)) runs the same greedy walk and produces the same minimal blocks. Keep this page for checking one answer by eye.
You already hold several prefixes and want fewer lines, not moreThe direction is fixed at one contiguous range in, blocks out, and the blocks it produces are never merged with one another.ipaddress.collapse_addresses([...]) goes the other way and folds adjacent prefixes into supernets. Enter the equivalent single range here only if those prefixes are already contiguous.

Frequently Asked Questions

Why does one range split into multiple CIDRs?
CIDR boundaries must align to powers of two (a /24’s third octet must be a multiple of its block size), and arbitrary start-end ranges rarely align. The tool covers your range exactly with the minimal set of CIDRs — 10.0.0.5–10.0.0.10 splitting into several blocks is correct behavior.
How do I verify nothing was over- or under-covered?
The tool lists each CIDR with its address count; the sum must equal your input range length. Reconcile totals before pushing bulk firewall rules — that is how you avoid the "allowlist accidentally admitted the neighboring subnet" incident.
Does it support IPv6 ranges?
This tool handles IPv4. IPv6 allocation practice is CIDR-native anyway (arbitrary ranges are rare), and expanding a 128-bit range can produce an explosion of blocks — plan with aligned prefixes from the start.

Content reviewed: