From 048bf96e514bb7cc43f3834e1738cdbf9f6a5d48 Mon Sep 17 00:00:00 2001 From: Daniel Brahneborg Date: Thu, 4 Dec 2025 15:52:13 +0100 Subject: [PATCH] more files --- .gitignore | 1 + CMakeLists.txt | 9 ++ autofw.c | 342 +++++++++++++++++++++++++++++++++++++++++++++++++ matcher.h | 11 ++ pbuf.h | 14 ++ vbuf.h | 0 6 files changed, 377 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 autofw.c create mode 100644 matcher.h create mode 100644 pbuf.h create mode 100644 vbuf.h diff --git a/.gitignore b/.gitignore index 762a103..a708289 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ CMakeCache.txt CMakeFiles cmake_install.cmake .DS_Store +Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..93505d2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ + +cmake_minimum_required(VERSION 3.10) + +project(AutoFW) + +add_executable(autofw + autofw.c + ) + diff --git a/autofw.c b/autofw.c new file mode 100644 index 0000000..7f8ea36 --- /dev/null +++ b/autofw.c @@ -0,0 +1,342 @@ + +#include +#include +#include +#include + +#include +#include + +#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); +} + diff --git a/matcher.h b/matcher.h new file mode 100644 index 0000000..374e44f --- /dev/null +++ b/matcher.h @@ -0,0 +1,11 @@ + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + +#include "pbuf.h" + +typedef struct { + pcre2_code* regexp; + pbuf_t results; +} regexp_matcher_t; + diff --git a/pbuf.h b/pbuf.h new file mode 100644 index 0000000..0192363 --- /dev/null +++ b/pbuf.h @@ -0,0 +1,14 @@ + +typedef void (*pbuf_func_free_t)(void*); + +typedef struct { + int used; + int allocated; + int chunksize; + void** ptr; + pbuf_func_free_t func_free; +} pbuf_t; + +// Add a new item at the end of the pbuf_t. +extern void pbuf_append(pbuf_t* pbuf, void* ptr); + diff --git a/vbuf.h b/vbuf.h new file mode 100644 index 0000000..e69de29