This is a small program that I wrote to include in a networking library for work so that we could do some quick-ish network checks & generate network ranges for IP addresses found in the config file. One important part was ensuring that we could read & generate a network range for IPs entered either by themselves(assuming a /32), with CIDR or with dotted-quad netmask.
/* cidr.c - v1.5 : 2013-07-17 - Xires <xires012@gmail.com>
*
* COMPILE: gcc -Wall -Wextra -W -ansi -o cidr{,.c}
*
* RUN:
* ./cidr <IPADDR>[/(CIDR|NETMASK)]
* IPADDR = dotted-quad IP address
* CIDR = CIDR mask
* NETMASK = dotted-quad netmask
*
* NOTE:
* -pedantic cannot be used because of the %hh type-conversion specifier
* This code tries to maintain network-byte-order (big-endian).
* This code is meant to be run on a little-endian system.
*/
#include <stdio.h> /* for I/O */
#include <stdint.h> /* uint8_t */
#include <arpa/inet.h> /* ntohl() */
/* msk2int():
* shift 0xFFFFFFFF(32 'on' bits) right by 'n' places
* return the negation('on' bits become 'off', 'off' bits become 'on')
*
* 11111111 11111111 11111111 11111111 >> 24
* 00000000 00000000 00000000 11111111
*
*~00000000 00000000 00000000 11111111
* 11111111 11111111 11111111 00000000
*/
#define msk2int(n) ((unsigned int) ~(0xFFFFFFFFUL >> n))
#define cnt_bits(n) seander(n) /* modularity; replace seander() with new alg */
/* a union lets us represent the same section of memory in multiple ways */
union ipv4_u { /* represents an IPv4 address */
uint8_t oct[4]; /* each octet of dotted-quad representation */
unsigned int ipl; /* single 32-bit representation */
};
typedef struct cidr_s { /* a netblock */
union ipv4_u ipv4; /* IP address */
union ipv4_u mask; /* netmask */
union ipv4_u bgn; /* beginning of network */
union ipv4_u end; /* end/broadcast of network */
} cidr_t;
/* Seander is a known bit-counting algorithm */
unsigned int seander(unsigned int n) { /* counts the bits that are 'on' */
n = n - ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
return(
(
(
(
n + (n >> 4)
) & 0xF0F0F0F
) * 0x1010101
) >> 24
);
}
int main(int argc, char **argv) {
int cnt = 0; /* number of items read from sscanf() operation */
cidr_t cidr; /* the netblock we're working with */
if (argc < 2) return (fprintf(stderr, "Must supply CIDR argument.\n"), 1);
cnt = sscanf( /* sscanf() returns number of input items read */
argv[1],
"%hhu.%hhu.%hhu.%hhu/%hhu.%hhu.%hhu.%hhu",
&cidr.ipv4.oct[3],
&cidr.ipv4.oct[2],
&cidr.ipv4.oct[1],
&cidr.ipv4.oct[0],
&cidr.mask.oct[3],
&cidr.mask.oct[2],
&cidr.mask.oct[1],
&cidr.mask.oct[0]
); /* maybe reverse scan order for big-endian systems */
switch (cnt) { /* check the number of arguments read */
case 4:
cidr.mask.ipl = 32; /* A.B.C.D */
case 5: /* A.B.C.D/CIDR */
cidr.mask.ipl = msk2int(ntohl(cidr.mask.ipl)); /* reinterpret mask */
break;
case 8: /* A.B.C.D/W.X.Y.Z */
break;
default: /* anything else */
return (fprintf(stderr, "Invalid CIDR argument: %u\n", cnt), ~cnt);
}
cidr.bgn.ipl = (cidr.ipv4.ipl & cidr.mask.ipl); /* only keep useful bits */
cidr.end.ipl = ((cidr.ipv4.ipl & cidr.mask.ipl) | ~cidr.mask.ipl); /* max */
/* test reports */
printf( /* spit everything out, in proper order */
"IP:\t%u.%u.%u.%u\nMsk:\t%u.%u.%u.%u\nBgn:\t%u.%u.%u.%u\nEnd:\t%u.%u.%u.%u\n",
cidr.ipv4.oct[3],
cidr.ipv4.oct[2],
cidr.ipv4.oct[1],
cidr.ipv4.oct[0],
cidr.mask.oct[3],
cidr.mask.oct[2],
cidr.mask.oct[1],
cidr.mask.oct[0],
cidr.bgn.oct[3],
cidr.bgn.oct[2],
cidr.bgn.oct[1],
cidr.bgn.oct[0],
cidr.end.oct[3],
cidr.end.oct[2],
cidr.end.oct[1],
cidr.end.oct[0]
);
printf( /* spit out unsigned int representations; big-endian */
"IP:\t%u\nMsk:\t%u\nBgn:\t%u\nEnd:\t%u\n",
cidr.ipv4.ipl,
cidr.mask.ipl,
cidr.bgn.ipl,
cidr.end.ipl
);
printf( /* output possible CIDR entries for conf file */
"conf:\t%u.%u.%u.%u/%u\n\t%u.%u.%u.%u/%u\n",
cidr.ipv4.oct[3],
cidr.ipv4.oct[2],
cidr.ipv4.oct[1],
cidr.ipv4.oct[0],
cnt_bits(cidr.mask.ipl),
cidr.bgn.oct[3],
cidr.bgn.oct[2],
cidr.bgn.oct[1],
cidr.bgn.oct[0],
cnt_bits(cidr.mask.ipl)
);
return 0;
}