342 lines
7.7 KiB
C
342 lines
7.7 KiB
C
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "matcher.h"
|
|
#include "vbuf.h"
|
|
|
|
static void
|
|
fclose_ptr(FILE** p)
|
|
{
|
|
if (!p || !*p)
|
|
return;
|
|
fclose(*p);
|
|
*p = NULL;
|
|
}
|
|
|
|
typedef struct {
|
|
bool obsolete;
|
|
uint32_t ip;
|
|
uint8_t width;
|
|
} iprange_t;
|
|
|
|
static pbuf_t black_ranges;
|
|
static pbuf_t white_ranges;
|
|
static pbuf_t new_ranges;
|
|
static pbuf_t new_b_ranges;
|
|
|
|
static iprange_t*
|
|
iprange_create(uint32_t ip, uint8_t width)
|
|
{
|
|
iprange_t* const range = malloc(sizeof(*range));
|
|
*range = (iprange_t) {
|
|
.ip = ip & ~0U << (32 - width),
|
|
.width = width,
|
|
};
|
|
return range;
|
|
}
|
|
|
|
static void
|
|
iprange_add(const char* ipstr, uint8_t width, bool white)
|
|
{
|
|
struct in_addr addr;
|
|
int const rc = inet_pton(AF_INET, ipstr, &addr);
|
|
if (!rc) return;
|
|
#if 0
|
|
printf("%s: inet_pton `%s' => %d.%d.%d.%d\n",
|
|
__FUNCTION__,
|
|
ipstr,
|
|
addr.s_addr >> 24,
|
|
(addr.s_addr >> 16) & 0xff,
|
|
(addr.s_addr >> 8) & 0xff,
|
|
(addr.s_addr >> 0) & 0xff);
|
|
#endif
|
|
iprange_t* const range = iprange_create(ntohl(addr.s_addr), width);
|
|
pbuf_append(white ? &white_ranges : &black_ranges, range);
|
|
}
|
|
|
|
static void
|
|
iprange_load(const char* filename, bool white)
|
|
{
|
|
FILE* __attribute__((cleanup(fclose_ptr))) fp = fopen(filename, "r");
|
|
if (!fp)
|
|
return;
|
|
|
|
regexp_matcher_t __attribute__((cleanup(matcher_destroy))) matcher;
|
|
matcher_init_regexp(&matcher,
|
|
"([0-9\\.]+)" // ipv4
|
|
"("
|
|
"/([0-9]+)" // optional width
|
|
")?");
|
|
|
|
vbuf_t __attribute__((cleanup(vbuf_freeptr))) vbuf;
|
|
vbuf_init0(&vbuf);
|
|
|
|
int len;
|
|
while ((len = vbuf_gets(&vbuf, fp)) > -1) {
|
|
char* const line = vbuf_tostring(&vbuf);
|
|
int rc = matcher_match(&matcher, line);
|
|
// printf("matcher on `%s' returned %d\n", line, rc);
|
|
if (rc < 2)
|
|
continue;
|
|
switch (rc) {
|
|
case 2:
|
|
iprange_add(matcher_result(&matcher, 1), 32, white);
|
|
break;
|
|
case 4:
|
|
iprange_add(
|
|
matcher_result(&matcher, 1),
|
|
atoi(matcher_result(&matcher, 3)),
|
|
white);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
iprange_cmp(const void* a, const void* b)
|
|
{
|
|
const iprange_t* const r_a = *((iprange_t**)a);
|
|
const iprange_t* const r_b = *((iprange_t**)b);
|
|
|
|
if (r_a->ip < r_b->ip)
|
|
return -1;
|
|
if (r_a->ip > r_b->ip)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
iprange_join(iprange_t* prev, iprange_t* curr)
|
|
{
|
|
uint32_t const prev_mask = ~0U << (32 - prev->width);
|
|
#if 0
|
|
uint32_t const curr_mask = ~0U << (32 - curr->width);
|
|
printf("masks: prev 0x%08x curr 0x%08x\n", prev_mask, curr_mask);
|
|
printf("masked: prev 0x%08x curr 0x%08x\n", prev->ip & prev_mask, curr->ip & curr_mask);
|
|
#endif
|
|
if ((prev->ip & prev_mask) == (curr->ip & prev_mask)) {
|
|
curr->obsolete = true;
|
|
return;
|
|
}
|
|
int min_width = prev->width < curr->width ? prev->width : curr->width;
|
|
if (min_width < 8)
|
|
min_width = 8;
|
|
int const max_width = prev->width > curr->width ? prev->width : curr->width;
|
|
// printf("widths: min %d max %d\n", min_width, max_width);
|
|
if (max_width - min_width > 8) {
|
|
// printf("width difference %d, too much\n", max_width - min_width);
|
|
return;
|
|
}
|
|
int new_width = max_width;
|
|
uint32_t new_mask = 0;
|
|
for (;;) {
|
|
if ((new_width <= 8) || (max_width - new_width > 8)) {
|
|
// printf("nothing in common with at least 8 bits\n");
|
|
return;
|
|
}
|
|
new_width = new_width - 1;
|
|
new_mask = ~0U << (32 - new_width);
|
|
uint32_t const prev_masked = prev->ip & new_mask;
|
|
uint32_t const curr_masked = curr->ip & new_mask;
|
|
#if 0
|
|
printf("new masked with width %d: prev 0x%08x curr 0x%08x\n",
|
|
new_width, prev_masked, curr_masked);
|
|
#endif
|
|
if (prev_masked == curr_masked)
|
|
break;
|
|
if (!(new_width & 7)) {
|
|
// printf("too far apart, giving up\n");
|
|
return;
|
|
}
|
|
}
|
|
uint32_t const new_ip = prev->ip & new_mask;
|
|
iprange_t* const range = iprange_create(new_ip, new_width);
|
|
pbuf_append(&new_ranges, range);
|
|
prev->obsolete = true;
|
|
curr->obsolete = true;
|
|
}
|
|
|
|
static void
|
|
ipranges_merge(void)
|
|
{
|
|
iprange_t* prev = NULL;
|
|
iprange_t* curr;
|
|
PBUF_FOREACH(curr, &black_ranges)
|
|
if (!prev) {
|
|
prev = curr;
|
|
continue;
|
|
}
|
|
#if 0
|
|
printf("comparing %d.%d.%d.%d/%d and %d.%d.%d.%d/%d\n",
|
|
prev->ip >> 24,
|
|
(prev->ip >> 16) & 0xff,
|
|
(prev->ip >> 8) & 0xff,
|
|
(prev->ip >> 0) & 0xff,
|
|
prev->width,
|
|
curr->ip >> 24,
|
|
(curr->ip >> 16) & 0xff,
|
|
(curr->ip >> 8) & 0xff,
|
|
(curr->ip >> 0) & 0xff,
|
|
curr->width);
|
|
#endif
|
|
iprange_join(prev, curr);
|
|
if (!curr->obsolete)
|
|
prev = curr;
|
|
PBUF_FOREACH_END
|
|
}
|
|
|
|
static void
|
|
iprange_maybe_add_b_block(uint32_t counter, uint32_t last_block)
|
|
{
|
|
if (counter < 4)
|
|
return;
|
|
#if 0
|
|
printf("found %d ips in block %d.%d.0.0/16\n",
|
|
counter,
|
|
last_block >> 24,
|
|
(last_block >> 16) & 0xff);
|
|
#endif
|
|
iprange_t* const range = iprange_create(last_block, 16);
|
|
pbuf_append(&new_b_ranges, range);
|
|
}
|
|
|
|
static int
|
|
iprange_match_block(void* obj, const void* arg)
|
|
{
|
|
iprange_t* const block = obj;
|
|
const iprange_t* const curr = arg;
|
|
|
|
if (curr->width < block->width)
|
|
return 0;
|
|
uint32_t const block_mask = ~0U << (32 - block->width);
|
|
if ((block->ip & block_mask) != (curr->ip & block_mask))
|
|
return 0;
|
|
#if 0
|
|
printf("ip %d.%d.%d.%d/%d is within block %d.%d.%d.%d/%d\n",
|
|
curr->ip >> 24,
|
|
(curr->ip >> 16) & 0xff,
|
|
(curr->ip >> 8) & 0xff,
|
|
(curr->ip >> 0) & 0xff,
|
|
curr->width,
|
|
block->ip >> 24,
|
|
(block->ip >> 16) & 0xff,
|
|
(block->ip >> 8) & 0xff,
|
|
(block->ip >> 0) & 0xff,
|
|
block->width);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
ipranges_find_b_blocks(void)
|
|
{
|
|
uint32_t last_block = 0;
|
|
uint32_t counter = 0;
|
|
uint32_t b_mask = 0xffff0000;
|
|
iprange_t* curr;
|
|
PBUF_FOREACH(curr, &black_ranges)
|
|
if (curr->obsolete)
|
|
continue;
|
|
uint32_t const curr_block = curr->ip & b_mask;
|
|
if (curr_block == last_block) {
|
|
counter++;
|
|
continue;
|
|
}
|
|
iprange_maybe_add_b_block(counter, last_block);
|
|
last_block = curr_block;
|
|
counter = 1;
|
|
PBUF_FOREACH_END
|
|
iprange_maybe_add_b_block(counter, last_block);
|
|
if (pbuf_size(&new_b_ranges) == 0)
|
|
return;
|
|
|
|
// cleanup
|
|
PBUF_FOREACH(curr, &black_ranges)
|
|
if (curr->obsolete)
|
|
continue;
|
|
iprange_t* const b_block = pbuf_find(&new_b_ranges, iprange_match_block, curr);
|
|
if (b_block)
|
|
curr->obsolete = true;
|
|
PBUF_FOREACH_END
|
|
}
|
|
|
|
int
|
|
main(int argc, const char* argv[])
|
|
{
|
|
pbuf_init(&black_ranges, 4, 0, NULL);
|
|
pbuf_init(&white_ranges, 4, 0, NULL);
|
|
pbuf_init(&new_ranges, 4, 0, NULL);
|
|
pbuf_init(&new_b_ranges, 4, 0, NULL);
|
|
|
|
iprange_load("autofw.whitelist", true);
|
|
iprange_load("autofw.blacklist", false);
|
|
|
|
(void) argc;
|
|
(void) argv;
|
|
|
|
pbuf_sort(&black_ranges, iprange_cmp);
|
|
ipranges_merge();
|
|
ipranges_find_b_blocks();
|
|
|
|
pbuf_sort(&new_ranges, iprange_cmp);
|
|
iprange_t* range;
|
|
|
|
PBUF_FOREACH(range, &black_ranges)
|
|
if (!range->obsolete)
|
|
continue;
|
|
printf("yes | ufw delete deny from %d.%d.%d.%d/%d\n",
|
|
range->ip >> 24,
|
|
(range->ip >> 16) & 0xff,
|
|
(range->ip >> 8) & 0xff,
|
|
(range->ip >> 0) & 0xff,
|
|
range->width);
|
|
PBUF_FOREACH_END
|
|
|
|
PBUF_FOREACH(range, &new_ranges)
|
|
printf("ufw insert 1 deny from %d.%d.%d.%d/%d\n",
|
|
range->ip >> 24,
|
|
(range->ip >> 16) & 0xff,
|
|
(range->ip >> 8) & 0xff,
|
|
(range->ip >> 0) & 0xff,
|
|
range->width);
|
|
PBUF_FOREACH_END
|
|
|
|
PBUF_FOREACH(range, &new_b_ranges)
|
|
printf("ufw insert 1 deny from %d.%d.%d.%d/%d\n",
|
|
range->ip >> 24,
|
|
(range->ip >> 16) & 0xff,
|
|
(range->ip >> 8) & 0xff,
|
|
(range->ip >> 0) & 0xff,
|
|
range->width);
|
|
PBUF_FOREACH_END
|
|
|
|
if (pbuf_size(&new_ranges) > 0) {
|
|
PBUF_FOREACH(range, &white_ranges)
|
|
printf("yes | ufw delete allow from %d.%d.%d.%d/%d\n",
|
|
range->ip >> 24,
|
|
(range->ip >> 16) & 0xff,
|
|
(range->ip >> 8) & 0xff,
|
|
(range->ip >> 0) & 0xff,
|
|
range->width);
|
|
printf("yes | ufw insert 1 allow from %d.%d.%d.%d/%d\n",
|
|
range->ip >> 24,
|
|
(range->ip >> 16) & 0xff,
|
|
(range->ip >> 8) & 0xff,
|
|
(range->ip >> 0) & 0xff,
|
|
range->width);
|
|
PBUF_FOREACH_END
|
|
}
|
|
|
|
pbuf_destroy(&black_ranges);
|
|
pbuf_destroy(&white_ranges);
|
|
pbuf_destroy(&new_ranges);
|
|
pbuf_destroy(&new_b_ranges);
|
|
exit(0);
|
|
}
|
|
|