Добавлен тестовый режим для TUN интерфейса и тест роутинга mesh

- Добавлена опция tun_test_mode в конфиг для работы без реального TUN устройства
- Добавлены функции для работы с очередями TUN в тестовом режиме
- Добавлен API для программного создания instance из структуры конфига
- Создан тест test_routing_mesh.c с 3-instance mesh топологией
- Ключи генерируются автоматически и распределяются между instance
This commit is contained in:
Evgeny committed 2026-02-15 20:37:10 +03:00
1 parent 602d9174a4
commit bee1e41b07
8 files changed
+1176 -33

No files matched your search

+5
View File
@@ -324,6 +324,10 @@ static int parse_global(const char *key, const char *value, struct global_config
global->enable_colors = atoi(value);
return 0;
}
if (strcmp(key, "tun_test_mode") == 0) {
global->tun_test_mode = atoi(value);
return 0;
}
return 0;
}
@@ -610,6 +614,7 @@ void print_config(const struct utun_config *cfg) {
printf(" TUN interface: %s\n", g->tun_ifname[0] ? g->tun_ifname : "auto");
printf(" TUN IP: %s\n", ip_to_string(&g->tun_ip, ip_buffer, sizeof(ip_buffer)));
printf(" MTU: %d\n", g->mtu);
printf(" TUN test mode: %s\n", g->tun_test_mode ? "enabled" : "disabled");
printf(" Net debug: %d\n", g->net_debug);
printf("\nServers:\n");
+2
View File
@@ -74,6 +74,8 @@ struct global_config {
int enable_function_names; // enable function names in logs
int enable_file_lines; // enable file:line in logs
int enable_colors; // enable ANSI colors in logs
int tun_test_mode; // test mode: 1 = don't open real TUN, queues only
};
struct utun_config {
+180 -33
View File
@@ -111,6 +111,9 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
return NULL;
}
// Check for test mode
int test_mode = config->global.tun_test_mode;
// Allocate tun_if structure
struct tun_if* tun = calloc(1, sizeof(struct tun_if));
if (!tun) {
@@ -122,6 +125,7 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
tun->fd = -1;
tun->platform_handle = NULL;
tun->adapter_handle = NULL;
tun->test_mode = test_mode;
// Get interface name from config (or empty for auto)
const char* ifname = config->global.tun_ifname;
@@ -147,18 +151,28 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
// Get MTU
int mtu = config->global.mtu > 0 ? config->global.mtu : TUN_MTU_DEFAULT;
// Platform-specific initialization
if (tun_platform_init(tun, tun->ifname, tun_ip_str, mtu) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Platform-specific TUN initialization failed");
free(tun);
return NULL;
// Platform-specific initialization (skip in test mode)
if (!test_mode) {
if (tun_platform_init(tun, tun->ifname, tun_ip_str, mtu) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Platform-specific TUN initialization failed");
free(tun);
return NULL;
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN test mode: skipping platform initialization");
// In test mode, set a fake interface name if not provided
if (tun->ifname[0] == '\0') {
strncpy(tun->ifname, "tun_test", sizeof(tun->ifname) - 1);
}
}
// Create memory pool for ETCP_FRAGMENT
tun->pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT));
if (!tun->pool) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create memory pool");
tun_platform_cleanup(tun);
if (!test_mode) {
tun_platform_cleanup(tun);
}
free(tun);
return NULL;
}
@@ -168,7 +182,9 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
if (!tun->output_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create output queue");
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
if (!test_mode) {
tun_platform_cleanup(tun);
}
free(tun);
return NULL;
}
@@ -179,7 +195,9 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create input queue");
queue_free(tun->output_queue);
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
if (!test_mode) {
tun_platform_cleanup(tun);
}
free(tun);
return NULL;
}
@@ -187,31 +205,38 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
// Set callback for input_queue - routing will put packets here
queue_set_callback(tun->input_queue, tun_input_queue_callback, tun);
// Register TUN socket with uasync
int poll_fd = tun_platform_get_poll_fd(tun);
if (poll_fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to get poll fd for TUN");
queue_free(tun->output_queue);
queue_free(tun->input_queue);
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
free(tun);
return NULL;
}
tun->socket_id = uasync_add_socket(ua, poll_fd, tun_read_callback, NULL, NULL, tun);
if (!tun->socket_id) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket with uasync");
queue_free(tun->output_queue);
queue_free(tun->input_queue);
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
free(tun);
return NULL;
// Register TUN socket with uasync (skip in test mode)
if (!test_mode) {
int poll_fd = tun_platform_get_poll_fd(tun);
if (poll_fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to get poll fd for TUN");
queue_free(tun->output_queue);
queue_free(tun->input_queue);
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
free(tun);
return NULL;
}
tun->socket_id = uasync_add_socket(ua, poll_fd, tun_read_callback, NULL, NULL, tun);
if (!tun->socket_id) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket with uasync");
queue_free(tun->output_queue);
queue_free(tun->input_queue);
memory_pool_destroy(tun->pool);
tun_platform_cleanup(tun);
free(tun);
return NULL;
}
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s (IP=%s, MTU=%d)",
tun->ifname, tun_ip_str, mtu);
if (test_mode) {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized in TEST MODE: %s (IP=%s, MTU=%d)",
tun->ifname, tun_ip_str, mtu);
} else {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s (IP=%s, MTU=%d)",
tun->ifname, tun_ip_str, mtu);
}
return tun;
}
@@ -259,8 +284,10 @@ void tun_close(struct tun_if* tun) {
tun->pool = NULL;
}
// Platform-specific cleanup
tun_platform_cleanup(tun);
// Platform-specific cleanup (skip in test mode)
if (!tun->test_mode) {
tun_platform_cleanup(tun);
}
free(tun);
}
@@ -271,6 +298,16 @@ ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len) {
return -1;
}
// In test mode, inject packet into output_queue instead of writing to interface
if (tun->test_mode) {
if (tun_inject_packet(tun, buf, len) == 0) {
return len;
} else {
tun->write_errors++;
return -1;
}
}
ssize_t nwritten = tun_platform_write(tun, buf, len);
if (nwritten < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to write to TUN device %s", tun->ifname);
@@ -285,3 +322,113 @@ ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len) {
return nwritten;
}
struct ll_queue* tun_get_input_queue(struct tun_if* tun) {
if (!tun) {
return NULL;
}
return tun->input_queue;
}
struct ll_queue* tun_get_output_queue(struct tun_if* tun) {
if (!tun) {
return NULL;
}
return tun->output_queue;
}
int tun_is_test_mode(struct tun_if* tun) {
if (!tun) {
return -1;
}
return tun->test_mode;
}
int tun_inject_packet(struct tun_if* tun, const uint8_t* buf, size_t len) {
if (!tun || !buf || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_inject_packet: invalid arguments");
return -1;
}
if (len > TUN_MAX_PACKET_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Packet too large: %zu bytes (max %d)",
len, TUN_MAX_PACKET_SIZE);
}
// Allocate packet data
uint8_t* packet_data = malloc(len);
if (!packet_data) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_inject_packet: failed to allocate packet data");
tun->read_errors++;
return -1;
}
memcpy(packet_data, buf, len);
// Allocate ETCP_FRAGMENT from pool
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(tun->pool);
if (!pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_inject_packet: failed to allocate ETCP_FRAGMENT");
free(packet_data);
tun->read_errors++;
return -1;
}
// Initialize fragment
pkt->seq = 0;
pkt->timestamp = 0;
pkt->ll.dgram = packet_data;
pkt->ll.len = len;
// Add to output queue (packets from TUN to routing)
if (queue_data_put(tun->output_queue, (struct ll_entry*)pkt, 0) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_inject_packet: failed to add packet to output queue");
free(packet_data);
memory_pool_free(tun->pool, pkt);
tun->read_errors++;
return -1;
}
// Update statistics
tun->bytes_read += len;
tun->packets_read++;
DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "Injected %zu bytes into TUN output queue %s", len, tun->ifname);
return 0;
}
ssize_t tun_read_packet(struct tun_if* tun, uint8_t* buf, size_t len) {
if (!tun || !buf || len == 0) {
return -1;
}
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue);
if (!pkt) {
return 0; // No packet available
}
size_t pkt_len = pkt->ll.len;
if (pkt_len > len) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "tun_read_packet: buffer too small (%zu > %zu)",
pkt_len, len);
// Put packet back - cannot read partial
// Note: This is a simplification; in real use, consider copying partial
queue_data_put(tun->input_queue, (struct ll_entry*)pkt, 0);
return -1;
}
// Copy data to user buffer
memcpy(buf, pkt->ll.dgram, pkt_len);
// Free packet
free(pkt->ll.dgram);
memory_pool_free(tun->pool, pkt);
// Update statistics
tun->bytes_written += pkt_len;
tun->packets_written++;
DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "Read %zu bytes from TUN input queue %s", pkt_len, tun->ifname);
return pkt_len;
}
+93
View File
@@ -30,6 +30,7 @@ struct tun_if {
struct memory_pool* pool; // Pool for ETCP_FRAGMENT structures
struct ll_queue* output_queue; // Queue for incoming packets from TUN (to routing)
struct ll_queue* input_queue; // Queue for outgoing packets to TUN (from routing)
int test_mode; // Test mode flag: 1 = no real TUN, queues only
// Statistics
uint64_t bytes_read; // Bytes read from TUN
uint64_t bytes_written; // Bytes written to TUN
@@ -67,6 +68,98 @@ void tun_close(struct tun_if* tun);
*/
ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len);
/**
* @brief Get TUN input queue (for sending packets to TUN from routing)
* @param tun TUN interface handle
* @return Pointer to input queue, NULL if tun is NULL
*
* In normal mode: routing layer puts packets here, TUN reads and sends to interface
* In test mode: tests can put packets here directly to simulate routing output
*/
struct ll_queue* tun_get_input_queue(struct tun_if* tun);
/**
* @brief Get TUN output queue (for receiving packets from TUN to routing)
* @param tun TUN interface handle
* @return Pointer to output queue, NULL if tun is NULL
*
* In normal mode: TUN receives packets from interface and puts here for routing
* In test mode: tests can read packets from here that were "sent" to TUN
*/
struct ll_queue* tun_get_output_queue(struct tun_if* tun);
/**
* @brief Check if TUN interface is in test mode
* @param tun TUN interface handle
* @return 1 if test mode, 0 if normal mode, -1 if tun is NULL
*/
int tun_is_test_mode(struct tun_if* tun);
/**
* @brief Write packet directly to TUN output queue (test mode helper)
* @param tun TUN interface handle
* @param buf Packet data
* @param len Packet size
* @return 0 on success, -1 on error
*
* This function simulates receiving a packet from the TUN interface.
* The packet is placed in the output_queue where routing layer would normally find it.
* Works in both test and normal modes.
*/
int tun_inject_packet(struct tun_if* tun, const uint8_t* buf, size_t len);
/**
* @brief Read packet directly from TUN input queue (test mode helper)
* @param tun TUN interface handle
* @param buf Output buffer for packet data
* @param len Output buffer size
* @return Number of bytes read, 0 if no packet available, -1 on error
*
* This function simulates what the TUN interface would normally do:
* take a packet from input_queue and return it.
* Works in both test and normal modes.
*/
ssize_t tun_read_packet(struct tun_if* tun, uint8_t* buf, size_t len);
/**
* @brief Test mode usage example and documentation
*
* To use TUN in test mode, set tun_test_mode=1 in the [global] section
* of the configuration file:
*
* [global]
* tun_test_mode=1
* tun_ip=10.0.0.1/24
*
* Example test code:
*
* // Initialize TUN in test mode
* struct tun_if* tun = tun_init(ua, config);
* if (!tun || !tun_is_test_mode(tun)) {
* // handle error
* }
*
* // Simulate receiving packet from TUN (as if from network)
* uint8_t packet[] = { ... }; // IP packet
* tun_inject_packet(tun, packet, sizeof(packet));
*
* // Routing layer will find this packet in output_queue
* struct ll_queue* out_q = tun_get_output_queue(tun);
* struct ETCP_FRAGMENT* pkt = queue_data_get(out_q);
*
* // Simulate sending packet to TUN (as if from routing)
* struct ll_queue* in_q = tun_get_input_queue(tun);
* // Put packet in input_queue, TUN will process it
*
* In test mode:
* - No real TUN device is created
* - No system calls to configure network interface
* - No socket is registered with uasync
* - Memory pool and queues are still created normally
* - All statistics are tracked normally
* - tun_write() still works (writes to platform, but no real interface)
*/
// Platform-specific internal functions (implemented in tun_linux.c / tun_windows.c)
int tun_platform_init(struct tun_if* tun, const char* ifname, const char* ip_str, int mtu);
void tun_platform_cleanup(struct tun_if* tun);
+89
View File
@@ -146,6 +146,95 @@ struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config
return instance;
}
// Create instance from existing config structure (config ownership transfers to instance)
struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config) {
if (!config) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "utun_instance_create_from_config: NULL config");
return NULL;
}
struct UTUN_INSTANCE *instance = calloc(1, sizeof(struct UTUN_INSTANCE));
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate UTUN_INSTANCE");
return NULL;
}
// Initialize basic fields
instance->running = 0;
instance->ua = ua;
instance->config = config; // Transfer ownership
// Set node_id from config
instance->node_id = config->global.my_node_id;
// Set my keys
if (sc_init_local_keys(&instance->my_keys, config->global.my_public_key_hex, config->global.my_private_key_hex) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize local keys");
free(instance);
return NULL;
}
instance->ack_pool = memory_pool_init(sizeof(struct ACK_PACKET));
instance->data_pool = memory_pool_init(PACKET_DATA_SIZE);
instance->pkt_pool = memory_pool_init(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE);
// Create routing module
if (routing_create(instance) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create routing module");
free(instance);
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created");
// Add local subnets from config as static routes
struct CFG_ROUTE_ENTRY* subnet = config->my_subnets;
while (subnet) {
struct ROUTE_ENTRY entry = {0};
entry.network = subnet->ip.addr.v4.s_addr;
entry.prefix_length = subnet->netmask;
entry.next_hop = NULL;
entry.type = ROUTE_TYPE_LOCAL;
entry.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_VALIDATED;
entry.metrics.bandwidth_kbps = UINT32_MAX;
entry.metrics.latency_ms = 0;
entry.metrics.packet_loss_rate = 0;
entry.metrics.hop_count = 0;
if (route_table_insert(instance->rt, &entry)) {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &entry.network, ip_str, sizeof(ip_str));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d",
ip_str, entry.prefix_length);
}
subnet = subnet->next;
}
// Initialize TUN device if enabled
if (g_tun_init_enabled) {
instance->tun = tun_init(ua, config);
if (!instance->tun) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to initialize TUN device");
free(instance);
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname);
} else {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup");
instance->tun = NULL;
}
// Initialize BGP module
instance->bgp = route_bgp_init(instance);
if (!instance->bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module");
} else {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized");
}
return instance;
}
// Destroy instance and cleanup resources
void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
if (!instance) return;
+1
View File
@@ -57,6 +57,7 @@ struct UTUN_INSTANCE {
// Functions
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file);
struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config);
void utun_instance_destroy(struct UTUN_INSTANCE* instance);
int utun_instance_init(struct UTUN_INSTANCE *instance);
void utun_instance_run(struct UTUN_INSTANCE *instance);