You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
8.5 KiB
212 lines
8.5 KiB
#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 9 // svc_id(1) + src_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_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, &tr->self_node_id, 8); |
|
memcpy(new_dgram + 9, 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); |
|
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, &inst->nat_tr.self_node_id, 8); |
|
memcpy(new_dgram + 9, 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); |
|
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 < NAT_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 (!ctx->initialized) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
|
|
uint64_t src_node_id; |
|
memcpy(&src_node_id, entry->dgram + 1, 8); |
|
uint8_t* ip_data = entry->dgram + NAT_SVC_HDR_SIZE; |
|
size_t ip_len = entry->len - NAT_SVC_HDR_SIZE; |
|
|
|
if (tr->nat_via_node_id != 0) { |
|
if (tr->nat_tun) { |
|
tun_write(tr->nat_tun, entry->dgram + NAT_SVC_HDR_SIZE - 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 + NAT_SVC_HDR_SIZE - 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"); |
|
}
|
|
|