Skip to content

Instantly share code, notes, and snippets.

@vmxdev
Created October 3, 2023 11:45
Show Gist options
  • Save vmxdev/2592304836c542c1e6022e2589fc0f64 to your computer and use it in GitHub Desktop.
Save vmxdev/2592304836c542c1e6022e2589fc0f64 to your computer and use it in GitHub Desktop.
Split a range of IP addresses into subnets
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
static void
print_cidr(uint32_t addr, int mask)
{
uint32_t tmp_addr;
char s[INET6_ADDRSTRLEN + 1];
tmp_addr = htobe32(addr);
inet_ntop(AF_INET, &tmp_addr, s, INET_ADDRSTRLEN);
printf("%s/%d\n", s, mask);
}
int
main(int argc, char *argv[])
{
uint32_t start, end;
uint32_t subnet_first, subnet_last;
uint32_t tmp_addr;
int rc;
/* processing command line */
if (argc < 3) {
fprintf(stderr, "Usage: %s IP1 IP2\n", argv[0]);
return EXIT_FAILURE;
}
rc = inet_pton(AF_INET, argv[1], &tmp_addr);
if (rc != 1) {
fprintf(stderr, "Can't parse address '%s': %s\n",
argv[1], strerror(errno));
return EXIT_FAILURE;
}
start = be32toh(tmp_addr);
rc = inet_pton(AF_INET, argv[2], &tmp_addr);
if (rc != 1) {
fprintf(stderr, "Can't parse address '%s': %s\n",
argv[2], strerror(errno));
return EXIT_FAILURE;
}
end = be32toh(tmp_addr);
/* IP range is split into several subnets in loop */
subnet_first = start;
for (;;) {
if (subnet_first > end) {
break;
} else if (subnet_first == end) {
print_cidr(subnet_first, 32);
break;
}
/* calculate last address in subnet
* for example if subnet_first == 192.168.1.0 last address
* (subnet_last) will be 192.168.1.255
*/
int mask_bits = __builtin_ctz(subnet_first);
subnet_last = subnet_first + (1 << mask_bits) - 1;
if (subnet_last == end) {
/* end of subnet is exactly the end of user requested
* range, for example 192.168.1.255 == 192.168.1.255 */
print_cidr(subnet_first, 32 - mask_bits);
break;
} else if (subnet_last > end) {
/* user requested range is less then subnet
* say if user needs 192.168.1.0 - 192.168.1.88,
* but subnet is 192.168.1.0 - 192.168.1.255 */
/* calculate difference between addressed first,
* for our example it will be (88 - 0 + 1) */
uint32_t diff = end - subnet_first + 1;
/* get mask of smaller subnet that fits in range
* for our example subnet will be 192.168.1.64/28 */
int p = 32 - __builtin_clz(diff) - 1;
/* get size of smaller subnet (64) */
uint32_t ndiff = 1 << p;
print_cidr(subnet_first, 32 - p);
/* continue with adjusted range
* 192.168.1.64-192.168.1.88 */
subnet_first += ndiff;
} else {
/* user requested range is bigger then subnet */
print_cidr(subnet_first, 32 - mask_bits);
/* continue with adjusted range */
subnet_first = subnet_last + 1;
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment