211 lines
8.5 KiB
C
211 lines
8.5 KiB
C
#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 "etcp_router.h"
|
|
#include "../routing_layer/topo_node.h"
|
|
#include "../lib/debug_config.h"
|
|
#include "../lib/mem.h"
|
|
#include "../lib/ll_queue.h"
|
|
#include <string.h>
|
|
|
|
#define NAT_SVC_HDR_SIZE 1 // send: svc_id(1) + ip_data (src/dst добавляет роутер)
|
|
|
|
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_RT_ID_NAT → send to provider via etcp_router
|
|
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 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; }
|
|
|
|
size_t ip_len = pkt->len - 1;
|
|
size_t total_len = NAT_SVC_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_RT_ID_NAT;
|
|
memcpy(new_dgram + 1, 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_route_send(inst, TOPO_GROUP_UTUN, tr->nat_via_node_id, new_entry, 0, 0);
|
|
if (ret != 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT client: etcp_route_send to provider %016llx failed",
|
|
(unsigned long long)tr->nat_via_node_id);
|
|
queue_dgram_free(new_entry); queue_entry_free(new_entry);
|
|
}
|
|
}
|
|
|
|
// PROVIDER: NAT TUN output (internet response) → ingress NAT → send back via etcp_router
|
|
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 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_node_id == 0) {
|
|
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_SVC_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_RT_ID_NAT;
|
|
memcpy(new_dgram + 1, 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_route_send(inst, TOPO_GROUP_UTUN, entry->src_node_id, new_entry, 0, 0);
|
|
if (send_ret != 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT provider: etcp_route_send back to node %016llx failed",
|
|
(unsigned long long)entry->src_node_id);
|
|
queue_dgram_free(new_entry); queue_entry_free(new_entry);
|
|
}
|
|
}
|
|
|
|
// ETCP_RT_ID_NAT receive via etcp_router: 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 < ROUTER_SVC_HDR_SIZE) {
|
|
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
|
return;
|
|
}
|
|
struct UTUN_INSTANCE* inst = conn->instance;
|
|
if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
|
struct nat_transport_ctx* tr = &inst->nat_tr;
|
|
struct eim_nat_ctx* ctx = &inst->nat;
|
|
if (!tr->initialized || !ctx->initialized) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
|
|
|
uint64_t src_node_id;
|
|
memcpy(&src_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
|
uint8_t* ip_data = entry->dgram + ROUTER_SVC_PAYLOAD_OFF;
|
|
size_t ip_len = entry->len - ROUTER_SVC_PAYLOAD_OFF;
|
|
|
|
if (tr->nat_via_node_id != 0) {
|
|
if (tr->nat_tun) {
|
|
tun_write(tr->nat_tun, entry->dgram + ROUTER_SVC_PAYLOAD_OFF - 1, ip_len + 1);
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT client: received %zu bytes from provider", ip_len);
|
|
}
|
|
} else {
|
|
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 + ROUTER_SVC_PAYLOAD_OFF - 1, ip_len + 1);
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT provider: sent %zu bytes to internet", ip_len);
|
|
}
|
|
}
|
|
|
|
queue_dgram_free(entry); queue_entry_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;
|
|
|
|
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 {
|
|
inst->nat.initialized = 1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (etcp_router_bind(inst, ETCP_RT_ID_NAT, nat_transport_etcp_recv_cb) != 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to bind ETCP_RT_ID_NAT via etcp_router");
|
|
tun_close(tr->nat_tun); tr->nat_tun = NULL;
|
|
eim_nat_destroy_ctx(&inst->nat);
|
|
return -1;
|
|
}
|
|
|
|
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_router_unbind(inst, ETCP_RT_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");
|
|
}
|