Created
October 3, 2023 11:45
-
-
Save vmxdev/2592304836c542c1e6022e2589fc0f64 to your computer and use it in GitHub Desktop.
Split a range of IP addresses into subnets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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