
The inclusive IPv4 range 192.0.2.40–192.0.2.95 contains 56 addresses. Its exact CIDR representation is three blocks:
192.0.2.40/29
192.0.2.48/28
192.0.2.64/27
A single /25 would be shorter to write, but would include 72 additional addresses. For an allowlist, that changes the policy. The goal is to cover the intended set, not merely find a prefix containing its first and last address.
This worked example uses the documentation range defined in RFC 5737. It does not represent a real customer allocation or a change to a firewall.
Start with an explicit, inclusive address range
Record the first and last address and confirm that both should be included. Also record what the list is for: a source group, destination group or some other policy object. An exact conversion does not prove that the requester owns the addresses or that every address should receive the same access.
In this example:
First included address: 192.0.2.40
Last included address: 192.0.2.95
Count: 95 − 40 + 1 = 56
The count includes every address in the set. It is not a count of assignable hosts in a newly configured subnet. Keeping those questions separate avoids silently dropping boundary addresses from a firewall object.
Fit aligned CIDR blocks without crossing the end
RFC 4632 describes CIDR prefix notation and its power-of-two address blocks. A valid block must also start at the corresponding boundary.
At .40, an eight-address /29 is aligned and spans .40–.47. A /28 cannot
start at .40: the containing /28 begins at .32 and would include unwanted
addresses. Continue from the next uncovered address, .48:
| Exact block | Included range | Addresses |
|---|---|---|
| 192.0.2.40/29 | 192.0.2.40–47 | 8 |
| 192.0.2.48/28 | 192.0.2.48–63 | 16 |
| 192.0.2.64/27 | 192.0.2.64–95 | 32 |
| Total | 192.0.2.40–95 | 56 |
Shortened range endings in the table refer to the final octet. There are no
gaps between the blocks, no overlaps and no entries before .40 or after .95.
CIDR coverage counts every address in a prefix, including its network and broadcast values when those terms apply to a LAN. It does not apply the IPv4 host-count subtraction of two. A /32 can represent one exact address in an allowlist; a /31 represents two address matches even when the list is not configuring a point-to-point link.
Compare an exact list with tempting shortcuts
Here are three possible descriptions of the same intended range:
| Proposed list | Addresses matched | Addresses outside the request |
|---|---|---|
| The three exact blocks above | 56 | 0 |
| 192.0.2.32/27 and 192.0.2.64/27 | 64 | 8: .32–.39 |
| 192.0.2.0/25 | 128 | 72: .0–.39 and .96–.127 |
The two-block shortcut looks close, but it still permits eight addresses that were not requested. The one-block shortcut is broader again. If a rule is supposed to grant exactly the original set, neither shortcut is an equivalent replacement.
Nor does writing 192.0.2.40/26 mean “start at .40 and take 64 addresses.”
That input has host bits set; its containing network is 192.0.2.0/26.
It both matches unwanted earlier addresses and misses .64–.95 from the request.
Check the normalized prefix whenever a tool accepts an address with a mask.
Reproduce the result in the converter
Open the IP Range to CIDR converter, choose
IP range → CIDR blocks, and enter .40 and .95 as full addresses:
- First: 192.0.2.40.
- Last, included: 192.0.2.95.
The output should contain 56 addresses and three blocks, matching the list at the top of this guide. Reverse-convert each block if you need to inspect its complete endpoints. The tool calculates address coverage locally; it does not scan the range, determine who controls it or apply a rule.
When reviewing an existing list, keep the full normalized CIDRs in the worksheet. An application may accept ranges directly or use different object types; check what it actually stores after the change rather than assuming that every input field has the same interpretation.
Verify the exact set independently
Python’s standard-library ipaddress module
can summarize the range and check its membership. This example enumerates only
the small worked range; it makes no network requests:
from ipaddress import ip_address, ip_network, summarize_address_range
first = ip_address("192.0.2.40")
last = ip_address("192.0.2.95")
blocks = list(summarize_address_range(first, last))
wanted = set(range(int(first), int(last) + 1))
actual = {int(address) for block in blocks for address in block}
assert actual == wanted
assert len(actual) == 56
assert [str(block) for block in blocks] == [
"192.0.2.40/29", "192.0.2.48/28", "192.0.2.64/27"
]
assert not any(ip_address("192.0.2.39") in block for block in blocks)
assert not any(ip_address("192.0.2.96") in block for block in blocks)
print("Exact coverage checked:", [str(block) for block in blocks])
For large ranges, use prefix arithmetic rather than expanding billions of addresses into a set. The explicit enumeration here is a convenient independent check of these 56 addresses, not the converter’s implementation strategy.
Review the surrounding rule as well as the list
An address group is a reusable object, not an access policy by itself. The OPNsense alias documentation illustrates how lists of hosts and networks are used by rules. Match the object type to the intended address set and verify the resulting entries.
Then review the rule’s direction, interfaces, source and destination, ports, action and order. A precise source list does not make a broad destination rule appropriate. A separately existing rule can also allow traffic outside this list, so testing only the positive case is insufficient.
For this example, the address-membership checks are: .40 and .95 included;
.39 and .96 excluded. At implementation time, pair them with a permitted
application test and a negative test through the actual policy. This article
verifies the mathematical set; no live rule or reachability test is claimed.
The MikroTik address-list guide explains the distinction between membership and policy. If the task is allocating new LANs instead, use the VLSM worked example and the IPv4 subnet calculator: address allocation and exact allowlist coverage have different acceptance checks.
