# mapCIDR, the subnet arithmetic you would otherwise write in awk

> mapCIDR is a Go utility and library for the operations mass scanning needs on CIDR ranges: expansion, slicing, aggregation, matching, filtering and sorting. It is from ProjectDiscovery, it installs with a single go install, and it reads from stdin as readily as from a file argument.

**projectdiscovery/mapcidr** — Utility program to perform multiple operations for a given subnet/CIDR ranges.

- Repository: https://github.com/projectdiscovery/mapcidr
- Website: https://projectdiscovery.io
- Stars: 1,239 · Forks: 113
- Language: Go
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/projectdiscovery-mapcidr

## The subnet arithmetic a scanning pipeline needs

mapCIDR is developed to ease load distribution for mass scanning operations, and it can be used both as a library and as an independent CLI tool. That first sentence is the design brief, and it explains the feature set precisely.

The features list is a catalogue of range operations. CIDR expansion is the default behaviour, so with no operation flag the tool lists the addresses inside the range. CIDR slicing is supported with two flags, sbh to slice by host count and sbc to slice by CIDR count. CIDR and IP aggregation is a and aa, the second being the approximate variant. A CIDR and IP matcher is match-ip and a CIDR and IP filter is filter-ip. Sorting is s and sr, and host counting is count. There is multiple IP format support through ip-format, IP and port shuffling through si and sp, IPv4 and IPv6 conversion through t4 and t6, and CIDR input from standard input for piping.

Every one of those operations is something you would otherwise write as a shell script, and several of them are the kind of thing that looks simple until you try to get the boundaries right. A tool that does them consistently is worth more than the sum of its parts.

The tool installs with a single go install:

```sh
go install -v github.com/projectdiscovery/mapcidr/cmd/mapcidr@latest
```

And it is MIT licensed, with the licence in LICENSE.MD at the root. The default branch is main, the last push was on 2026-10-14, and the release tags are v1.1.97 from 2025-10-28, v1.1.96 from 2025-10-07 and v1.1.95 from 2025-09-24. The project homepage is projectdiscovery.io, and the tool is one of a family of utilities from the same group, with a Discord and a Twitter account linked in the README.

## Expansion, from an argument or from a pipe

The default operation is expansion, and the invocation is the obvious one:

```console
mapcidr -cidr 173.0.84.0/24
```

The output is a banner, two warnings, and then the addresses. The warnings are worth reading as a statement of intent from the project. Use with caution. You are responsible for your actions. And developers assume no liability and are not responsible for any misuse or damage.

The addresses themselves are the interesting part, because the default expansion is not a naive enumeration. The list runs 173.0.84.0 through 173.0.84.5, and then jumps to 173.0.84.13. The addresses that would normally be used by infrastructure, the .0 network address and the .1 to .12 range in this case, are omitted. That is a sensible default for scanning, where you want hosts and not the reserved addresses.

The second input form is a pipe, and it is where the tool fits into a pipeline. An IP range can be given on standard input:

```console
$ echo "192.168.0.0-192.168.0.5" | mapcidr
```

which expands the range to the six addresses. The README notes that the input flag also accepts a file, so the same tool handles a single CIDR, a range, a list of ranges from a file, and a list from standard input.

The host count operation is the other thing you will reach for often, because a /24 has 256 addresses and a scan budget might be 10, so count tells you what you are dealing with before you start.

## Slicing, and the power of two caveat

Slicing divides a range into smaller blocks, and there are two ways to ask for it. By CIDR count, which produces a number of subnets of a given size:

```console
mapcidr -cidr 173.0.84.0/24 -sbc 10 -silent
```

The output is a set of /27 and /28 blocks, and the shape of the result is instructive. Most of the pieces are /27, which is 32 addresses each, and the tail is made of /28 blocks. Asking for 10 equal pieces of a /24 is not exactly possible, and the tool does the closest thing it can.

By host count, which produces subnets with an equal number of hosts in each:

```console
mapcidr -cidr 173.0.84.0/16 -sbh 20000 -silent
```

That one comes out clean, four /18 blocks of 16,384 addresses each, because 20,000 hosts is close to a /18.

The README states the rule that explains both examples. It is possible to obtain a perfect split only when the desired number of slices or hosts per subnet is a power of two. Otherwise, the tool will attempt to automatically find the best split strategy to obtain the desired outcome.

That is the honest framing of what a subnet split can be. Network masks are powers of two by construction, so a /24 splits evenly into /27s or /28s and not into ten equal pieces. A tool that pretended otherwise would give you a result that does not correspond to real routing, and this one tells you when it is approximating.

The -silent flag is worth noting for scripting, since it suppresses the banner and the warnings that would otherwise be interleaved with your data.

## Aggregation, and why the approximate variant is dangerous

Aggregation is the inverse operation, merging a list of ranges back into the smallest set of subnets that covers them.

```console
$ mapcidr -cl cidrs.txt -aggregate
```

The same works on a list of individual IP addresses rather than CIDRs, and on a range given either as an argument or through a pipe. The output for a range like 192.168.0.1-192.168.0.255 is the classic ladder of prefixes, 192.168.0.1/32, 192.168.0.2/31, 192.168.0.4/30, and so on up to 192.168.0.128/25. That is the mathematically minimal cover of the range, and it is exactly the shape a firewall rule or a route table would want.

The approximate variant is the one to understand before using. It performs approximated aggregations for sparse IP groups, only version 4, and the final interval will contain contiguous IPs not belonging to the input. The example in the README makes it concrete. An input of 1.1.1.1, 1.1.1.16 and 1.1.1.31 aggregates approximately to 1.1.1.0/27, which covers 32 addresses, eleven of which were not in the input.

That is a legitimate operation for some purposes, such as producing a coarse summary or a candidate range to check, and a dangerous one for others, such as generating an allowlist or a scan target set where every extra address is a host you did not intend to touch. The IPv4-only restriction is a further limitation to note.

The exact variant has no such caveat. If the goal is to know exactly which addresses are covered, the -aggregate flag is the one to use, and the approximate flag is for when you explicitly want the coarser answer.

## Filtering, matching, and the library form

The filter and match operations work the same way, with the difference being which side the list goes on.

Matching takes the CIDR ranges as input and a list of addresses to look for:

```console
$ mapcidr -cidr 192.168.1.0/24 -mi 192.168.1.253,192.168.1.252
$ mapcidr -cidr 192.168.1.0/24 -mi ip_list_to_match.txt
```

Filtering is the same shape but removes the addresses instead of selecting them, with -fi. Both flags accept a comma separated list or a file.

There is also a set of flags that adjust what expansion produces rather than what happens to the output. -skip-base skips base IPs ending in .0, and -skip-broadcast skips broadcast addresses ending in .255, both of which matter for scanning and for port range work. There are the family filters -f4 and -f6 to keep only IPv4 or only IPv6, and the format conversion flags -t4 and -t6 to convert addresses between the two families. The ip-format flag takes a set of numbers from 0 to 11, which selects among the textual representations an address can be written in.

The zero-pad options are for enumeration work. -zpn sets the number of padded zeros to use, defaulting to 3, and -zpp enables permutations from 0 to that count for each octet. That is for producing candidate addresses in a range, which is why the tool pairs it with the port shuffling flags -si and -sp.

The library form is the other half of the project. The repository is laid out as a Go package with the core in the root, cidr.go, ip.go, shuffle.go, type.go, urlencode.go, utils.go and error.go, an asn/ subpackage, cmd/ for the CLI, and both unit tests and an integration_tests/ directory. The go.mod shows the dependency set, including projectdiscovery/ipranger for the range operations, goflags for the command line parsing, gologger for the output, asnmap for the ASN lookups, and aurora for the colouring in the banner.

## Conclusion

mapCIDR suits anyone who works with address ranges as part of a larger scanning or inventory pipeline, since it removes the shell scripting that otherwise turns a /24 into a list, and it suits Go projects that want the same operations as a library. It is the wrong tool for a one-off calculation you can do in your head, because installing a binary for that is overhead, and it is the wrong tool if you need routing or BGP functionality, because it does not talk to any routing system. Before using it, read the slicing note, because a perfect split only happens when the requested count or host count is a power of two, and check the aggregation flags, since the approximate variant is IPv4 only and will include addresses that were not in your input.

## FAQ

### How do I install mapCIDR and list the addresses in a range?

Run go install -v github.com/projectdiscovery/mapcidr/cmd/mapcidr@latest, then mapcidr -cidr 173.0.84.0/24 to expand the range. A range can also be piped in, for example echo "192.168.0.0-192.168.0.5" | mapcidr, and the -cl flag accepts a file of CIDRs.

### How do I split a CIDR into smaller subnets?

Use -sbc to slice by a number of CIDR blocks, for example mapcidr -cidr 173.0.84.0/24 -sbc 10, or -sbh to slice by an equal number of hosts, for example mapcidr -cidr 173.0.84.0/16 -sbh 20000. A perfect split is only possible when the requested count is a power of two.

### What is the difference between aggregate and aggregate-approx?

The -aggregate flag merges ranges into the minimum exact subnets that cover the input. The -aggregate-approx flag works on sparse IPv4 groups and produces an interval that contains contiguous addresses which were not in your input, so it is for coarse summaries rather than exact allowlists.

### Can mapCIDR be used as a Go library?

Yes, the README states it can be used both as a library and as an independent CLI tool. The repository is a Go module with the core in the root package and an asn/ subpackage, with the CLI in cmd/.

### How do I match or filter specific addresses inside a range?

Use -mi to match a list of IPs against the input ranges and -fi to filter them out, both accepting a comma separated list or a file. The -skip-base and -skip-broadcast flags drop addresses ending in .0 and .255 from the output, and -f4 and -f6 filter by address family.

## Sources

- [License: MIT](https://github.com/projectdiscovery/mapcidr/blob/main/LICENSE)
- [projectdiscovery/mapcidr on GitHub](https://github.com/projectdiscovery/mapcidr)
- [Project website](https://projectdiscovery.io)
- [README](https://github.com/projectdiscovery/mapcidr/blob/main/README.md)
- [Releases](https://github.com/projectdiscovery/mapcidr/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/projectdiscovery-mapcidr
