UPDATE: netcalc has a newer version available.
tl;dr: download netcalc—an IPv4 and IPv6 subnet calculator—to make subnetting easier.
In one of my Computer Science units last year we studied subnetting. It was really interesting but also highly programmatic. So like any good CS student, when faced with a repetitive problem to solve—such as calculating subnets—you automate the process; which I did! I first wrote a program in Python but then decided to create one in C. It only provided IPv4 functionality, though, as that's all we worked on at university. More recently, however, I thought I'd expand it to include IPv6; C makes this pretty simple with the inet_pton and inet_ntop functions. I've avoided using atoi because its use has been discouraged due to its inherent vulnerability for quite awhile now. I would've used OpenBSD's strtonum implementation but wanted to use the calculator on macOS, too, so settled for strtol. The string to long function incorporates error checking to help protect against under- and over-flows, which is an encouraged best practice. It might seem overly convoluted at first, but breaks down into an easily understood logical sequence:
long num; /* integer to hold converted number */ char str; /* character array of string to convert */ char *ptr; /* points to first non-digit char in str */ errno = 0; /* reset error number */ num = strtol(str, &ptr, 10); if (errno == ERANGE) printf("error: overflow\n"); else if (errno != 0) printf("error: string not a number\n"); else if (*ptr != '\0') printf("invalid characters in string\n"); else printf("success!"\n);
First, strtol is passed three arguments: (1) the string str containing the number to be converted; (2) the address of the pointer &ptr that points to the end of the string when converted; and (3) the base number system (i.e., 10, in this case, for decimal). Second, the input is subjected to a series of tests: (1), if the number is too small or large, errno is set to ERANGE; (2) if str isn't an actual number, errno will be set to some error code other than 0; and (3) if the pointer ptr at the end of str doesn't point to a null character, there was invalid input in the string. This covers all bases. OpenBSD, however, has distilled this error detection during number conversion process into the much simpler strtonum(3) function, which is equally, if not more, robust—and worth further perusal.
In any case, it's a quick and simple command line app that is easy to use. At the moment, multiple subnets can be requested for a given IPv4 CIDR address:
$ ./netcalc 192.168.1.200/26 -s2 Address : 192.168.1.200 CIDR notation : 192.168.1.192/26 Address range : 192.168.1.193 —— 192.168.1.254 Broadcast address : 192.168.1.255 Subnet mask : 255.255.255.192 [0xffffffc0] Address block : 62 contiguous hosts [+] subnets requested: 2 [+] subnets delivered: 2 -> subnet 1 CIDR notation : 192.168.1.192/27 Address range : 192.168.1.193 —— 192.168.1.222 Broadcast address : 192.168.1.223 Subnet mask : 255.255.255.224 [0xffffffe0] Address block : 30 contiguous hosts -> subnet 2 CIDR notation : 192.168.1.224/27 Address range : 192.168.1.225 —— 192.168.1.254 Broadcast address : 192.168.1.255 Subnet mask : 255.255.255.224 [0xffffffe0] Address block : 30 contiguous hosts
Or a single network when using IPv6:
$ ./netcalc -6 c7a9:2ce5:3425:8045:7777:67b4:cd58:7494/114 Address : c7a9:2ce5:3425:8045:7777:67b4:cd58:7494 CIDR notation : c7a9:2ce5:3425:8045:7777:67b4:cd58:4000/114 Start address : c7a9:2ce5:3425:8045:7777:67b4:cd58:4000 End address : c7a9:2ce5:3425:8045:7777:67b4:cd58:7fff Block size: : 16,384 contiguous usable hosts
The subnet division feature for IPv6 should be added in the future. But for now, I'm working on a home alarm project and have enough schoolwork to keep busy.
The program is called netcalc, and is released under the BSD-styled ISC license. The source code can be downloaded and built locally, or there is a binary executable available. It needs to be compiled with the math library linked when building on OpenBSD:
ftp -S do https://jamsek.dev/resources/pub/netcalc/netcalc.c cc -Werror -Wall -o netcalc netcalc.c -lm ./netcalc netcalc 0.2 IPv4 & IPv6 CIDR Subnet Calculator usage: netcalc [-s subnets | -6] ipaddr/prefix e.g.: netcalc -s 4 192.168.0.1/24 netcalc -6 8c6b:dbfd:5c73:8f14:f815:a4a2:5dab:38b0/110
On macOS you can drop the -lm.
Alternatively, download and untar the appropriate tarball before running make on either macOS or OpenBSD.
# macOS curl -O https://jamsek.dev/resources/pub/netcalc/netcalc_macos.tar.gz tar -zxvf netcalc_macos.tar.gz # OpenBSD ftp -S do https://jamsek.dev/resources/pub/netcalc/netcalc_openbsd.tar.gz tar -zxvf netcalc_openbsd.tar.gz # both macOS and OpenBSD cd netcalc/src make cc -c -o obj/main.o main.c -I../include -Werror -Wall cc -c -o obj/netcalc.o netcalc.c -I../include -Werror -Wall cc -o netcalc obj/main.o obj/netcalc.o -I../include -Werror -Wall -lm # macOS ./netcalc # OpenBSD cd obj && ./netcalc netcalc 0.2 IPv4 & IPv6 CIDR Subnet Calculator usage: netcalc [-s subnets | -6] ipaddr/prefix e.g.: netcalc -s 4 192.168.0.1/24 netcalc -6 8c6b:dbfd:5c73:8f14:f815:a4a2:5dab:38b0/110
It's only been tested on OpenBSD 6.3, 6.4, 6.5, & 6.6, and macOS Mojave 10.14 & Catalina 10.15, but should be compatible on other BSDs (or even Linux).
Let me know if you find it useful!