299 lines
6.6 KiB
C
299 lines
6.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
#include <error.h>
|
|
#include <errno.h>
|
|
|
|
#include "config.h"
|
|
|
|
#define IP4LEN 32
|
|
#define IP6LEN 128
|
|
#define IPMAXLEN IP6LEN
|
|
|
|
struct in4_addr {
|
|
unsigned char s4_addr[4];
|
|
};
|
|
|
|
struct node { pos_t a[2]; };
|
|
typedef struct node *iptree;
|
|
typedef int *bits;
|
|
typedef void (*bitsfun)(bits, mask_t);
|
|
|
|
pos_t maxpos = 0;
|
|
|
|
iptree iptree_new(pos_t size) {
|
|
iptree t = malloc(size * sizeof(struct node));
|
|
if (t == 0) error(1, errno, "iptree_new: malloc failed");
|
|
pos_t i;
|
|
for(i = 0; i < size; ++i) {
|
|
t[i].a[0] = i + 1;
|
|
t[i].a[1] = 0;
|
|
}
|
|
t[i].a[0] = 0;
|
|
return t;
|
|
}
|
|
|
|
pos_t iptree_popfree(iptree t) {
|
|
pos_t f = t[0].a[0];
|
|
if (f > maxpos) maxpos = f;
|
|
if (!f) error(1, 0, "iptree_popfree: no free nodes left");
|
|
t[0].a[0] = t[f].a[0];
|
|
t[f].a[0] = 0;
|
|
return f;
|
|
}
|
|
|
|
void iptree_pushfree(iptree t, pos_t f) {
|
|
t[f].a[0] = t[0].a[0];
|
|
t[f].a[1] = 0;
|
|
t[0].a[0] = f;
|
|
}
|
|
|
|
int iptree_is_term(iptree t, pos_t h) {
|
|
return (h && !t[h].a[0] && !t[h].a[1]);
|
|
}
|
|
|
|
void iptree_sub_del(iptree t, pos_t h) {
|
|
for (int i = 0; i < 2; ++i) {
|
|
int j = t[h].a[i];
|
|
if (j) iptree_sub_del(t, j);
|
|
}
|
|
iptree_pushfree(t, h);
|
|
}
|
|
|
|
void iptree_add_new(iptree t, pos_t h, bits b, mask_t m) {
|
|
if (m) {
|
|
// there are bits left
|
|
int l = b[0]; // our bit is left
|
|
int r = 1 - l; // sibling bit is right
|
|
// allocate next node element
|
|
pos_t jl = iptree_popfree(t);
|
|
// fill current node
|
|
t[h].a[l] = jl;
|
|
// recurse to add other bits
|
|
iptree_add_new(t, jl, b + 1, m - 1);
|
|
} else {
|
|
// no bits left, this node is terminal
|
|
t[h].a[0] = 0;
|
|
t[h].a[1] = 0;
|
|
}
|
|
}
|
|
|
|
void iptree_add_rec(iptree t, pos_t h, bits b, mask_t m) {
|
|
if (m) {
|
|
// bits to check are left
|
|
int l = b[0]; // our bit is left
|
|
int r = 1 - l; // sibling bit is right
|
|
pos_t jl = t[h].a[l]; // left subtree head
|
|
pos_t jr = t[h].a[r]; // right subtree head
|
|
if (jl) {
|
|
// tree for our bit is not empty, recurse
|
|
iptree_add_rec(t, jl, b + 1, m - 1);
|
|
} else {
|
|
// tree for our bit is missing
|
|
// check if this element is terminal (less specific here)
|
|
if (!jr) return;
|
|
// otherwise fill new subtree
|
|
iptree_add_new(t, h, b, m);
|
|
}
|
|
// check if we can fold
|
|
if (iptree_is_term(t, t[h].a[0]) && iptree_is_term(t, t[h].a[1])) {
|
|
for (int i = 0; i < 2; ++i) {
|
|
iptree_pushfree(t, t[h].a[i]);
|
|
t[h].a[i] = 0;
|
|
}
|
|
}
|
|
} else {
|
|
// no bits left, this node must be terminal
|
|
for (int i = 0; i < 2; ++i) {
|
|
pos_t j = t[h].a[i]; // subtree head
|
|
// delete subtree if exist
|
|
if (j) {
|
|
iptree_sub_del(t, j);
|
|
t[h].a[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void iptree_add(iptree t, pos_t h, bits b, mask_t m) {
|
|
if (t[h].a[0]) {
|
|
// tree is not empty already
|
|
iptree_add_rec(t, t[h].a[0], b, m);
|
|
} else {
|
|
// tree is empty, initialize it
|
|
t[h].a[0] = iptree_popfree(t);
|
|
iptree_add_new(t, t[h].a[0], b, m);
|
|
}
|
|
}
|
|
|
|
void iptree_traverse_rec(iptree t, bitsfun f, pos_t h, bits b, mask_t m) {
|
|
int flag = 1;
|
|
for (int i = 0; i < 2; ++i) {
|
|
pos_t j = t[h].a[i];
|
|
// if subtree exists
|
|
if (j) {
|
|
flag = 0; // node is not terminal
|
|
b[m] = i; // set bit
|
|
// recurse
|
|
iptree_traverse_rec(t, f, j, b, m + 1);
|
|
}
|
|
}
|
|
if (flag) {
|
|
// terminal node
|
|
f(b, m);
|
|
}
|
|
}
|
|
|
|
void iptree_traverse(iptree t, pos_t h, bitsfun f) {
|
|
int b[IPMAXLEN];
|
|
if (t[h].a[0]) {
|
|
// tree is not empty
|
|
iptree_traverse_rec(t, f, t[h].a[0], b, 0);
|
|
}
|
|
}
|
|
|
|
void bits2ip4(bits b, mask_t m, char *s, int size) {
|
|
// convert bits to in4_addr
|
|
struct in4_addr ip;
|
|
for (int i = 0; i < 4; ++i) {
|
|
ip.s4_addr[i] = 0;
|
|
for (int j = 0; j < 8; ++j) {
|
|
ip.s4_addr[i] <<= 1;
|
|
int p = i*8 + j;
|
|
ip.s4_addr[i] |= (p >= m) ? 0 : b[p];
|
|
}
|
|
}
|
|
// get test representation of ip
|
|
char s_ip[INET_ADDRSTRLEN];
|
|
if (!inet_ntop(AF_INET, &ip, s_ip, INET_ADDRSTRLEN)) error(1, errno, "bits2ip4");
|
|
// print ip with mask to buffer
|
|
int res = snprintf(s, size, "%s/%d", s_ip, m);
|
|
// if printing to buffer has failed
|
|
if (res < 0 || res >= size) error(1, errno, "bits2ip4");
|
|
}
|
|
|
|
void bits2ip6(bits b, mask_t m, char *s, int size) {
|
|
// convert bits to in6_addr
|
|
struct in6_addr ip;
|
|
for (int i = 0; i < 16; ++i) {
|
|
ip.s6_addr[i] = 0;
|
|
for (int j = 0; j < 8; ++j) {
|
|
ip.s6_addr[i] <<= 1;
|
|
int p = i*8 + j;
|
|
ip.s6_addr[i] |= (p >= m) ? 0 : b[p];
|
|
}
|
|
}
|
|
// get test representation of ip
|
|
char s_ip[INET6_ADDRSTRLEN];
|
|
if (!inet_ntop(AF_INET6, &ip, s_ip, INET6_ADDRSTRLEN)) error(1, errno, "bits2ip6");
|
|
// print ip with mask to buffer
|
|
int res = snprintf(s, size, "%s/%d", s_ip, m);
|
|
// if printing to buffer has failed
|
|
if (res < 0 || res >= size) error(1, errno, "bits2ip6");
|
|
}
|
|
|
|
void ip42bits(char *s, bits b, mask_t *m) {
|
|
// split ip and mask
|
|
char *p = strchr(s, '/');
|
|
if (p) {
|
|
*p = 0;
|
|
++p;
|
|
unsigned long l = strtol(p, &p, 10);
|
|
if (*p || l > IP4LEN) error(1, errno, "ip42bits: bad mask");
|
|
*m = l;
|
|
} else {
|
|
*m = IP4LEN;
|
|
}
|
|
// convert ip to binary
|
|
struct in4_addr ip;
|
|
if (!inet_pton(AF_INET, s, &ip)) error(1, errno, "ip42bits");
|
|
// convert binary ip to bits
|
|
for (int i = 0; i < 4; ++i) {
|
|
for (int j = 0; j < 8; ++j) {
|
|
int p = i*8 + 8-1-j;
|
|
b[p] = (p > *m) ? 0 : (ip.s4_addr[i] & 1);
|
|
ip.s4_addr[i] >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ip62bits(char *s, bits b, mask_t *m) {
|
|
// split ip and mask
|
|
char *p = strchr(s, '/');
|
|
if (p) {
|
|
*p = 0;
|
|
++p;
|
|
unsigned long l = strtol(p, &p, 10);
|
|
if (*p || l > IP6LEN) error(1, errno, "ip62bits: bad mask");
|
|
*m = l;
|
|
} else {
|
|
*m = IP6LEN;
|
|
}
|
|
// convert ip to binary
|
|
struct in6_addr ip;
|
|
if (!inet_pton(AF_INET6, s, &ip)) error(1, errno, "ip62bits");
|
|
// convert binary ip to bits
|
|
for (int i = 0; i < 16; ++i) {
|
|
for (int j = 0; j < 8; ++j) {
|
|
int p = i*8 + 8-1-j;
|
|
b[p] = (p > *m) ? 0 : (ip.s6_addr[i] & 1);
|
|
ip.s6_addr[i] >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void print4(bits b, mask_t m) {
|
|
char s_ip[BUFLEN];
|
|
bits2ip4(b, m, s_ip, BUFLEN);
|
|
puts(s_ip);
|
|
}
|
|
|
|
void print6(bits b, mask_t m) {
|
|
char s_ip[BUFLEN];
|
|
bits2ip6(b, m, s_ip, BUFLEN);
|
|
puts(s_ip);
|
|
}
|
|
|
|
double nodes2mb(pos_t n) {
|
|
return sizeof(struct node) * (double)n / (double)(1 << 20);
|
|
}
|
|
|
|
int main(void) {
|
|
int b[IPMAXLEN];
|
|
mask_t m;
|
|
char s[BUFLEN];
|
|
|
|
iptree t = iptree_new(SIZE);
|
|
pos_t ip4 = iptree_popfree(t);
|
|
pos_t ip6 = iptree_popfree(t);
|
|
|
|
while (1) {
|
|
if (!fgets(s, BUFLEN, stdin)) {
|
|
if (!feof(stdin)) error(1, errno, "input");
|
|
break;
|
|
}
|
|
int l = strlen(s);
|
|
if (!l) continue;
|
|
if (s[l - 1] == '\n') s[l - 1] = 0;
|
|
if (!strchr(s, ':')) {
|
|
// ipv4
|
|
ip42bits(s, b, &m);
|
|
iptree_add(t, ip4, b, m);
|
|
} else {
|
|
// ipv6
|
|
ip62bits(s, b, &m);
|
|
iptree_add(t, ip6, b, m);
|
|
}
|
|
}
|
|
|
|
iptree_traverse(t, ip4, print4);
|
|
iptree_traverse(t, ip6, print6);
|
|
|
|
double mb_used = nodes2mb(maxpos);
|
|
double mb_all = nodes2mb(SIZE);
|
|
fprintf(stderr, "max nodes: %.2f%%, %d / %d, %.2fMB / %.2fMB\n", maxpos / (double)SIZE * 100.0, maxpos, SIZE, mb_used, mb_all);
|
|
|
|
return 0;
|
|
}
|