firewall + random init pkt size
This commit is contained in:
1 parent
42eef31a64
commit
dbcd167a85
12 files changed
+286
-4
No files matched your search
+2
-1
@@ -23,7 +23,8 @@ utun_CORE_SOURCES = \
|
||||
pkt_normalizer.c \
|
||||
packet_dump.c \
|
||||
etcp_api.c \
|
||||
control_server.c
|
||||
control_server.c \
|
||||
firewall.c
|
||||
|
||||
# Platform-specific TUN libs (Windows only)
|
||||
utun_TUN_LIBS = @TUN_LIBS@
|
||||
|
||||
+89
-1
@@ -21,6 +21,7 @@
|
||||
|
||||
/* Forward declaration for new functions */
|
||||
static int assign_string(char *dest, size_t dest_size, const char *src);
|
||||
static int parse_firewall_rule(const char *rule_str, struct global_config *global);
|
||||
|
||||
typedef enum {
|
||||
SECTION_UNKNOWN,
|
||||
@@ -28,7 +29,8 @@ typedef enum {
|
||||
SECTION_SERVER,
|
||||
SECTION_CLIENT,
|
||||
SECTION_ROUTING,
|
||||
SECTION_DEBUG
|
||||
SECTION_DEBUG,
|
||||
SECTION_FIREWALL
|
||||
} section_type_t;
|
||||
|
||||
static char* trim(char *str) {
|
||||
@@ -203,6 +205,59 @@ static int add_route_entry(struct CFG_ROUTE_ENTRY **list, const char *subnet_str
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_firewall_rule(const char *rule_str, struct global_config *global) {
|
||||
if (!rule_str || !global) return -1;
|
||||
|
||||
char rule_copy[64];
|
||||
strncpy(rule_copy, rule_str, sizeof(rule_copy) - 1);
|
||||
rule_copy[sizeof(rule_copy) - 1] = '\0';
|
||||
trim(rule_copy);
|
||||
|
||||
if (strcasecmp(rule_copy, "all") == 0) {
|
||||
global->firewall_bypass_all = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Firewall: bypass all enabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse IP:port or IP
|
||||
char *port_str = strchr(rule_copy, ':');
|
||||
uint16_t port = 0;
|
||||
if (port_str) {
|
||||
*port_str = '\0';
|
||||
port = (uint16_t)atoi(port_str + 1);
|
||||
}
|
||||
|
||||
// Parse IP to uint32_t (host order)
|
||||
struct in_addr addr;
|
||||
if (inet_pton(AF_INET, rule_copy, &addr) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_firewall_rule: invalid IP: %s", rule_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t ip = ntohl(addr.s_addr); // convert to host order for sorting
|
||||
|
||||
// Reallocate array if needed
|
||||
if ((global->firewall_rule_count % INITIAL_ARRAY_CAPACITY) == 0) {
|
||||
int new_cap = global->firewall_rule_count + INITIAL_ARRAY_CAPACITY;
|
||||
struct CFG_FIREWALL_RULE *new_rules = u_realloc(global->firewall_rules,
|
||||
new_cap * sizeof(struct CFG_FIREWALL_RULE));
|
||||
if (!new_rules) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to realloc firewall rules");
|
||||
return -1;
|
||||
}
|
||||
global->firewall_rules = new_rules;
|
||||
}
|
||||
|
||||
struct CFG_FIREWALL_RULE *rule = &global->firewall_rules[global->firewall_rule_count];
|
||||
rule->ip = ip;
|
||||
rule->port = port;
|
||||
rule->bypass = 0;
|
||||
global->firewall_rule_count++;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Added firewall rule: %s:%d", rule_str, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) {
|
||||
struct CFG_SERVER *srv = servers;
|
||||
while (srv) {
|
||||
@@ -392,6 +447,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
|
||||
if (strcasecmp(section, "global") == 0) return SECTION_GLOBAL;
|
||||
if (strcasecmp(section, "routing") == 0) return SECTION_ROUTING;
|
||||
if (strcasecmp(section, "debug") == 0) return SECTION_DEBUG;
|
||||
if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL;
|
||||
|
||||
char *colon = strchr(section, ':');
|
||||
if (!colon) return SECTION_UNKNOWN;
|
||||
@@ -418,6 +474,9 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
|
||||
// Set default values
|
||||
cfg->global.keepalive_timeout = 2000; // Default 2 seconds
|
||||
cfg->global.firewall_rules = NULL;
|
||||
cfg->global.firewall_rule_count = 0;
|
||||
cfg->global.firewall_bypass_all = 0;
|
||||
|
||||
section_type_t cur_section = SECTION_UNKNOWN;
|
||||
struct CFG_SERVER *cur_server = NULL;
|
||||
@@ -508,6 +567,13 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cfg->global.debug_levels.count++;
|
||||
}
|
||||
break;
|
||||
case SECTION_FIREWALL:
|
||||
if (strcmp(key, "allow") == 0) {
|
||||
if (parse_firewall_rule(value, &cfg->global) < 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid firewall rule: %s", filename, line_num, value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
|
||||
break;
|
||||
@@ -580,6 +646,9 @@ void free_config(struct utun_config *config) {
|
||||
free_route_entries(config->route_subnets);
|
||||
free_route_entries(config->my_subnets);
|
||||
|
||||
// Free firewall rules
|
||||
u_free(config->global.firewall_rules);
|
||||
|
||||
u_free(config);
|
||||
}
|
||||
|
||||
@@ -644,6 +713,25 @@ void print_config(const struct utun_config *cfg) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " %s/%d", ip_to_string(&my->ip, ip_buffer, sizeof(ip_buffer)), my->netmask);
|
||||
my = my->next;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Firewall:");
|
||||
if (cfg->global.firewall_bypass_all) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " bypass_all=1");
|
||||
} else if (cfg->global.firewall_rule_count > 0) {
|
||||
for (int i = 0; i < cfg->global.firewall_rule_count; i++) {
|
||||
struct CFG_FIREWALL_RULE *r = &cfg->global.firewall_rules[i];
|
||||
char ip_str[16];
|
||||
struct in_addr ia = { .s_addr = htonl(r->ip) };
|
||||
inet_ntop(AF_INET, &ia, ip_str, sizeof(ip_str));
|
||||
if (r->port == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s", ip_str);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s:%d", ip_str, r->port);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules");
|
||||
}
|
||||
}
|
||||
|
||||
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) {
|
||||
|
||||
@@ -59,6 +59,12 @@ struct CFG_ROUTE_ENTRY {
|
||||
uint8_t netmask;
|
||||
};
|
||||
|
||||
struct CFG_FIREWALL_RULE {
|
||||
uint32_t ip; // IPv4 in host byte order for sorting
|
||||
uint16_t port; // 0 means any port
|
||||
uint8_t bypass; // 1 for allow=all (bypass all checks)
|
||||
};
|
||||
|
||||
struct global_config {
|
||||
char my_private_key_hex[MAX_KEY_LEN];
|
||||
char my_public_key_hex[MAX_KEY_LEN];
|
||||
@@ -88,6 +94,11 @@ struct global_config {
|
||||
int tun_test_mode; // test mode: 1 = don't open real TUN, queues only
|
||||
int keepalive_timeout; // keepalive timeout in ms (default: 2000)
|
||||
int keepalive_interval; // keepalive interval in ms (default: 200)
|
||||
|
||||
// Firewall configuration
|
||||
struct CFG_FIREWALL_RULE *firewall_rules;
|
||||
int firewall_rule_count;
|
||||
int firewall_bypass_all;
|
||||
};
|
||||
|
||||
struct utun_config {
|
||||
|
||||
+28
-2
@@ -4,6 +4,7 @@
|
||||
#ifndef _WIN32
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "utun_instance.h"
|
||||
@@ -84,6 +85,16 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
|
||||
|
||||
dgram->data[offset++] = link->local_link_id;
|
||||
|
||||
// padding
|
||||
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
|
||||
if (s > link->mtu - dgram->noencrypt_len) s = link->mtu - dgram->noencrypt_len;
|
||||
|
||||
int to_add=s-offset-UDP_HDR_SIZE - UDP_SC_HDR_SIZE;
|
||||
if (to_add<0) to_add=0;
|
||||
|
||||
for (int i=0; i<to_add; i++) dgram->data[offset++]=rand();// fill pad
|
||||
// padding end
|
||||
|
||||
uint8_t salt[SC_PUBKEY_ENC_SALT_SIZE];
|
||||
random_bytes(salt, sizeof(salt));
|
||||
memcpy(dgram->data + offset, salt, SC_PUBKEY_ENC_SALT_SIZE);
|
||||
@@ -95,6 +106,7 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
|
||||
memcpy(dgram->data + offset, obfuscated_pubkey, SC_PUBKEY_SIZE);
|
||||
offset += SC_PUBKEY_SIZE;
|
||||
|
||||
|
||||
dgram->data_len = offset;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%016llx, retry=%d", (unsigned long long)node_id, link->init_retry_count);
|
||||
@@ -568,6 +580,9 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
link->init_timeout = 0;
|
||||
link->init_retry_count = 0;
|
||||
link->link_status = 0; // down initially
|
||||
link->handshake_minsize = 100;
|
||||
link->handshake_maxsize = mtu;// 28 = udp header size
|
||||
|
||||
|
||||
// Initialize keepalive timeout from global config
|
||||
if (etcp->instance && etcp->instance->config) {
|
||||
@@ -865,7 +880,7 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
|
||||
// Try INIT decryption (for incoming connection requests)
|
||||
// This handles: no link found, or link without session, or normal decrypt failed
|
||||
if (recv_len <= SC_PUBKEY_ENC_SIZE) {
|
||||
if (recv_len <= SC_PUBKEY_ENC_SIZE + UDP_SC_HDR_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet too small for init, size=%zd", recv_len);
|
||||
errorcode=1;
|
||||
goto ec_fr;
|
||||
@@ -1036,11 +1051,22 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
memset(ack_repl_hdr->peer_ipv4, 0, 4);
|
||||
memset(ack_repl_hdr->peer_port, 0, 2);
|
||||
}
|
||||
pkt->data_len=sizeof(*ack_repl_hdr);
|
||||
pkt->noencrypt_len=0;
|
||||
pkt->link=link;
|
||||
link->recv_keepalive = 1;
|
||||
|
||||
int xoffset=sizeof(*ack_repl_hdr);
|
||||
// padding
|
||||
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
|
||||
if (s > link->mtu) s = link->mtu;
|
||||
|
||||
int to_add=s - xoffset - UDP_HDR_SIZE - UDP_SC_HDR_SIZE;
|
||||
if (to_add<0) to_add=0;
|
||||
|
||||
for (int i=0; i<to_add; i++) pkt->data[xoffset++]=rand();// fill pad
|
||||
// padding end
|
||||
pkt->data_len=xoffset;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Sending INIT RESPONSE, link=%p, local_link_id=%d, remote_link_id=%d", link, link->local_link_id, link->remote_link_id);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] Send INIT RESPONSE");
|
||||
etcp_encrypt_send(pkt);
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "../lib/socket_compat.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define UDP_HDR_SIZE 28// размер udp header + ethernet заголовков (ipv4) [для ipv6 = 48 байт]
|
||||
#define UDP_SC_HDR_SIZE (13+8+4 + 5)// 13+8+4 - sc_nonce+tag size+crc, 5 - payload hdr
|
||||
|
||||
|
||||
#define PACKET_DATA_SIZE 1600//1536
|
||||
|
||||
// Типы кодограмм протокола
|
||||
@@ -151,6 +155,8 @@ struct ETCP_LINK {
|
||||
uint8_t pkt_sent_since_keepalive; // Флаг: был ли отправлен пакет с последнего keepalive тика
|
||||
uint32_t keepalive_sent_count; // Счётчик отправленных keepalive
|
||||
uint32_t keepalive_recv_count; // Счётчик полученных keepalive
|
||||
uint16_t handshake_minsize; // минимальный размер udp при handshake
|
||||
uint16_t handshake_maxsize; // мax размер udp при handshake (выбирает рандом)
|
||||
};
|
||||
|
||||
// INITIALIZATION (создаёт listen-сокеты и подключения из конфига)
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// firewall.c - Firewall module for utun
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "firewall.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
|
||||
static int compare_rules(const void *a, const void *b) {
|
||||
const struct CFG_FIREWALL_RULE *ra = (const struct CFG_FIREWALL_RULE *)a;
|
||||
const struct CFG_FIREWALL_RULE *rb = (const struct CFG_FIREWALL_RULE *)b;
|
||||
if (ra->ip != rb->ip) {
|
||||
return (ra->ip > rb->ip) ? 1 : -1;
|
||||
}
|
||||
return (ra->port > rb->port) ? 1 : -1;
|
||||
}
|
||||
|
||||
int fw_init(struct firewall_ctx *ctx) {
|
||||
if (!ctx) return -1;
|
||||
memset(ctx, 0, sizeof(struct firewall_ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fw_load_rules(struct firewall_ctx *ctx, const struct global_config *cfg) {
|
||||
if (!ctx || !cfg) return -1;
|
||||
|
||||
fw_free(ctx);
|
||||
fw_init(ctx);
|
||||
|
||||
ctx->bypass_all = cfg->firewall_bypass_all;
|
||||
|
||||
if (cfg->firewall_rule_count > 0) {
|
||||
ctx->rules = u_calloc(cfg->firewall_rule_count, sizeof(struct CFG_FIREWALL_RULE));
|
||||
if (!ctx->rules) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "fw_load_rules: failed to allocate rules");
|
||||
return -1;
|
||||
}
|
||||
memcpy(ctx->rules, cfg->firewall_rules,
|
||||
cfg->firewall_rule_count * sizeof(struct CFG_FIREWALL_RULE));
|
||||
ctx->count = cfg->firewall_rule_count;
|
||||
|
||||
// Sort rules by IP (and port)
|
||||
qsort(ctx->rules, ctx->count, sizeof(struct CFG_FIREWALL_RULE), compare_rules);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Loaded %d firewall rules (sorted)", ctx->count);
|
||||
}
|
||||
|
||||
if (ctx->bypass_all) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Firewall bypass_all mode enabled");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fw_check(const struct firewall_ctx *ctx, uint32_t ip, uint16_t port) {
|
||||
if (!ctx) return 0;
|
||||
if (ctx->bypass_all) return 1;
|
||||
|
||||
// Binary search for IP
|
||||
int low = 0;
|
||||
int high = ctx->count - 1;
|
||||
while (low <= high) {
|
||||
int mid = (low + high) / 2;
|
||||
if (ctx->rules[mid].ip == ip) {
|
||||
// Found matching IP, check port
|
||||
int i = mid;
|
||||
// Check exact match or any-port rule
|
||||
while (i >= 0 && ctx->rules[i].ip == ip) {
|
||||
if (ctx->rules[i].port == 0 || ctx->rules[i].port == port) {
|
||||
return 1;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
i = mid + 1;
|
||||
while (i < ctx->count && ctx->rules[i].ip == ip) {
|
||||
if (ctx->rules[i].port == 0 || ctx->rules[i].port == port) {
|
||||
return 1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
} else if (ctx->rules[mid].ip < ip) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fw_free(struct firewall_ctx *ctx) {
|
||||
if (!ctx) return;
|
||||
u_free(ctx->rules);
|
||||
memset(ctx, 0, sizeof(struct firewall_ctx));
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// firewall.h - Firewall rules for utun
|
||||
#ifndef FIREWALL_H
|
||||
#define FIREWALL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "config_parser.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct firewall_ctx {
|
||||
struct CFG_FIREWALL_RULE *rules;
|
||||
int count;
|
||||
int bypass_all;
|
||||
};
|
||||
|
||||
int fw_init(struct firewall_ctx *ctx);
|
||||
int fw_load_rules(struct firewall_ctx *ctx, const struct global_config *cfg);
|
||||
int fw_check(const struct firewall_ctx *ctx, uint32_t ip, uint16_t port);
|
||||
void fw_free(struct firewall_ctx *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "debug_config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@@ -319,6 +321,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
srand(time(NULL));
|
||||
// Create uasync instance
|
||||
struct UASYNC* ua = uasync_create();
|
||||
main_ua=ua;
|
||||
|
||||
@@ -117,6 +117,15 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized");
|
||||
}
|
||||
|
||||
// Initialize firewall
|
||||
fw_init(&instance->fw);
|
||||
if (fw_load_rules(&instance->fw, &config->global) != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Failed to load firewall rules");
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Firewall initialized: %d rules, bypass_all=%d",
|
||||
instance->fw.count, instance->fw.bypass_all);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -264,6 +273,9 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
|
||||
instance->bgp = NULL;
|
||||
}
|
||||
|
||||
// Cleanup firewall
|
||||
fw_free(&instance->fw);
|
||||
|
||||
// Cleanup config
|
||||
if (instance->config) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration");
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "secure_channel.h"
|
||||
#include "etcp_api.h"
|
||||
#include "config_parser.h"
|
||||
#include "firewall.h"
|
||||
|
||||
// Forward declarations
|
||||
struct utun_config;
|
||||
@@ -70,6 +71,9 @@ struct UTUN_INSTANCE {
|
||||
|
||||
// Control server for monitoring
|
||||
struct control_server* control_srv;
|
||||
|
||||
// Firewall
|
||||
struct firewall_ctx fw;
|
||||
};
|
||||
|
||||
// Functions
|
||||
|
||||
@@ -84,6 +84,7 @@ ETCP_FULL_OBJS = \
|
||||
$(top_builddir)/src/utun-tun_if.o \
|
||||
$(top_builddir)/src/utun-tun_route.o \
|
||||
$(top_builddir)/src/utun-packet_dump.o \
|
||||
$(top_builddir)/src/utun-firewall.o \
|
||||
$(TUN_PLATFORM_OBJ) \
|
||||
$(top_builddir)/src/utun-utun_instance.o \
|
||||
$(ETCP_CORE_OBJS)
|
||||
|
||||
@@ -30,3 +30,11 @@ addr=192.168.29.117:1333
|
||||
#so_mark=100
|
||||
#netif=eth0
|
||||
type=public # public / nat / private
|
||||
|
||||
[firewall]
|
||||
#ограничиваем подключения к собственным ресурсам. по умолчанию всё запрещено. разрешаем доступ директивами:
|
||||
# Firewall rules: allow=all, allow=IP or allow=IP:port
|
||||
allow=all
|
||||
#allow=192.168.1.100
|
||||
#allow=10.0.0.50:443
|
||||
#allow=8.8.8.8
|
||||
Reference in new issue
Block a user