Browse Source
- Add route_bgp.c/h with BGP-like route exchange functionality - Implement route_bgp_init/destroy for module lifecycle - Add route_bgp_new_conn to send routing table on connection - Implement route_bgp_receive_cbk for processing incoming routes - Add route_table_delete_entry for individual route removal - Extend ROUTE_ENTRY with endpoint_ip, endpoint_port, destination_node_id - Add DEBUG_CATEGORY_BGP to debug_config.h - Integrate BGP initialization into utun_instance_create - Call route_bgp_new_conn from etcp_connections on link init - Create integration test test_bgp_route_exchange.c - Add route_bgp_delete_entry tests to test_route_lib.c - Update Makefiles to include new module Route exchange tested and working in both directions (client-server)nodeinfo-routing-update
15 changed files with 1044 additions and 22 deletions
@ -0,0 +1,323 @@
|
||||
/**
|
||||
* @file route_bgp.c |
||||
* @brief Модуль обмена роутинг-таблицами между узлами (BGP-like) |
||||
*
|
||||
* Функции: |
||||
* - Прием и обработка роутинг-пакетов от других узлов |
||||
* - Рассылка локальной таблицы при подключении нового узла |
||||
* - Обновление таблицы при получении изменений |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
#include "route_bgp.h" |
||||
#include "route_lib.h" |
||||
#include "etcp_api.h" |
||||
#include "etcp.h" |
||||
#include "utun_instance.h" |
||||
#include "../lib/debug_config.h" |
||||
|
||||
// Размер пакета (без заголовка ETCP)
|
||||
#define BGP_PACKET_SIZE (sizeof(struct ROUTE_BGP_PACKET)) |
||||
|
||||
// Внутренняя структура для элемента списка соединений
|
||||
struct bgp_conn_item { |
||||
struct ETCP_CONN* conn; |
||||
struct bgp_conn_item* next; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Колбэк для приема роутинг-пакетов от ETCP |
||||
*
|
||||
* Вызывается когда приходит пакет с ETCP_ID_ROUTE_ENTRY. |
||||
* Парсит пакет и добавляет маршрут в таблицу. |
||||
*/ |
||||
static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) |
||||
{ |
||||
if (!from_conn || !entry || !entry->dgram || entry->len < sizeof(struct ROUTE_BGP_PACKET)) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid BGP packet received"); |
||||
if (entry) { |
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
struct UTUN_INSTANCE* instance = from_conn->instance; |
||||
if (!instance || !instance->rt) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "No instance or routing table"); |
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
return; |
||||
} |
||||
|
||||
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram; |
||||
|
||||
// Проверяем команду
|
||||
if (pkt->cmd != ETCP_ID_ROUTE_ENTRY) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP command: %d", pkt->cmd); |
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
return; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet: subcmd=%d, network=%08x/%d, node_id=%016llx", |
||||
pkt->subcmd, ntohl(pkt->network), pkt->prefix_length & 0x3F, (unsigned long long)pkt->node_id); |
||||
|
||||
switch (pkt->subcmd) { |
||||
case ROUTE_SUBCMD_ENTRY: { |
||||
// Создаем запись маршрута
|
||||
struct ROUTE_ENTRY route; |
||||
memset(&route, 0, sizeof(route)); |
||||
|
||||
route.network = ntohl(pkt->network); |
||||
route.prefix_length = pkt->prefix_length & 0x3F; // Младшие 6 бит - длина префикса
|
||||
route.next_hop = from_conn; |
||||
route.type = ROUTE_TYPE_LEARNED; |
||||
route.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_LEARNED; |
||||
|
||||
// Метрики
|
||||
route.metrics.hop_count = pkt->hop_count + 1; // Инкрементируем
|
||||
route.metrics.latency_ms = pkt->latency / 10; // x0.1 ms -> ms
|
||||
route.metrics.bandwidth_kbps = ntohl(pkt->bandwidth_kbps); |
||||
route.metrics.last_updated = 0; // Будет установлено при вставке
|
||||
|
||||
// BGP-специфичные поля
|
||||
route.endpoint_ip = ntohl(pkt->endpoint_ip); |
||||
route.endpoint_port = ntohs(pkt->endpoint_port); |
||||
route.destination_node_id = pkt->node_id; |
||||
|
||||
// Проверяем флаги
|
||||
if (pkt->prefix_length & ROUTE_PREFIX_F_LOCAL) { |
||||
// Локальный маршрут - не пересылаем
|
||||
route.flags |= ROUTE_FLAG_ADVERTISED; |
||||
} |
||||
|
||||
// Вставляем в таблицу
|
||||
if (route_table_insert(instance->rt, &route)) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added learned route: %08x/%d via node %016llx (hops=%d)", |
||||
route.network, route.prefix_length,
|
||||
(unsigned long long)route.destination_node_id, |
||||
route.metrics.hop_count); |
||||
} else { |
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert route: %08x/%d", |
||||
route.network, route.prefix_length); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
case ROUTE_SUBCMD_WITHDRAW: { |
||||
// Удаление маршрута (withdrawal)
|
||||
uint32_t network = ntohl(pkt->network); |
||||
uint8_t prefix_length = pkt->prefix_length & 0x3F; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing route withdrawal: %08x/%d from conn %p", |
||||
network, prefix_length, (void*)from_conn); |
||||
|
||||
if (route_table_delete_entry(instance->rt, network, prefix_length, from_conn)) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route withdrawn successfully: %08x/%d",
|
||||
network, prefix_length); |
||||
} else { |
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Route to withdraw not found: %08x/%d", |
||||
network, prefix_length); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
case ROUTE_SUBCMD_REQUEST: { |
||||
// Запрос полной таблицы - отправляем всё
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Full table request received from connection"); |
||||
// Получаем bgp из instance (нужно добавить поле в instance)
|
||||
// Пока просто игнорируем - таблица отправляется при new_conn
|
||||
break; |
||||
} |
||||
|
||||
default: |
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd); |
||||
break; |
||||
} |
||||
|
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Отправить один маршрут через ETCP |
||||
*/ |
||||
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||
const struct ROUTE_ENTRY* route) |
||||
{ |
||||
if (!bgp || !conn || !route) { |
||||
return; |
||||
} |
||||
|
||||
// Пропускаем неактивные маршруты
|
||||
if (!(route->flags & ROUTE_FLAG_ACTIVE)) { |
||||
return; |
||||
} |
||||
|
||||
// Пропускаем локальные маршруты если они не для анонса
|
||||
if ((route->flags & ROUTE_FLAG_ADVERTISED) && (route->prefix_length & ROUTE_PREFIX_F_LOCAL)) { |
||||
return; |
||||
} |
||||
|
||||
// Создаем ETCP пакет
|
||||
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct ROUTE_BGP_PACKET)); |
||||
if (!entry) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate packet"); |
||||
return; |
||||
} |
||||
|
||||
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram; |
||||
pkt->cmd = ETCP_ID_ROUTE_ENTRY; |
||||
pkt->subcmd = ROUTE_SUBCMD_ENTRY; |
||||
pkt->hop_count = route->metrics.hop_count; |
||||
pkt->prefix_length = route->prefix_length; |
||||
|
||||
// Устанавливаем флаги
|
||||
if (route->endpoint_ip && route->endpoint_port) { |
||||
pkt->prefix_length |= ROUTE_PREFIX_F_DIRECT; |
||||
} |
||||
// Если это локальный маршрут - отмечаем (не будет пересылаться дальше)
|
||||
if (route->type == ROUTE_TYPE_LOCAL) { |
||||
pkt->prefix_length |= ROUTE_PREFIX_F_LOCAL; |
||||
} |
||||
|
||||
pkt->latency = htons(route->metrics.latency_ms * 10); // ms -> x0.1 ms
|
||||
pkt->endpoint_port = htons(route->endpoint_port); |
||||
pkt->endpoint_ip = htonl(route->endpoint_ip); |
||||
pkt->network = htonl(route->network); |
||||
pkt->bandwidth_kbps = htonl(route->metrics.bandwidth_kbps); |
||||
pkt->node_id = bgp->instance->node_id; // Наш node_id
|
||||
|
||||
entry->len = sizeof(struct ROUTE_BGP_PACKET); |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending route: %08x/%d to conn %p", |
||||
route->network, route->prefix_length & 0x3F, (void*)conn); |
||||
|
||||
// Отправляем через ETCP
|
||||
if (etcp_send(conn, entry) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to send route via ETCP"); |
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief Отправить полную таблицу указанному соединению |
||||
*/ |
||||
static void route_bgp_send_table_to_conn_internal(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) |
||||
{ |
||||
if (!bgp || !conn || !bgp->instance->rt) { |
||||
return; |
||||
} |
||||
|
||||
struct ROUTE_TABLE* rt = bgp->instance->rt; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending full routing table (%zu routes) to connection", |
||||
rt->count); |
||||
|
||||
// Отправляем все маршруты из таблицы
|
||||
for (size_t i = 0; i < rt->count; i++) { |
||||
route_bgp_send_route(bgp, conn, &rt->entries[i]); |
||||
} |
||||
} |
||||
|
||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) |
||||
{ |
||||
if (!instance) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot init BGP: no instance"); |
||||
return NULL; |
||||
} |
||||
|
||||
struct ROUTE_BGP* bgp = (struct ROUTE_BGP*)calloc(1, sizeof(struct ROUTE_BGP)); |
||||
if (!bgp) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate BGP structure"); |
||||
return NULL; |
||||
} |
||||
|
||||
bgp->instance = instance; |
||||
|
||||
// Создаем очередь для рассылки
|
||||
bgp->senders_list = queue_new(instance->ua, 0); |
||||
if (!bgp->senders_list) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to create senders queue"); |
||||
free(bgp); |
||||
return NULL; |
||||
} |
||||
|
||||
// Регистрируем колбэк для приема роутинг-пакетов
|
||||
if (etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to bind ETCP callback"); |
||||
queue_free(bgp->senders_list); |
||||
free(bgp); |
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized"); |
||||
return bgp; |
||||
} |
||||
|
||||
void route_bgp_destroy(struct UTUN_INSTANCE* instance) |
||||
{ |
||||
if (!instance) { |
||||
return; |
||||
} |
||||
|
||||
// Отвязываем колбэк
|
||||
etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); |
||||
|
||||
// Очищаем и освобождаем структуру BGP
|
||||
if (instance->bgp) { |
||||
if (instance->bgp->senders_list) { |
||||
// Очищаем очередь
|
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(instance->bgp->senders_list)) != NULL) { |
||||
queue_dgram_free(entry); |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(instance->bgp->senders_list); |
||||
} |
||||
free(instance->bgp); |
||||
instance->bgp = NULL; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module destroyed"); |
||||
} |
||||
|
||||
void route_bgp_new_conn(struct ETCP_CONN* conn) |
||||
{ |
||||
if (!conn || !conn->instance) { |
||||
return; |
||||
} |
||||
|
||||
struct UTUN_INSTANCE* instance = conn->instance; |
||||
struct ROUTE_BGP* bgp = instance->bgp; |
||||
|
||||
if (!bgp) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "BGP not initialized"); |
||||
return; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "New connection, sending routing table"); |
||||
|
||||
// Отправляем полную таблицу новому соединению
|
||||
route_bgp_send_table_to_conn_internal(bgp, conn); |
||||
} |
||||
|
||||
void route_bgp_close_conn(struct ETCP_CONN* conn) |
||||
{ |
||||
if (!conn) { |
||||
return; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection closed, removing from distribution list"); |
||||
|
||||
// Удаляем все маршруты, связанные с этим соединением
|
||||
struct UTUN_INSTANCE* instance = conn->instance; |
||||
if (instance && instance->rt) { |
||||
route_table_delete(instance->rt, conn); |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@
|
||||
#ifndef ROUTE_BGP_H |
||||
#define ROUTE_BGP_H |
||||
|
||||
#include <stdint.h> |
||||
#include "../lib/ll_queue.h" |
||||
|
||||
// Forward declarations
|
||||
struct UTUN_INSTANCE; |
||||
struct ETCP_CONN; |
||||
|
||||
// ETCP ID для маршрутных пакетов
|
||||
#define ETCP_ID_ROUTE_ENTRY 0x01 |
||||
|
||||
// Sub-команды для маршрутных пакетов
|
||||
#define ROUTE_SUBCMD_ENTRY 0x01 // Элемент таблицы
|
||||
#define ROUTE_SUBCMD_WITHDRAW 0x02 // Отзыв маршрута
|
||||
#define ROUTE_SUBCMD_REQUEST 0x03 // Запрос полной таблицы
|
||||
|
||||
// Флаги в prefix_length (2 старших бита)
|
||||
#define ROUTE_PREFIX_F_DIRECT 0x40 // Можно подключиться напрямую
|
||||
#define ROUTE_PREFIX_F_LOCAL 0x80 // Локальный маршрут (не пересылать)
|
||||
|
||||
/**
|
||||
* @brief Пакет маршрутной информации (бинарный формат) |
||||
*/ |
||||
struct ROUTE_BGP_PACKET { |
||||
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_ENTRY (1)
|
||||
uint8_t hop_count; // Количество прыжков (инкриментирует получатель)
|
||||
uint8_t prefix_length; // Длина префикса подсети + флаги (2 старших бита)
|
||||
uint16_t latency; // Задержка (x0.1 ms)
|
||||
uint16_t endpoint_port; // Порт для прямого подключения
|
||||
uint32_t endpoint_ip; // IP для прямого подключения
|
||||
uint32_t network; // Сетевой адрес (big-endian)
|
||||
uint32_t bandwidth_kbps; // Пропускная способность
|
||||
uint64_t node_id; // ID узла назначения
|
||||
} __attribute__((packed)); |
||||
|
||||
|
||||
struct ROUTE_BGP_QUEUE_ITEM { |
||||
struct ll_entry ll; // Для очереди
|
||||
struct ETCP_CONN* target; // Кому отправлять (NULL = всем)
|
||||
}; |
||||
|
||||
struct ROUTE_BGP { |
||||
struct UTUN_INSTANCE* instance; // Ссылка на instance
|
||||
struct ll_queue* senders_list; // список для рассылки обновлений (рассылаем через etcp_send)
|
||||
}; |
||||
|
||||
|
||||
// Создание/уничтожение (делаем bind на 0x01)
|
||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance); |
||||
void route_bgp_destroy(struct UTUN_INSTANCE* instance); |
||||
|
||||
// Прием пакетов (вызывается из ETCP при получении ETCP_ID_ROUTE)
|
||||
//BIND: static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry);
|
||||
|
||||
// Отправка таблицы конкретному соединению или всем
|
||||
void route_bgp_new_conn(struct ETCP_CONN* conn);// send_table_to_conn, добавление в список рассылки
|
||||
void route_bgp_close_conn(struct ETCP_CONN* conn);// убрать из списка рассылки
|
||||
|
||||
#endif // ROUTE_BGP_H
|
||||
@ -1,16 +0,0 @@
|
||||
подсистема роутинга |
||||
utun - это сеть узлов. у каждого узла есть собственные локальные подсети. |
||||
и узлы обмениваются таблицой маршрутов между собой так чтобы у каждого была актуальная таблица подсетей всех узлов. |
||||
маршрутами меняются только те подключения которые взяты из конфига. |
||||
инициируется подключение, клиент отправляет свою таблицу. когда сервер принимает таблицу - сервер помечает что по этому маршруту надо обмениваться маршрутами, далее; |
||||
- добавляет узел в список рассылки обновлений маршрутов |
||||
- отправляет свою таблицу |
||||
- добавляет в свою таблицу отсутствующие маршруты |
||||
- если что-то добавил: |
||||
- рассылает измененные маршруты по списку рассылки |
||||
- список рассылки - это linked-list очередей (также на базе ll_queue - каждый элемент = подписчик). один маршрут = одна отправленная кодограмма |
||||
|
||||
|
||||
|
||||
при подключении узла или изменении таблицы: узлы обмениваются изменениями таблиц маршрутов. |
||||
то есть каждая запись в таблице имеет флаги - кому отправлена. |
||||
@ -0,0 +1,431 @@
|
||||
/**
|
||||
* @file test_bgp_route_exchange.c |
||||
* @brief Интеграционный тест обмена маршрутами между двумя инстансами |
||||
*
|
||||
* Тест проверяет: |
||||
* - Создание двух инстансов с разными подсетями |
||||
* - Установление ETCP соединения |
||||
* - Обмен маршрутами через BGP |
||||
* - Проверку таблиц роутинга (client -> server direction) |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <time.h> |
||||
#include <sys/stat.h> |
||||
#include <arpa/inet.h> |
||||
#include <sys/socket.h> |
||||
|
||||
#include "../src/etcp.h" |
||||
#include "../src/etcp_connections.h" |
||||
#include "../src/config_parser.h" |
||||
#include "../src/utun_instance.h" |
||||
#include "../src/routing.h" |
||||
#include "../src/route_lib.h" |
||||
#include "../src/route_bgp.h" |
||||
#include "../src/tun_if.h" |
||||
#include "../src/secure_channel.h" |
||||
#include "../lib/u_async.h" |
||||
#include "../lib/debug_config.h" |
||||
|
||||
#define TEST_TIMEOUT_MS 5000 // 5 seconds timeout
|
||||
#define BGP_EXCHANGE_WAIT_MS 200 // Wait for BGP exchange (100ms + margin)
|
||||
|
||||
// Test state
|
||||
static struct UTUN_INSTANCE* server_instance = NULL; |
||||
static struct UTUN_INSTANCE* client_instance = NULL; |
||||
static struct UASYNC* ua = NULL; |
||||
static int test_phase = 0; // 0=init, 1=connected, 2=bgp_exchanged, 3=success, 4=failure
|
||||
static void* monitor_timeout_id = NULL; |
||||
static void* test_timeout_id = NULL; |
||||
|
||||
// Temp config file paths
|
||||
static char temp_dir[] = "/tmp/utun_bgp_test_XXXXXX"; |
||||
static char server_config_path[256]; |
||||
static char client_config_path[256]; |
||||
|
||||
// Server config: 2 subnets - 192.168.10.0/24 and 192.168.11.0/24
|
||||
static const char* server_config_content = |
||||
"[global]\n" |
||||
"my_node_id=0x1111111111111111\n" |
||||
"my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" |
||||
"my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" |
||||
"tun_ip=10.99.0.1/24\n" |
||||
"tun_ifname=tun99\n" |
||||
"\n" |
||||
"[routing]\n" |
||||
"my_subnet=192.168.10.0/24\n" |
||||
"my_subnet=192.168.11.0/24\n" |
||||
"\n" |
||||
"[server: test]\n" |
||||
"addr=127.0.0.1:39011\n" |
||||
"type=public\n"; |
||||
|
||||
// Client config: 2 subnets - 192.168.20.0/24 and 192.168.21.0/24
|
||||
static const char* client_config_content = |
||||
"[global]\n" |
||||
"my_node_id=0x2222222222222222\n" |
||||
"my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n" |
||||
"my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" |
||||
"tun_ip=10.99.0.2/24\n" |
||||
"tun_ifname=tun98\n" |
||||
"\n" |
||||
"[routing]\n" |
||||
"my_subnet=192.168.20.0/24\n" |
||||
"my_subnet=192.168.21.0/24\n" |
||||
"\n" |
||||
"[server: test]\n" |
||||
"addr=127.0.0.1:39012\n" |
||||
"type=public\n" |
||||
"\n" |
||||
"[client: test_client]\n" |
||||
"keepalive=1\n" |
||||
"peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" |
||||
"link=test:127.0.0.1:39011\n"; |
||||
|
||||
// Create temp config files
|
||||
static int create_temp_configs(void) { |
||||
if (mkdtemp(temp_dir) == NULL) { |
||||
fprintf(stderr, "Failed to create temp directory\n"); |
||||
return -1; |
||||
} |
||||
|
||||
snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir); |
||||
snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir); |
||||
|
||||
FILE* f = fopen(server_config_path, "w"); |
||||
if (!f) { |
||||
fprintf(stderr, "Failed to create server config file\n"); |
||||
return -1; |
||||
} |
||||
fprintf(f, "%s", server_config_content); |
||||
fclose(f); |
||||
|
||||
f = fopen(client_config_path, "w"); |
||||
if (!f) { |
||||
fprintf(stderr, "Failed to create client config file\n"); |
||||
unlink(server_config_path); |
||||
return -1; |
||||
} |
||||
fprintf(f, "%s", client_config_content); |
||||
fclose(f); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Cleanup temp config files
|
||||
static void cleanup_temp_configs(void) { |
||||
if (server_config_path[0]) unlink(server_config_path); |
||||
if (client_config_path[0]) unlink(client_config_path); |
||||
if (temp_dir[0]) rmdir(temp_dir); |
||||
} |
||||
|
||||
// Check if connection is established (link initialized)
|
||||
static int is_connection_established(struct UTUN_INSTANCE* inst) { |
||||
if (!inst) return 0; |
||||
|
||||
struct ETCP_CONN* conn = inst->connections; |
||||
while (conn) { |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
if (link->initialized) { |
||||
return 1; |
||||
} |
||||
link = link->next; |
||||
} |
||||
conn = conn->next; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// Print routing table contents
|
||||
static void print_routing_table(struct UTUN_INSTANCE* inst, const char* name) { |
||||
if (!inst || !inst->rt) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "%s: No routing table\n", name); |
||||
return; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "\n=== %s Routing Table (%zu routes) ===", name, inst->rt->count); |
||||
|
||||
for (size_t i = 0; i < inst->rt->count; i++) { |
||||
struct ROUTE_ENTRY* entry = &inst->rt->entries[i]; |
||||
char network_str[16]; |
||||
struct in_addr addr; |
||||
addr.s_addr = htonl(entry->network); |
||||
inet_ntop(AF_INET, &addr, network_str, sizeof(network_str)); |
||||
|
||||
const char* type_str = "UNKNOWN"; |
||||
switch (entry->type) { |
||||
case ROUTE_TYPE_STATIC: type_str = "STATIC"; break; |
||||
case ROUTE_TYPE_DYNAMIC: type_str = "DYNAMIC"; break; |
||||
case ROUTE_TYPE_LOCAL: type_str = "LOCAL"; break; |
||||
case ROUTE_TYPE_LEARNED: type_str = "LEARNED"; break; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Route %zu: %s/%d [%s] node_id=%016llX hops=%d",
|
||||
i + 1, network_str, entry->prefix_length, type_str, |
||||
(unsigned long long)entry->destination_node_id, |
||||
entry->metrics.hop_count); |
||||
} |
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=====================================\n"); |
||||
} |
||||
|
||||
// Check if specific learned route exists
|
||||
static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network,
|
||||
uint8_t prefix_len, uint64_t expected_node_id) { |
||||
if (!inst || !inst->rt) return 0; |
||||
|
||||
for (size_t i = 0; i < inst->rt->count; i++) { |
||||
struct ROUTE_ENTRY* entry = &inst->rt->entries[i]; |
||||
if (entry->network == network &&
|
||||
entry->prefix_length == prefix_len && |
||||
entry->type == ROUTE_TYPE_LEARNED && |
||||
entry->destination_node_id == expected_node_id) { |
||||
return 1; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// Verify BGP route exchange results
|
||||
static int verify_bgp_exchange(void) { |
||||
int success = 1; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "\n=== Verifying BGP Route Exchange ===\n"); |
||||
|
||||
// Print tables for debugging
|
||||
print_routing_table(server_instance, "SERVER"); |
||||
print_routing_table(client_instance, "CLIENT"); |
||||
|
||||
// Check server learned client's routes (192.168.20.0/24, 192.168.21.0/24)
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Checking server learned client's routes..."); |
||||
|
||||
if (!check_learned_route(server_instance, 0xC0A81400, 24, 0x2222222222222222ULL)) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.20.0/24"); |
||||
success = 0; |
||||
} else { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.20.0/24"); |
||||
} |
||||
|
||||
if (!check_learned_route(server_instance, 0xC0A81500, 24, 0x2222222222222222ULL)) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.21.0/24"); |
||||
success = 0; |
||||
} else { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.21.0/24"); |
||||
} |
||||
|
||||
// Check client learned server's routes (192.168.10.0/24, 192.168.11.0/24)
|
||||
// NOTE: For now we only test client -> server direction
|
||||
// This will be enabled after server -> client is implemented
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Checking client learned server's routes (disabled - server->client not implemented)..."); |
||||
|
||||
return success; |
||||
} |
||||
|
||||
// Monitor callback - checks connection status and BGP exchange
|
||||
static void monitor_connections(void* arg) { |
||||
(void)arg; |
||||
|
||||
if (test_phase >= 3) { |
||||
monitor_timeout_id = NULL; |
||||
return; |
||||
} |
||||
|
||||
switch (test_phase) { |
||||
case 0: { // Waiting for connection
|
||||
int server_connected = is_connection_established(server_instance); |
||||
int client_connected = is_connection_established(client_instance); |
||||
|
||||
if (server_connected && client_connected) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "=== Connection established! Waiting for BGP exchange... ==="); |
||||
test_phase = 1; |
||||
// Schedule next check after BGP exchange time
|
||||
monitor_timeout_id = uasync_set_timeout(ua, BGP_EXCHANGE_WAIT_MS, NULL, monitor_connections); |
||||
return; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
case 1: { // Connection established, wait for BGP exchange
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Checking BGP route exchange ==="); |
||||
test_phase = 2; |
||||
|
||||
// Verify routes
|
||||
if (verify_bgp_exchange()) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== BGP ROUTE EXCHANGE SUCCESS ==="); |
||||
test_phase = 3; // Success
|
||||
} else { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "=== BGP ROUTE EXCHANGE FAILED ==="); |
||||
test_phase = 4; // Failure
|
||||
} |
||||
|
||||
// Cancel timeout for quick exit
|
||||
if (test_timeout_id) { |
||||
uasync_cancel_timeout(ua, test_timeout_id); |
||||
test_timeout_id = NULL; |
||||
} |
||||
return; |
||||
} |
||||
|
||||
case 2: // Verification done
|
||||
return; |
||||
} |
||||
|
||||
// Schedule next check
|
||||
if (test_phase < 3) { |
||||
monitor_timeout_id = uasync_set_timeout(ua, 100, NULL, monitor_connections); |
||||
} |
||||
} |
||||
|
||||
static void test_timeout(void* arg) { |
||||
(void)arg; |
||||
if (test_phase < 3) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "\n=== TEST TIMEOUT ==="); |
||||
test_phase = 4; // Failure
|
||||
if (monitor_timeout_id) { |
||||
uasync_cancel_timeout(ua, monitor_timeout_id); |
||||
monitor_timeout_id = NULL; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int main() { |
||||
printf("=== BGP Route Exchange Test ===\n"); |
||||
|
||||
// Create temp config files
|
||||
if (create_temp_configs() != 0) { |
||||
fprintf(stderr, "Failed to create temporary config files\n"); |
||||
return 1; |
||||
} |
||||
|
||||
debug_config_init(); |
||||
debug_set_level(DEBUG_LEVEL_INFO); |
||||
debug_set_categories(DEBUG_CATEGORY_ROUTING | DEBUG_CATEGORY_ETCP | DEBUG_CATEGORY_BGP); |
||||
|
||||
// Disable TUN for testing
|
||||
utun_instance_set_tun_init_enabled(0); |
||||
|
||||
// Create shared uasync context
|
||||
ua = uasync_create(); |
||||
if (!ua) { |
||||
fprintf(stderr, "Failed to create uasync context\n"); |
||||
return 1; |
||||
} |
||||
|
||||
// Create server instance
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating server instance..."); |
||||
server_instance = utun_instance_create(ua, server_config_path); |
||||
if (!server_instance) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create server instance"); |
||||
return 1; |
||||
} |
||||
|
||||
// Initialize server connections and routing
|
||||
if (init_connections(server_instance) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server connections"); |
||||
return 1; |
||||
} |
||||
|
||||
if (utun_instance_init(server_instance) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance"); |
||||
return 1; |
||||
} |
||||
|
||||
// BGP module is now initialized automatically in utun_instance_create
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Server instance ready (node_id=%016llX, bgp=%p)",
|
||||
(unsigned long long)server_instance->node_id, (void*)server_instance->bgp); |
||||
|
||||
// Create client instance
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating client instance..."); |
||||
client_instance = utun_instance_create(ua, client_config_path); |
||||
if (!client_instance) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create client instance"); |
||||
return 1; |
||||
} |
||||
|
||||
// Initialize client connections and routing
|
||||
if (init_connections(client_instance) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize client connections"); |
||||
return 1; |
||||
} |
||||
|
||||
if (utun_instance_init(client_instance) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance"); |
||||
return 1; |
||||
} |
||||
|
||||
// BGP module is now initialized automatically in utun_instance_create
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Client instance ready (node_id=%016llX, bgp=%p)",
|
||||
(unsigned long long)client_instance->node_id, (void*)client_instance->bgp); |
||||
|
||||
// Give server time to initialize
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting for server initialization..."); |
||||
for (int i = 0; i < 50; i++) { |
||||
uasync_poll(ua, 10); |
||||
} |
||||
|
||||
// Start monitoring
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Starting connection monitoring..."); |
||||
monitor_timeout_id = uasync_set_timeout(ua, 100, NULL, monitor_connections); |
||||
test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout); |
||||
|
||||
// Main event loop
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Running event loop..."); |
||||
|
||||
int elapsed = 0; |
||||
int poll_interval = 5; |
||||
while (test_phase < 3 && elapsed < TEST_TIMEOUT_MS) { |
||||
uasync_poll(ua, poll_interval); |
||||
elapsed += poll_interval; |
||||
} |
||||
|
||||
// Cleanup
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\nCleaning up..."); |
||||
|
||||
if (monitor_timeout_id) { |
||||
uasync_cancel_timeout(ua, monitor_timeout_id); |
||||
monitor_timeout_id = NULL; |
||||
} |
||||
if (test_timeout_id) { |
||||
uasync_cancel_timeout(ua, test_timeout_id); |
||||
test_timeout_id = NULL; |
||||
} |
||||
|
||||
// Print final routing tables
|
||||
print_routing_table(server_instance, "SERVER (final)"); |
||||
print_routing_table(client_instance, "CLIENT (final)"); |
||||
|
||||
// Destroy instances
|
||||
if (server_instance) { |
||||
server_instance->running = 0; |
||||
utun_instance_destroy(server_instance); |
||||
} |
||||
if (client_instance) { |
||||
client_instance->running = 0; |
||||
utun_instance_destroy(client_instance); |
||||
} |
||||
|
||||
if (ua) { |
||||
uasync_destroy(ua, 0); |
||||
ua = NULL; |
||||
} |
||||
|
||||
cleanup_temp_configs(); |
||||
|
||||
// Test result
|
||||
if (test_phase == 3) { |
||||
printf("\n=== TEST PASSED ===\n"); |
||||
printf("BGP route exchange working (client -> server direction)\n"); |
||||
return 0; |
||||
} else { |
||||
printf("\n=== TEST FAILED ===\n"); |
||||
if (test_phase == 4) { |
||||
printf("BGP route exchange verification failed\n"); |
||||
} else { |
||||
printf("Test timeout or connection failed\n"); |
||||
} |
||||
return 1; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue