NAT: Full Cone (EIM) NAT with ETCP_ID_NAT protocol, per-node routing, and comprehensive test suite
- New ETCP protocol ETCP_ID_NAT (0x02) for NAT traffic between nodes - eim_nat.c/h: Pure NAT engine (table, port allocation, checksum updates, egress/ingress) - nat_transport.c/h: Transport layer (NAT TUN, ETCP binding, client/provider roles) - route_bgp_find_conn_for_node(): shared utility for optimal connection lookup - tun_init_nat(): dedicated TUN creation for NAT - Config: [nat] section with tun_ip, port_start/end, nat_via, forward rules - Fix: csum_update_ip/transport use htonl() halves (correct on LE systems) - Fix: IP_FRAG_MF_MASK changed to 0x2000 (after ntohs, MF is bit 13) - Tests: 26 engine unit + 4 transport integration + 7 stress (29 total)
This commit is contained in:
1 parent
b7da11c2bf
commit
907ef244c6
22 files changed
+2780
-4
No files matched your search
+3
-1
@@ -26,7 +26,9 @@ utun_CORE_SOURCES = \
|
||||
packet_dump.c \
|
||||
etcp_api.c \
|
||||
control_server.c \
|
||||
firewall.c
|
||||
firewall.c \
|
||||
eim_nat.c \
|
||||
nat_transport.c
|
||||
|
||||
# Platform-specific TUN libs (Windows only)
|
||||
utun_TUN_LIBS = @TUN_LIBS@
|
||||
|
||||
+81
-1
@@ -13,6 +13,7 @@
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include "../lib/mem.h"
|
||||
|
||||
@@ -42,7 +43,8 @@ typedef enum {
|
||||
SECTION_DEBUG,
|
||||
SECTION_FIREWALL,
|
||||
SECTION_CONTROL,
|
||||
SECTION_ALLOWED_KEYS
|
||||
SECTION_ALLOWED_KEYS,
|
||||
SECTION_NAT
|
||||
} section_type_t;
|
||||
|
||||
static char* trim(char *str) {
|
||||
@@ -414,6 +416,77 @@ static int parse_control(const char *key, const char *value, struct global_confi
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nat(const char *key, const char *value, struct global_config *global) {
|
||||
if (strcmp(key, "tun_ifname") == 0) {
|
||||
strncpy(global->nat_tun_ifname, value, sizeof(global->nat_tun_ifname) - 1);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "tun_ip") == 0) {
|
||||
uint8_t netmask;
|
||||
parse_ip_with_netmask(value, &global->nat_tun_ip, &netmask);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "nat_via") == 0) {
|
||||
global->nat_via_node_id = strtoull(value, NULL, 16);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "port_start") == 0) {
|
||||
global->nat_port_start = (uint16_t)atoi(value);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "port_end") == 0) {
|
||||
global->nat_port_end = (uint16_t)atoi(value);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "forward") == 0) {
|
||||
if (global->nat_forward_count >= MAX_NAT_FORWARDS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many NAT forward rules (max %d)", MAX_NAT_FORWARDS);
|
||||
return -1;
|
||||
}
|
||||
// Format: proto:internal_ip:internal_port:external_port
|
||||
// Example: tcp:192.168.1.100:8080:8080
|
||||
char buf[128];
|
||||
strncpy(buf, value, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
trim(buf);
|
||||
|
||||
char* proto_str = strtok(buf, ":");
|
||||
char* ip_str = strtok(NULL, ":");
|
||||
char* int_port = strtok(NULL, ":");
|
||||
char* ext_port = strtok(NULL, ":");
|
||||
if (!proto_str || !ip_str || !int_port || !ext_port) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid NAT forward format: %s (expected proto:ip:internal_port:external_port)", value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t proto;
|
||||
if (strcasecmp(proto_str, "tcp") == 0) proto = IPPROTO_TCP;
|
||||
else if (strcasecmp(proto_str, "udp") == 0) proto = IPPROTO_UDP;
|
||||
else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid NAT forward proto: %s (tcp/udp)", proto_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct in_addr addr;
|
||||
if (inet_pton(AF_INET, ip_str, &addr) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid NAT forward IP: %s", ip_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int idx = global->nat_forward_count;
|
||||
global->nat_forwards[idx].proto = proto;
|
||||
global->nat_forwards[idx].internal_ip_host = ntohl(addr.s_addr);
|
||||
global->nat_forwards[idx].internal_port_net = htons((uint16_t)atoi(int_port));
|
||||
global->nat_forwards[idx].external_port = (uint16_t)atoi(ext_port);
|
||||
global->nat_forward_count++;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "NAT forward: %s %s:%s -> :%s",
|
||||
proto_str, ip_str, int_port, ext_port);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) {
|
||||
if (strcmp(key, "addr") == 0) {
|
||||
return parse_address_and_port(value, &srv->ip);
|
||||
@@ -519,6 +592,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
|
||||
if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL;
|
||||
if (strcasecmp(section, "control") == 0) return SECTION_CONTROL;
|
||||
if (strcasecmp(section, "allowed_keys") == 0) return SECTION_ALLOWED_KEYS;
|
||||
if (strcasecmp(section, "nat") == 0) return SECTION_NAT;
|
||||
|
||||
char *colon = strchr(section, ':');
|
||||
if (!colon) return SECTION_UNKNOWN;
|
||||
@@ -674,6 +748,12 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SECTION_NAT:
|
||||
cfg->global.nat_enabled = 1;
|
||||
if (parse_nat(key, value, &cfg->global) < 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid NAT key '%s'", filename, line_num, key);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
|
||||
break;
|
||||
|
||||
@@ -121,6 +121,22 @@ struct global_config {
|
||||
struct CFG_ALLOWED_KEY *allowed_keys;
|
||||
int allowed_keys_count;
|
||||
int allowed_keys_allow_all;
|
||||
|
||||
// NAT configuration ([nat] section)
|
||||
int nat_enabled;
|
||||
char nat_tun_ifname[16];
|
||||
struct IP nat_tun_ip;
|
||||
uint64_t nat_via_node_id; // 0 = this node is provider; !=0 = client, use this provider
|
||||
uint16_t nat_port_start;
|
||||
uint16_t nat_port_end;
|
||||
#define MAX_NAT_FORWARDS 64
|
||||
struct {
|
||||
uint8_t proto;
|
||||
uint32_t internal_ip_host;
|
||||
uint16_t internal_port_net;
|
||||
uint16_t external_port;
|
||||
} nat_forwards[MAX_NAT_FORWARDS];
|
||||
int nat_forward_count;
|
||||
};
|
||||
|
||||
struct utun_config {
|
||||
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
#include "eim_nat.h"
|
||||
#include "config_parser.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// IP header offsets
|
||||
#define IP_IHL_OFFSET 0
|
||||
#define IP_PROTO_OFFSET 9
|
||||
#define IP_CHECKSUM_OFFSET 10
|
||||
#define IP_SRC_ADDR_OFFSET 12
|
||||
#define IP_DST_ADDR_OFFSET 16
|
||||
#define IP_HDR_MIN_SIZE 20
|
||||
|
||||
#define IP_FRAG_MF_MASK 0x2000
|
||||
#define IP_FRAG_OFF_MASK 0x1FFF
|
||||
|
||||
#define TCP_SRC_PORT_OFFSET 0
|
||||
#define TCP_DST_PORT_OFFSET 2
|
||||
#define TCP_CHECKSUM_OFFSET 16
|
||||
#define UDP_SRC_PORT_OFFSET 0
|
||||
#define UDP_DST_PORT_OFFSET 2
|
||||
#define UDP_CHECKSUM_OFFSET 6
|
||||
#define ICMP_TYPE_OFFSET 0
|
||||
#define ICMP_ID_OFFSET 4
|
||||
#define ICMP_CHECKSUM_OFFSET 2
|
||||
#define ICMP_ECHO_REQUEST 8
|
||||
#define ICMP_ECHO_REPLY 0
|
||||
|
||||
static ip_str_t ip_host_to_str(uint32_t ip_host) {
|
||||
struct in_addr a; a.s_addr = htonl(ip_host);
|
||||
return ip_to_str(&a, AF_INET);
|
||||
}
|
||||
|
||||
static uint16_t csum_update_n(uint16_t old_csum, const uint16_t* old_vals,
|
||||
const uint16_t* new_vals, int n) {
|
||||
uint32_t sum = (uint32_t)(uint16_t)(~old_csum);
|
||||
for (int i = 0; i < n; i++) { sum -= old_vals[i]; sum += new_vals[i]; }
|
||||
sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
return (uint16_t)(~sum);
|
||||
}
|
||||
|
||||
static void csum_update_ip(uint8_t* ip_data, uint32_t old_ip_host, uint32_t new_ip_host) {
|
||||
uint16_t old_csum; memcpy(&old_csum, ip_data + IP_CHECKSUM_OFFSET, 2);
|
||||
uint32_t old_ip_net = htonl(old_ip_host), new_ip_net = htonl(new_ip_host);
|
||||
uint16_t old_w[2] = {(uint16_t)(old_ip_net & 0xFFFF), (uint16_t)(old_ip_net >> 16)};
|
||||
uint16_t new_w[2] = {(uint16_t)(new_ip_net & 0xFFFF), (uint16_t)(new_ip_net >> 16)};
|
||||
uint16_t new_csum = csum_update_n(old_csum, old_w, new_w, 2);
|
||||
memcpy(ip_data + IP_CHECKSUM_OFFSET, &new_csum, 2);
|
||||
}
|
||||
|
||||
static void csum_update_transport(uint8_t* transport, int csum_off,
|
||||
uint32_t old_ip_host, uint32_t new_ip_host,
|
||||
uint16_t old_port_net, uint16_t new_port_net) {
|
||||
uint16_t old_csum; memcpy(&old_csum, transport + csum_off, 2);
|
||||
uint32_t old_ip_net = htonl(old_ip_host), new_ip_net = htonl(new_ip_host);
|
||||
uint16_t old_w[3] = {(uint16_t)(old_ip_net & 0xFFFF), (uint16_t)(old_ip_net >> 16), old_port_net};
|
||||
uint16_t new_w[3] = {(uint16_t)(new_ip_net & 0xFFFF), (uint16_t)(new_ip_net >> 16), new_port_net};
|
||||
uint16_t new_csum = csum_update_n(old_csum, old_w, new_w, 3);
|
||||
memcpy(transport + csum_off, &new_csum, 2);
|
||||
}
|
||||
|
||||
static void csum_update_icmp(uint8_t* icmp, uint16_t old_word, uint16_t new_word) {
|
||||
uint16_t old_csum; memcpy(&old_csum, icmp + ICMP_CHECKSUM_OFFSET, 2);
|
||||
uint16_t new_csum = csum_update_n(old_csum, &old_word, &new_word, 1);
|
||||
memcpy(icmp + ICMP_CHECKSUM_OFFSET, &new_csum, 2);
|
||||
}
|
||||
|
||||
static struct eim_nat_entry* eim_nat_find_egress(struct eim_nat_ctx* ctx,
|
||||
uint8_t proto, uint32_t ip_host, uint16_t port_net) {
|
||||
for (uint16_t i = ctx->port_start; i <= ctx->port_end; i++) {
|
||||
struct eim_nat_entry* e = &ctx->table[i];
|
||||
if (e->state != EIM_NAT_ENTRY_FREE && e->proto == proto &&
|
||||
e->internal_ip == ip_host && e->internal_port == port_net) return e;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint16_t eim_nat_alloc_port(struct eim_nat_ctx* ctx) {
|
||||
uint16_t start = ctx->next_port;
|
||||
do {
|
||||
if (ctx->table[ctx->next_port].state == EIM_NAT_ENTRY_FREE) {
|
||||
uint16_t port = ctx->next_port;
|
||||
ctx->next_port++;
|
||||
if (ctx->next_port > ctx->port_end) ctx->next_port = ctx->port_start;
|
||||
return port;
|
||||
}
|
||||
ctx->next_port++;
|
||||
if (ctx->next_port > ctx->port_end) ctx->next_port = ctx->port_start;
|
||||
} while (ctx->next_port != start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ==================== Public API ====================
|
||||
|
||||
int eim_nat_egress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len,
|
||||
uint64_t src_node_id, struct ETCP_CONN* src_conn) {
|
||||
if (ip_len < IP_HDR_MIN_SIZE) return -1;
|
||||
uint8_t ihl = ip_data[IP_IHL_OFFSET] & 0x0F;
|
||||
if (ihl < 5) return -1;
|
||||
uint16_t ip_hdr_len = ihl * 4;
|
||||
if (ip_len < ip_hdr_len) return -1;
|
||||
|
||||
uint16_t frag_off = ntohs(*(uint16_t*)(ip_data + 6));
|
||||
if ((frag_off & (IP_FRAG_MF_MASK | IP_FRAG_OFF_MASK)) != 0) return 0;
|
||||
|
||||
uint8_t proto = ip_data[IP_PROTO_OFFSET];
|
||||
uint32_t src_ip_net, src_ip_host;
|
||||
memcpy(&src_ip_net, ip_data + IP_SRC_ADDR_OFFSET, 4);
|
||||
src_ip_host = ntohl(src_ip_net);
|
||||
|
||||
uint8_t* transport = ip_data + ip_hdr_len;
|
||||
size_t tlen = ip_len - ip_hdr_len;
|
||||
uint16_t src_port_net = 0;
|
||||
int is_tcp = (proto == IPPROTO_TCP_UINT8 && tlen >= 20);
|
||||
int is_udp = (proto == IPPROTO_UDP_UINT8 && tlen >= 8);
|
||||
int is_icmp_echo = 0;
|
||||
|
||||
if (is_tcp) memcpy(&src_port_net, transport + TCP_SRC_PORT_OFFSET, 2);
|
||||
else if (is_udp) memcpy(&src_port_net, transport + UDP_SRC_PORT_OFFSET, 2);
|
||||
else if (proto == IPPROTO_ICMP_UINT8 && tlen >= 8) {
|
||||
uint8_t icmp_type = transport[ICMP_TYPE_OFFSET];
|
||||
if (icmp_type == ICMP_ECHO_REQUEST || icmp_type == ICMP_ECHO_REPLY) {
|
||||
is_icmp_echo = 1; memcpy(&src_port_net, transport + ICMP_ID_OFFSET, 2);
|
||||
}
|
||||
}
|
||||
if (!is_tcp && !is_udp && !is_icmp_echo) return 0;
|
||||
|
||||
struct eim_nat_entry* entry = eim_nat_find_egress(ctx, proto, src_ip_host, src_port_net);
|
||||
uint16_t ext_port_host;
|
||||
if (entry) {
|
||||
ext_port_host = (uint16_t)(entry - ctx->table);
|
||||
} else {
|
||||
ext_port_host = eim_nat_alloc_port(ctx);
|
||||
if (ext_port_host == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT port range exhausted (%s:%u proto=%u)",
|
||||
ip_host_to_str(src_ip_host).str, ntohs(src_port_net), proto);
|
||||
return -1;
|
||||
}
|
||||
entry = &ctx->table[ext_port_host];
|
||||
entry->internal_ip = src_ip_host;
|
||||
entry->internal_port = src_port_net;
|
||||
entry->proto = proto;
|
||||
entry->state = EIM_NAT_ENTRY_ACTIVE;
|
||||
entry->src_node_id = src_node_id;
|
||||
entry->src_conn = src_conn;
|
||||
entry->last_seen = 0;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT map: %s:%u -> %s:%u (proto=%u) from node %016llx",
|
||||
ip_host_to_str(src_ip_host).str, ntohs(src_port_net),
|
||||
ip_host_to_str(ctx->gateway_ip).str, ext_port_host, proto,
|
||||
(unsigned long long)src_node_id);
|
||||
}
|
||||
|
||||
uint32_t gw_ip_net = htonl(ctx->gateway_ip);
|
||||
memcpy(ip_data + IP_SRC_ADDR_OFFSET, &gw_ip_net, 4);
|
||||
csum_update_ip(ip_data, src_ip_host, ctx->gateway_ip);
|
||||
|
||||
uint16_t new_port_net = htons(ext_port_host);
|
||||
if (is_tcp) {
|
||||
memcpy(transport + TCP_SRC_PORT_OFFSET, &new_port_net, 2);
|
||||
csum_update_transport(transport, TCP_CHECKSUM_OFFSET, src_ip_host, ctx->gateway_ip, src_port_net, new_port_net);
|
||||
} else if (is_udp) {
|
||||
memcpy(transport + UDP_SRC_PORT_OFFSET, &new_port_net, 2);
|
||||
csum_update_transport(transport, UDP_CHECKSUM_OFFSET, src_ip_host, ctx->gateway_ip, src_port_net, new_port_net);
|
||||
} else if (is_icmp_echo) {
|
||||
memcpy(transport + ICMP_ID_OFFSET, &new_port_net, 2);
|
||||
csum_update_icmp(transport, src_port_net, new_port_net);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eim_nat_ingress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len,
|
||||
struct eim_nat_entry** out_entry) {
|
||||
if (ip_len < IP_HDR_MIN_SIZE) return -1;
|
||||
uint8_t ihl = ip_data[IP_IHL_OFFSET] & 0x0F;
|
||||
if (ihl < 5) return -1;
|
||||
uint16_t ip_hdr_len = ihl * 4;
|
||||
if (ip_len < ip_hdr_len) return -1;
|
||||
|
||||
uint16_t frag_off = ntohs(*(uint16_t*)(ip_data + 6));
|
||||
if ((frag_off & (IP_FRAG_MF_MASK | IP_FRAG_OFF_MASK)) != 0) return 0;
|
||||
|
||||
uint8_t proto = ip_data[IP_PROTO_OFFSET];
|
||||
uint32_t dst_ip_net;
|
||||
memcpy(&dst_ip_net, ip_data + IP_DST_ADDR_OFFSET, 4);
|
||||
if (dst_ip_net != htonl(ctx->gateway_ip)) return 0;
|
||||
|
||||
uint8_t* transport = ip_data + ip_hdr_len;
|
||||
size_t tlen = ip_len - ip_hdr_len;
|
||||
uint16_t dst_port_net = 0;
|
||||
int is_tcp = (proto == IPPROTO_TCP_UINT8 && tlen >= 20);
|
||||
int is_udp = (proto == IPPROTO_UDP_UINT8 && tlen >= 8);
|
||||
int is_icmp_echo = 0;
|
||||
|
||||
if (is_tcp) memcpy(&dst_port_net, transport + TCP_DST_PORT_OFFSET, 2);
|
||||
else if (is_udp) memcpy(&dst_port_net, transport + UDP_DST_PORT_OFFSET, 2);
|
||||
else if (proto == IPPROTO_ICMP_UINT8 && tlen >= 8) {
|
||||
uint8_t icmp_type = transport[ICMP_TYPE_OFFSET];
|
||||
if (icmp_type == ICMP_ECHO_REQUEST || icmp_type == ICMP_ECHO_REPLY) {
|
||||
is_icmp_echo = 1; memcpy(&dst_port_net, transport + ICMP_ID_OFFSET, 2);
|
||||
}
|
||||
}
|
||||
if (!is_tcp && !is_udp && !is_icmp_echo) return 0;
|
||||
|
||||
uint16_t ext_port_host = ntohs(dst_port_net);
|
||||
if (ext_port_host < ctx->port_start || ext_port_host > ctx->port_end) return 0;
|
||||
|
||||
struct eim_nat_entry* entry = &ctx->table[ext_port_host];
|
||||
if (entry->state == EIM_NAT_ENTRY_FREE) return 0;
|
||||
if (entry->proto != proto) return 0;
|
||||
|
||||
uint32_t dst_ip_host = ntohl(dst_ip_net);
|
||||
uint32_t internal_ip_net = htonl(entry->internal_ip);
|
||||
memcpy(ip_data + IP_DST_ADDR_OFFSET, &internal_ip_net, 4);
|
||||
csum_update_ip(ip_data, dst_ip_host, entry->internal_ip);
|
||||
|
||||
uint16_t internal_port_net = entry->internal_port;
|
||||
if (is_tcp) {
|
||||
memcpy(transport + TCP_DST_PORT_OFFSET, &internal_port_net, 2);
|
||||
csum_update_transport(transport, TCP_CHECKSUM_OFFSET, dst_ip_host, entry->internal_ip, dst_port_net, internal_port_net);
|
||||
} else if (is_udp) {
|
||||
memcpy(transport + UDP_DST_PORT_OFFSET, &internal_port_net, 2);
|
||||
csum_update_transport(transport, UDP_CHECKSUM_OFFSET, dst_ip_host, entry->internal_ip, dst_port_net, internal_port_net);
|
||||
} else if (is_icmp_echo) {
|
||||
memcpy(transport + ICMP_ID_OFFSET, &internal_port_net, 2);
|
||||
csum_update_icmp(transport, dst_port_net, internal_port_net);
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT unmap: %s:%u <- %s:%u (proto=%u) back to node %016llx",
|
||||
ip_host_to_str(entry->internal_ip).str, ntohs(entry->internal_port),
|
||||
ip_host_to_str(ctx->gateway_ip).str, ext_port_host, proto,
|
||||
(unsigned long long)entry->src_node_id);
|
||||
|
||||
if (out_entry) *out_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ==================== Init / Destroy ====================
|
||||
|
||||
int eim_nat_init_ctx(struct eim_nat_ctx* ctx, const struct global_config* g) {
|
||||
if (!ctx || !g) return -1;
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
if (!g->nat_enabled) return 0;
|
||||
|
||||
if (g->nat_tun_ip.family == AF_INET) {
|
||||
ctx->gateway_ip = ntohl(g->nat_tun_ip.addr.v4.s_addr);
|
||||
} else if (g->tun_ip.family == AF_INET) {
|
||||
ctx->gateway_ip = ntohl(g->tun_ip.addr.v4.s_addr);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No valid IP for NAT gateway");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->port_start = g->nat_port_start;
|
||||
ctx->port_end = g->nat_port_end;
|
||||
if (ctx->port_start == 0 || ctx->port_end == 0 || ctx->port_start >= ctx->port_end) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Invalid NAT port range: %u-%u", ctx->port_start, ctx->port_end);
|
||||
return -1;
|
||||
}
|
||||
ctx->next_port = ctx->port_start;
|
||||
|
||||
ctx->table_size = EIM_NAT_TABLE_SIZE;
|
||||
ctx->table = u_calloc(ctx->table_size, sizeof(struct eim_nat_entry));
|
||||
if (!ctx->table) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to allocate NAT table"); return -1; }
|
||||
|
||||
for (int i = 0; i < g->nat_forward_count; i++) {
|
||||
uint16_t ext = g->nat_forwards[i].external_port;
|
||||
if (ext >= ctx->table_size) continue;
|
||||
struct eim_nat_entry* e = &ctx->table[ext];
|
||||
e->internal_ip = g->nat_forwards[i].internal_ip_host;
|
||||
e->internal_port = g->nat_forwards[i].internal_port_net;
|
||||
e->proto = g->nat_forwards[i].proto;
|
||||
e->state = EIM_NAT_ENTRY_STATIC;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "Port forward: %s:%u <- :%u (proto=%u)",
|
||||
ip_host_to_str(e->internal_ip).str, ntohs(e->internal_port), ext, e->proto);
|
||||
}
|
||||
|
||||
ctx->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT engine initialized: gw=%s ports=%u-%u",
|
||||
ip_host_to_str(ctx->gateway_ip).str, ctx->port_start, ctx->port_end);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eim_nat_destroy_ctx(struct eim_nat_ctx* ctx) {
|
||||
if (!ctx || !ctx->initialized) return;
|
||||
u_free(ctx->table);
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT engine destroyed");
|
||||
}
|
||||
|
||||
int eim_nat_add_forward(struct eim_nat_ctx* ctx, uint8_t proto,
|
||||
uint32_t internal_ip_host, uint16_t internal_port_net,
|
||||
uint16_t external_port) {
|
||||
if (!ctx || !ctx->initialized || external_port >= ctx->table_size) return -1;
|
||||
if (ctx->table[external_port].state != EIM_NAT_ENTRY_FREE) return -1;
|
||||
struct eim_nat_entry* e = &ctx->table[external_port];
|
||||
e->internal_ip = internal_ip_host;
|
||||
e->internal_port = internal_port_net;
|
||||
e->proto = proto;
|
||||
e->state = EIM_NAT_ENTRY_STATIC;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "Port forward: %s:%u <- :%u (proto=%u)",
|
||||
ip_host_to_str(internal_ip_host).str, ntohs(internal_port_net), external_port, proto);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef EIM_NAT_H
|
||||
#define EIM_NAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct ETCP_CONN;
|
||||
|
||||
#define EIM_NAT_TABLE_SIZE 65536
|
||||
|
||||
#define EIM_NAT_ENTRY_FREE 0
|
||||
#define EIM_NAT_ENTRY_ACTIVE 1
|
||||
#define EIM_NAT_ENTRY_STATIC 2
|
||||
|
||||
#define IPPROTO_TCP_UINT8 6
|
||||
#define IPPROTO_UDP_UINT8 17
|
||||
#define IPPROTO_ICMP_UINT8 1
|
||||
|
||||
struct eim_nat_entry {
|
||||
uint32_t internal_ip;
|
||||
uint16_t internal_port;
|
||||
uint8_t proto;
|
||||
uint8_t state;
|
||||
uint64_t src_node_id; // which node sent the original packet
|
||||
uint64_t last_seen;
|
||||
struct ETCP_CONN* src_conn; // cached connection for sendback
|
||||
};
|
||||
|
||||
// Pure NAT engine state (no transport/TUN/ETCP fields)
|
||||
struct eim_nat_ctx {
|
||||
uint32_t gateway_ip;
|
||||
uint16_t port_start;
|
||||
uint16_t port_end;
|
||||
uint16_t next_port;
|
||||
struct eim_nat_entry* table;
|
||||
size_t table_size;
|
||||
int initialized;
|
||||
};
|
||||
|
||||
struct global_config;
|
||||
|
||||
int eim_nat_init_ctx(struct eim_nat_ctx* ctx, const struct global_config* g);
|
||||
void eim_nat_destroy_ctx(struct eim_nat_ctx* ctx);
|
||||
|
||||
int eim_nat_egress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len,
|
||||
uint64_t src_node_id, struct ETCP_CONN* src_conn);
|
||||
int eim_nat_ingress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len,
|
||||
struct eim_nat_entry** out_entry);
|
||||
|
||||
int eim_nat_add_forward(struct eim_nat_ctx* ctx, uint8_t proto,
|
||||
uint32_t internal_ip_host, uint16_t internal_port_net,
|
||||
uint16_t external_port);
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,7 @@
|
||||
// ETCP packet IDs
|
||||
#define ETCP_ID_DATA 0x00 // Пакет для передачи адресату
|
||||
#define ETCP_ID_ROUTE_ENTRY 0x01 // Элемент роутинг-таблицы
|
||||
#define ETCP_ID_NAT 0x02 // NAT трафик между узлами
|
||||
|
||||
// Forward declarations
|
||||
struct ETCP_CONN;
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#include "nat_transport.h"
|
||||
#include "eim_nat.h"
|
||||
#include "etcp.h"
|
||||
#include "utun_instance.h"
|
||||
#include "config_parser.h"
|
||||
#include "tun_if.h"
|
||||
#include "etcp_api.h"
|
||||
#include "route_bgp.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include <string.h>
|
||||
|
||||
#define NAT_HDR_SIZE 17 // cmd(1) + src_node_id(8) + dst_node_id(8)
|
||||
|
||||
static ip_str_t ip_host_to_str(uint32_t ip_host) {
|
||||
struct in_addr a; a.s_addr = htonl(ip_host);
|
||||
return ip_to_str(&a, AF_INET);
|
||||
}
|
||||
|
||||
// ==================== Callbacks ====================
|
||||
|
||||
// CLIENT: NAT TUN output → encapsulate in ETCP_ID_NAT → send to provider
|
||||
static void nat_transport_client_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
|
||||
if (!inst) { queue_resume_callback(q); return; }
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
struct eim_nat_ctx* ctx = &inst->nat;
|
||||
|
||||
struct ll_entry* pkt = queue_data_get(q);
|
||||
if (!pkt) { queue_resume_callback(q); return; }
|
||||
|
||||
if (!pkt->dgram || pkt->len < 2) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
if (!tr->nat_via_conn) {
|
||||
tr->nat_via_conn = route_bgp_find_conn_for_node(inst->bgp, tr->nat_via_node_id);
|
||||
if (!tr->nat_via_conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "No connection to NAT provider %016llx, dropping",
|
||||
(unsigned long long)tr->nat_via_node_id);
|
||||
queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ip_len = pkt->len - 1;
|
||||
size_t total_len = NAT_HDR_SIZE + ip_len;
|
||||
uint8_t* new_dgram = u_malloc(total_len);
|
||||
if (!new_dgram) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
new_dgram[0] = ETCP_ID_NAT;
|
||||
memcpy(new_dgram + 1, &tr->self_node_id, 8);
|
||||
memcpy(new_dgram + 9, &tr->nat_via_node_id, 8);
|
||||
memcpy(new_dgram + 17, pkt->dgram + 1, ip_len);
|
||||
|
||||
struct ll_entry* new_entry = queue_entry_new(0);
|
||||
if (!new_entry) { u_free(new_dgram); queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
new_entry->dgram = new_dgram;
|
||||
new_entry->len = total_len;
|
||||
|
||||
queue_dgram_free(pkt); queue_entry_free(pkt);
|
||||
queue_resume_callback(q);
|
||||
|
||||
int ret = etcp_send(tr->nat_via_conn, new_entry);
|
||||
if (ret != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "etcp_send to provider failed");
|
||||
queue_entry_free(new_entry); queue_dgram_free(new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// PROVIDER: NAT TUN output (internet response) → ingress NAT → encapsulate ETCP_ID_NAT → send back
|
||||
static void nat_transport_provider_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
|
||||
if (!inst) { queue_resume_callback(q); return; }
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
struct eim_nat_ctx* ctx = &inst->nat;
|
||||
|
||||
struct ll_entry* pkt = queue_data_get(q);
|
||||
if (!pkt) { queue_resume_callback(q); return; }
|
||||
if (!pkt->dgram || pkt->len < 2) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
struct eim_nat_entry* entry = NULL;
|
||||
int ret = eim_nat_ingress(ctx, pkt->dgram + 1, pkt->len - 1, &entry);
|
||||
if (ret != 0 || !entry || !entry->src_conn) {
|
||||
if (ret == 0) DEBUG_WARN(DEBUG_CATEGORY_NAT, "Ingress NAT: no matching entry, dropping");
|
||||
queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ip_len = pkt->len - 1;
|
||||
size_t total_len = NAT_HDR_SIZE + ip_len;
|
||||
uint8_t* new_dgram = u_malloc(total_len);
|
||||
if (!new_dgram) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
new_dgram[0] = ETCP_ID_NAT;
|
||||
memcpy(new_dgram + 1, &tr->self_node_id, 8);
|
||||
memcpy(new_dgram + 9, &entry->src_node_id, 8);
|
||||
memcpy(new_dgram + 17, pkt->dgram + 1, ip_len);
|
||||
|
||||
struct ll_entry* new_entry = queue_entry_new(0);
|
||||
if (!new_entry) { u_free(new_dgram); queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
new_entry->dgram = new_dgram;
|
||||
new_entry->len = total_len;
|
||||
|
||||
queue_dgram_free(pkt); queue_entry_free(pkt);
|
||||
queue_resume_callback(q);
|
||||
|
||||
int send_ret = etcp_send(entry->src_conn, new_entry);
|
||||
if (send_ret != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "etcp_send back to node %016llx failed",
|
||||
(unsigned long long)entry->src_node_id);
|
||||
queue_entry_free(new_entry); queue_dgram_free(new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// ETCP_ID_NAT receive: CLIENT gets response, PROVIDER gets request
|
||||
static void nat_transport_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!conn || !entry || !entry->dgram || entry->len < NAT_HDR_SIZE) {
|
||||
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); }
|
||||
return;
|
||||
}
|
||||
struct UTUN_INSTANCE* inst = conn->instance;
|
||||
if (!inst) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
struct eim_nat_ctx* ctx = &inst->nat;
|
||||
if (!ctx->initialized) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
|
||||
uint64_t src_node_id, dst_node_id;
|
||||
memcpy(&src_node_id, entry->dgram + 1, 8);
|
||||
memcpy(&dst_node_id, entry->dgram + 9, 8);
|
||||
uint8_t* ip_data = entry->dgram + NAT_HDR_SIZE;
|
||||
size_t ip_len = entry->len - NAT_HDR_SIZE;
|
||||
|
||||
if (tr->nat_via_node_id != 0) {
|
||||
// CLIENT: response from provider → write to NAT TUN
|
||||
if (tr->nat_tun) {
|
||||
tun_write(tr->nat_tun, entry->dgram + NAT_HDR_SIZE - 1, ip_len + 1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT client: received %zu bytes from provider", ip_len);
|
||||
}
|
||||
} else {
|
||||
// PROVIDER: request from client → egress NAT → write to NAT TUN
|
||||
int ret = eim_nat_egress(ctx, ip_data, ip_len, src_node_id, conn);
|
||||
if (ret < 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "Egress NAT failed");
|
||||
} else if (ret == 0 && tr->nat_tun) {
|
||||
tun_write(tr->nat_tun, entry->dgram + NAT_HDR_SIZE - 1, ip_len + 1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT provider: sent %zu bytes to internet", ip_len);
|
||||
}
|
||||
}
|
||||
|
||||
queue_entry_free(entry); queue_dgram_free(entry);
|
||||
}
|
||||
|
||||
// ==================== Init / Destroy ====================
|
||||
|
||||
int nat_transport_init(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !inst->config) return -1;
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
struct global_config* g = &inst->config->global;
|
||||
|
||||
memset(tr, 0, sizeof(*tr));
|
||||
if (!g->nat_enabled) return 0;
|
||||
|
||||
tr->self_node_id = inst->node_id;
|
||||
tr->nat_via_node_id = g->nat_via_node_id;
|
||||
|
||||
// Initialize NAT engine (table, forwards, gateway_ip) — only provider needs full init.
|
||||
// Client also gets minimal init so ctx->initialized==1 for callback check.
|
||||
if (tr->nat_via_node_id == 0) {
|
||||
if (eim_nat_init_ctx(&inst->nat, g) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to init NAT engine");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// Client: minimal init — mark as initialized without allocating table
|
||||
inst->nat.initialized = 1;
|
||||
}
|
||||
|
||||
// Create NAT TUN
|
||||
const char* tun_name = g->nat_tun_ifname[0] ? g->nat_tun_ifname : "tun_nat";
|
||||
char ip_str[64] = "";
|
||||
if (g->nat_tun_ip.family == AF_INET) {
|
||||
snprintf(ip_str, sizeof(ip_str), "%s", ip_to_str(&g->nat_tun_ip.addr.v4, AF_INET).str);
|
||||
} else if (g->tun_ip.family == AF_INET) {
|
||||
snprintf(ip_str, sizeof(ip_str), "%s", ip_to_str(&g->tun_ip.addr.v4, AF_INET).str);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No NAT TUN IP");
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
return -1;
|
||||
}
|
||||
tr->nat_tun = tun_init_nat(inst->ua, tun_name, ip_str, g->mtu, g->tun_test_mode);
|
||||
if (!tr->nat_tun) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to create NAT TUN %s ip=%s", tun_name, ip_str);
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind ETCP_ID_NAT
|
||||
if (etcp_bind(inst, ETCP_ID_NAT, nat_transport_etcp_recv_cb) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to bind ETCP_ID_NAT");
|
||||
tun_close(tr->nat_tun); tr->nat_tun = NULL;
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Role-specific TUN callback
|
||||
struct eim_nat_ctx* ctx = &inst->nat;
|
||||
if (tr->nat_via_node_id != 0) {
|
||||
if (tr->nat_tun->output_queue) queue_set_callback(tr->nat_tun->output_queue, nat_transport_client_tun_out_cb, inst);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT client via node %016llx, TUN=%s gw=%s ports=%u-%u",
|
||||
(unsigned long long)tr->nat_via_node_id, tun_name,
|
||||
ip_host_to_str(ctx->gateway_ip).str, ctx->port_start, ctx->port_end);
|
||||
} else {
|
||||
if (tr->nat_tun->output_queue) queue_set_callback(tr->nat_tun->output_queue, nat_transport_provider_tun_out_cb, inst);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT provider TUN=%s gw=%s ports=%u-%u",
|
||||
tun_name, ip_host_to_str(ctx->gateway_ip).str, ctx->port_start, ctx->port_end);
|
||||
}
|
||||
|
||||
tr->initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nat_transport_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !inst->nat_tr.initialized) return;
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
|
||||
etcp_unbind(inst, ETCP_ID_NAT);
|
||||
|
||||
if (tr->nat_tun) { tun_close(tr->nat_tun); tr->nat_tun = NULL; }
|
||||
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
|
||||
memset(tr, 0, sizeof(*tr));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT transport destroyed");
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// nat_transport.h — NAT transport layer (TUN + ETCP protocol handling)
|
||||
#ifndef NAT_TRANSPORT_H
|
||||
#define NAT_TRANSPORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct tun_if;
|
||||
struct ETCP_CONN;
|
||||
|
||||
struct nat_transport_ctx {
|
||||
uint64_t self_node_id;
|
||||
uint64_t nat_via_node_id;
|
||||
struct tun_if* nat_tun;
|
||||
struct ETCP_CONN* nat_via_conn;
|
||||
int initialized;
|
||||
};
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
int nat_transport_init(struct UTUN_INSTANCE* inst);
|
||||
void nat_transport_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
#endif
|
||||
@@ -456,6 +456,21 @@ struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
|
||||
return e ? (struct NODEINFO_Q*)e : NULL;
|
||||
}
|
||||
|
||||
struct ETCP_CONN* route_bgp_find_conn_for_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
|
||||
if (!bgp) return NULL;
|
||||
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
|
||||
if (!nq || !nq->paths || !nq->paths->head) return NULL;
|
||||
struct ll_entry* e = nq->paths->head;
|
||||
struct NODEINFO_PATH* best = NULL;
|
||||
uint8_t best_hops = 255;
|
||||
while (e) {
|
||||
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
|
||||
if (path->conn && path->hop_count < best_hops) { best = path; best_hops = path->hop_count; }
|
||||
e = e->next;
|
||||
}
|
||||
return best ? best->conn : NULL;
|
||||
}
|
||||
|
||||
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count) {
|
||||
if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args");
|
||||
|
||||
@@ -158,6 +158,17 @@ void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
*/
|
||||
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
/**
|
||||
* @brief Поиск оптимального ETCP соединения для указанного node_id.
|
||||
*
|
||||
* Перебирает paths узла, выбирает путь с минимальным hop_count.
|
||||
*
|
||||
* @param bgp указатель на ROUTE_BGP
|
||||
* @param node_id целевой узел
|
||||
* @return оптимальный ETCP_CONN* или NULL
|
||||
*/
|
||||
struct ETCP_CONN* route_bgp_find_conn_for_node(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
/**
|
||||
* @brief Добавляет путь (conn) в paths узла.
|
||||
*
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ static uint32_t extract_dst_ip(uint8_t* data, size_t len) {
|
||||
}
|
||||
|
||||
// entry format: <id 1 byte> <ip_packet ...>
|
||||
static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t src_node_id) {
|
||||
void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t src_node_id) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "");
|
||||
if (!instance || !entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_pkt: invalid arguments: instance=%p entry=%p entry->len=%zu",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// Forward declarations
|
||||
struct ETCP_CONN;
|
||||
struct UTUN_INSTANCE;
|
||||
struct ll_entry;
|
||||
|
||||
/**
|
||||
* @brief Initialize routing module for instance
|
||||
@@ -43,4 +44,9 @@ void routing_del_conn(struct ETCP_CONN* etcp);
|
||||
*/
|
||||
void routing_set_tun(struct UTUN_INSTANCE* instance);
|
||||
|
||||
/**
|
||||
* @brief Route a single packet (exposed for NAT module interception)
|
||||
*/
|
||||
void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t src_node_id);
|
||||
|
||||
#endif // ROUTING_H
|
||||
@@ -204,6 +204,64 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Init NAT TUN (separate TUN interface for NAT traffic)
|
||||
// ===================================================================
|
||||
struct tun_if* tun_init_nat(struct UASYNC* ua, const char* ifname, const char* ip_addr_str, int mtu, int test_mode)
|
||||
{
|
||||
if (!ua || !ifname || !ip_addr_str) return NULL;
|
||||
if (mtu <= 0) mtu = TUN_MTU_DEFAULT;
|
||||
|
||||
struct tun_if* tun = u_calloc(1, sizeof(struct tun_if));
|
||||
if (!tun) return NULL;
|
||||
|
||||
tun->ua = ua;
|
||||
tun->test_mode = test_mode;
|
||||
tun->fd = -1;
|
||||
strncpy(tun->ifname, ifname, sizeof(tun->ifname) - 1);
|
||||
|
||||
char ip_str[64];
|
||||
snprintf(ip_str, sizeof(ip_str), "%s/32", ip_addr_str);
|
||||
|
||||
if (!test_mode) {
|
||||
if (tun_platform_init(tun, tun->ifname, ip_str, mtu) != 0) {
|
||||
u_free(tun);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tun->pool = memory_pool_init(sizeof(struct ll_entry));
|
||||
if (!tun->pool) goto fail2;
|
||||
|
||||
tun->output_queue = queue_new(ua, 0, "NAT TUN output");
|
||||
tun->input_queue = queue_new(ua, 0, "NAT TUN input");
|
||||
if (!tun->output_queue || !tun->input_queue) goto fail2;
|
||||
|
||||
queue_set_callback(tun->input_queue, tun_input_queue_callback, tun);
|
||||
|
||||
if (!test_mode) {
|
||||
int poll_fd = tun_platform_get_poll_fd(tun);
|
||||
if (poll_fd >= 0) {
|
||||
#ifndef _WIN32
|
||||
tun->socket_id = uasync_add_socket(ua, poll_fd, tun_read_callback, NULL, NULL, tun);
|
||||
if (!tun->socket_id) goto fail2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_TUN, "NAT TUN %s initialized (%s mode)", tun->ifname,
|
||||
test_mode ? "TEST" : "REAL");
|
||||
return tun;
|
||||
|
||||
fail2:
|
||||
if (tun->output_queue) queue_free(tun->output_queue);
|
||||
if (tun->input_queue) queue_free(tun->input_queue);
|
||||
if (tun->pool) memory_pool_destroy(tun->pool);
|
||||
if (!test_mode) tun_platform_cleanup(tun);
|
||||
u_free(tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Остальные функции (без изменений)
|
||||
// ===================================================================
|
||||
|
||||
@@ -75,6 +75,17 @@ struct tun_if {
|
||||
*/
|
||||
struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config);
|
||||
|
||||
/**
|
||||
* @brief Инициализация TUN интерфейса с явными параметрами (для NAT TUN)
|
||||
* @param ua экземпляр uasync
|
||||
* @param ifname имя интерфейса
|
||||
* @param ip_addr IP-адрес (строка, например "10.0.1.1")
|
||||
* @param mtu MTU интерфейса
|
||||
* @param test_mode тестовый режим
|
||||
* @return указатель на tun_if или NULL при ошибке
|
||||
*/
|
||||
struct tun_if* tun_init_nat(struct UASYNC* ua, const char* ifname, const char* ip_addr, int mtu, int test_mode);
|
||||
|
||||
/**
|
||||
* @brief Закрытие и освобождение всех ресурсов
|
||||
*/
|
||||
|
||||
@@ -297,6 +297,11 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
|
||||
// Cleanup firewall
|
||||
fw_free(&instance->fw);
|
||||
|
||||
// Cleanup NAT
|
||||
if (instance->nat_tr.initialized) {
|
||||
nat_transport_destroy(instance);
|
||||
}
|
||||
|
||||
// Cleanup config
|
||||
if (instance->config) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration");
|
||||
@@ -361,6 +366,14 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
|
||||
routing_set_tun(instance);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "TUN interface registered in routing module");
|
||||
}
|
||||
|
||||
// Initialize NAT (after routing_set_tun, before connections)
|
||||
if (instance->config->global.nat_enabled) {
|
||||
int nat_ret = nat_transport_init(instance);
|
||||
if (nat_ret != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT transport init failed (non-fatal)");
|
||||
}
|
||||
}
|
||||
|
||||
// Note: TUN socket is already registered in tun_init()
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "etcp_api.h"
|
||||
#include "config_parser.h"
|
||||
#include "firewall.h"
|
||||
#include "eim_nat.h"
|
||||
#include "nat_transport.h"
|
||||
|
||||
// Forward declarations
|
||||
struct utun_config;
|
||||
@@ -84,6 +86,10 @@ struct UTUN_INSTANCE {
|
||||
// Firewall
|
||||
struct firewall_ctx fw;
|
||||
|
||||
// EIM NAT
|
||||
struct eim_nat_ctx nat;
|
||||
struct nat_transport_ctx nat_tr;
|
||||
|
||||
// Socket initialization status: 0=OK, 1=partial (some sockets failed), -1=error (none created)
|
||||
int socket_init_status;
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user