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[256];      /* 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/246 -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!


Comments

comments powered by Disqus