Browse Source

BGP: Complete route exchange with connection tracking and broadcast

- Add ROUTE_BGP_CONN_ITEM structure for connection list management
- Modify route_bgp_new_conn() to add connections to senders_list
- Create route_bgp_remove_conn() for cleanup on connection close
- Add route_change_callback typedef and fields to ROUTE_TABLE
- Implement route_bgp_on_route_change() to broadcast updates
- Modify route_table_insert() to call callback on insert/update
- Modify route_table_delete_entry() to call callback on delete
- Add route_bgp_remove_conn() call in etcp_connection_close()
- Fix test: remove duplicate init_connections() calls

Features:
- Connections tracked in senders_list (ll_queue)
- Route changes broadcast to all connections
- Withdraw messages sent on connection close
- No port binding conflicts in tests

All changes working correctly - BGP route exchange functional in both directions
nodeinfo-routing-update
Evgeny 8 months ago
parent
commit
43114d727c
  1. 12
      src/etcp.c
  2. 203
      src/route_bgp.c
  3. 13
      src/route_bgp.h
  4. 16
      src/route_lib.c
  5. 15
      src/route_lib.h
  6. 14
      tests/test_bgp_route_exchange.c

12
src/etcp.c

@ -3,10 +3,11 @@
#include "etcp.h"
#include "etcp_loadbalancer.h"
#include "routing.h"
#include "route_bgp.h"
#include "../lib/u_async.h"
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "crc32.h" // For potential hashing, though not used yet.
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "crc32.h" // For potential hashing, though not used yet.
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
@ -137,6 +138,11 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
routing_del_conn(etcp);
// Notify BGP about connection closure (send withdraws, remove from senders_list)
if (etcp->instance && etcp->instance->bgp) {
route_bgp_remove_conn(etcp);
}
// Deinitialize packet normalizer (this will call routing_del_conn)
if (etcp->normalizer) {
pn_deinit((struct PKTNORM*)etcp->normalizer);

203
src/route_bgp.c

@ -22,11 +22,12 @@
// Размер пакета (без заголовка ETCP)
#define BGP_PACKET_SIZE (sizeof(struct ROUTE_BGP_PACKET))
// Внутренняя структура для элемента списка соединений
struct bgp_conn_item {
struct ETCP_CONN* conn;
struct bgp_conn_item* next;
};
// Forward declarations
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
const struct ROUTE_ENTRY* route);
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY* entry,
int action, void* arg);
/**
* @brief Колбэк для приема роутинг-пакетов от ETCP
@ -143,6 +144,124 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
queue_entry_free(entry);
}
/**
* @brief Отправить withdraw (отзыв маршрута) через ETCP
*/
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
const struct ROUTE_ENTRY* route)
{
if (!bgp || !conn || !route) {
return;
}
// Создаем ETCP пакет
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct ROUTE_BGP_PACKET));
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate withdraw packet");
return;
}
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
pkt->hop_count = 0;
pkt->prefix_length = route->prefix_length & 0x3F;
pkt->latency = 0;
pkt->endpoint_port = 0;
pkt->endpoint_ip = 0;
pkt->network = htonl(route->network);
pkt->bandwidth_kbps = 0;
pkt->node_id = route->destination_node_id;
entry->len = sizeof(struct ROUTE_BGP_PACKET);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending withdraw: %08x/%d to conn %p",
route->network, route->prefix_length, (void*)conn);
// Отправляем через ETCP
if (etcp_send(conn, entry) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to send withdraw via ETCP");
queue_dgram_free(entry);
queue_entry_free(entry);
}
}
/**
* @brief Отправить withdraw для всех маршрутов через указанное соединение
*/
static void route_bgp_send_withdraw_for_conn(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 withdraw for routes via conn %p to all peers", (void*)conn);
// Находим все маршруты через это соединение и отправляем withdraw
for (size_t i = 0; i < rt->count; i++) {
struct ROUTE_ENTRY* entry = &rt->entries[i];
if (entry->next_hop == conn && entry->type == ROUTE_TYPE_LEARNED) {
// Отправляем withdraw всем соединениям в senders_list
struct ll_entry* item_entry = bgp->senders_list->head;
while (item_entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)item_entry->data;
if (item->conn != conn) { // Не отправляем тому, кто прислал
route_bgp_send_withdraw(bgp, item->conn, entry);
}
item_entry = item_entry->next;
}
}
}
}
/**
* @brief Колбэк при изменении таблицы маршрутизации
*
* Рассылает обновления всем соединениям в senders_list
*/
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY* entry,
int action, void* arg)
{
struct ROUTE_BGP* bgp = (struct ROUTE_BGP*)arg;
if (!bgp || !entry) {
return;
}
// Не рассылаем локальные маршруты помеченные ROUTE_PREFIX_F_LOCAL
if (entry->type == ROUTE_TYPE_LOCAL && (entry->prefix_length & ROUTE_PREFIX_F_LOCAL)) {
return;
}
// Не рассылаем маршруты через которые мы их получили (чтобы избежать петель)
// Это делается в цикле ниже
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Route change: action=%d, network=%08x/%d, type=%d",
action, entry->network, entry->prefix_length, entry->type);
// Перебираем все соединения в senders_list
struct ll_entry* item_entry = bgp->senders_list->head;
while (item_entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)item_entry->data;
// Не отправляем тому, от кого пришел маршрут
if (entry->type == ROUTE_TYPE_LEARNED && entry->next_hop == item->conn) {
item_entry = item_entry->next;
continue;
}
if (action == 2) { // delete - отправить withdraw
route_bgp_send_withdraw(bgp, item->conn, entry);
} else { // insert (0) или update (1) - отправить маршрут
route_bgp_send_route(bgp, item->conn, entry);
}
item_entry = item_entry->next;
}
}
/**
* @brief Отправить один маршрут через ETCP
*/
@ -256,6 +375,13 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance)
return NULL;
}
// Устанавливаем callback на изменение таблицы маршрутизации
if (instance->rt) {
instance->rt->change_callback = route_bgp_on_route_change;
instance->rt->change_callback_arg = bgp;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route change callback registered");
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized");
return bgp;
}
@ -271,11 +397,16 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance)
// Очищаем и освобождаем структуру BGP
if (instance->bgp) {
// Убираем callback из таблицы маршрутизации
if (instance->rt && instance->rt->change_callback == route_bgp_on_route_change) {
instance->rt->change_callback = NULL;
instance->rt->change_callback_arg = NULL;
}
if (instance->bgp->senders_list) {
// Очищаем очередь
// Очищаем очередь - освобождаем элементы conn_item
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);
@ -301,23 +432,69 @@ void route_bgp_new_conn(struct ETCP_CONN* conn)
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "New connection, sending routing table");
// Создаем элемент для добавления в senders_list
struct ll_entry* entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM));
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate connection item");
return;
}
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data;
item->conn = conn;
// Добавляем в очередь (id = 0, не используем хеш)
if (queue_data_put(bgp->senders_list, entry, 0) != 0) {
queue_entry_free(entry);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to add connection to senders_list");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added connection %p to senders_list", (void*)conn);
// Отправляем полную таблицу новому соединению
route_bgp_send_table_to_conn_internal(bgp, conn);
}
void route_bgp_close_conn(struct ETCP_CONN* conn)
void route_bgp_remove_conn(struct ETCP_CONN* conn)
{
if (!conn) {
if (!conn || !conn->instance) {
return;
}
struct UTUN_INSTANCE* instance = conn->instance;
struct ROUTE_BGP* bgp = instance->bgp;
if (!bgp) {
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection closed, removing from distribution list");
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removing connection %p from senders_list", (void*)conn);
// Отправляем withdraw всем остальным соединениям для маршрутов через это соединение
route_bgp_send_withdraw_for_conn(bgp, conn);
// Находим и удаляем соединение из senders_list
struct ll_entry* entry = bgp->senders_list->head;
while (entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data;
if (item->conn == conn) {
// Удаляем из очереди
queue_remove_data(bgp->senders_list, entry);
queue_entry_free(entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection %p removed from senders_list", (void*)conn);
break;
}
entry = entry->next;
}
// Удаляем все маршруты, связанные с этим соединением
struct UTUN_INSTANCE* instance = conn->instance;
if (instance && instance->rt) {
if (instance->rt) {
route_table_delete(instance->rt, conn);
}
}
void route_bgp_close_conn(struct ETCP_CONN* conn)
{
// Устаревшая функция, используйте route_bgp_remove_conn
route_bgp_remove_conn(conn);
}

13
src/route_bgp.h

@ -42,6 +42,14 @@ struct ROUTE_BGP_QUEUE_ITEM {
struct ETCP_CONN* target; // Кому отправлять (NULL = всем)
};
/**
* @brief Элемент списка соединений для рассылки обновлений
*/
struct ROUTE_BGP_CONN_ITEM {
struct ll_entry ll; // Для очереди/списка
struct ETCP_CONN* conn; // Указатель на соединение
};
struct ROUTE_BGP {
struct UTUN_INSTANCE* instance; // Ссылка на instance
struct ll_queue* senders_list; // список для рассылки обновлений (рассылаем через etcp_send)
@ -56,7 +64,8 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance);
//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);// убрать из списка рассылки
void route_bgp_new_conn(struct ETCP_CONN* conn); // Добавление в список рассылки и отправка таблицы
void route_bgp_remove_conn(struct ETCP_CONN* conn); // Удаление из списка рассылки и отправка withdraw
void route_bgp_close_conn(struct ETCP_CONN* conn); // Убрать из списка рассылки (deprecated, используй remove_conn)
#endif // ROUTE_BGP_H

16
src/route_lib.c

@ -345,6 +345,12 @@ bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *ent
// Обновляем существующий маршрут
table->entries[i] = *entry;
table->entries[i].last_update = (uint64_t)time(NULL) * 1000000;
// Вызываем callback (action=1 - update)
if (table->change_callback) {
table->change_callback(table, &table->entries[i], 1, table->change_callback_arg);
}
return true;
}
}
@ -356,6 +362,11 @@ bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *ent
// Insert at the end (order doesn't matter, lookup will sort)
table->entries[table->count++] = new_entry;
// Вызываем callback (action=0 - insert)
if (table->change_callback) {
table->change_callback(table, &table->entries[table->count - 1], 0, table->change_callback_arg);
}
// Update stats
if (new_entry.type == ROUTE_TYPE_LOCAL) {
table->stats.local_routes++;
@ -441,6 +452,11 @@ bool route_table_delete_entry(struct ROUTE_TABLE *table, uint32_t network,
return false;
}
// Вызываем callback перед удалением (action=2 - delete)
if (table->change_callback) {
table->change_callback(table, &table->entries[found_idx], 2, table->change_callback_arg);
}
// Обновляем статистику
if (table->entries[found_idx].type == ROUTE_TYPE_LOCAL) {
table->stats.local_routes--;

15
src/route_lib.h

@ -48,6 +48,19 @@ struct ROUTE_ARRAY {
struct ROUTE_ENTRY* entries[0]; /**< массив указателей на роуты */
};
/**
* @brief Callback тип для уведомления об изменении маршрута
*
* @param table Таблица маршрутизации
* @param entry Запись маршрута
* @param action Действие: 0=insert, 1=update, 2=delete
* @param arg Пользовательский аргумент
*/
typedef void (*route_change_callback_fn)(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY* entry,
int action,
void* arg);
/**
* @brief Расширенная запись маршрута
@ -89,6 +102,8 @@ struct ROUTE_TABLE {
uint64_t routes_lookup_hits; /**< Количество попаданий в поиск маршрутов */
uint64_t routes_lookup_misses; /**< Количество промахов в поиск маршрутов */
} stats; /**< Статистика таблицы маршрутизации */
route_change_callback_fn change_callback; /**< Callback при изменении маршрута */
void* change_callback_arg; /**< Аргумент для callback */
};
/**

14
tests/test_bgp_route_exchange.c

@ -322,12 +322,7 @@ int main() {
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;
}
// Initialize server instance (includes connections, routing, and BGP)
if (utun_instance_init(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance");
return 1;
@ -345,12 +340,7 @@ int main() {
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;
}
// Initialize client instance (includes connections, routing, and BGP)
if (utun_instance_init(client_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance");
return 1;

Loading…
Cancel
Save