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
This commit is contained in:
1 parent
68668821e4
commit
43114d727c
6 files changed
+243
-30
No files matched your search
+9
-3
@@ -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);
|
||||
|
||||
+190
-13
@@ -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;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection closed, removing from distribution list");
|
||||
struct UTUN_INSTANCE* instance = conn->instance;
|
||||
struct ROUTE_BGP* bgp = instance->bgp;
|
||||
|
||||
if (!bgp) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
+11
-2
@@ -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
|
||||
@@ -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--;
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user