diff --git a/src/etcp.c b/src/etcp.c index b8727cdd..71bf5e22 100644 --- a/src/etcp.c +++ b/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 #include #include @@ -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); diff --git a/src/route_bgp.c b/src/route_bgp.c index 920d3af3..5c402846 100644 --- a/src/route_bgp.c +++ b/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); +} diff --git a/src/route_bgp.h b/src/route_bgp.h index 9be66617..bfd49d0b 100644 --- a/src/route_bgp.h +++ b/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 diff --git a/src/route_lib.c b/src/route_lib.c index ce2629d9..09b2d6f4 100644 --- a/src/route_lib.c +++ b/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--; diff --git a/src/route_lib.h b/src/route_lib.h index 0134af6d..4adcef53 100644 --- a/src/route_lib.h +++ b/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 */ }; /** diff --git a/tests/test_bgp_route_exchange.c b/tests/test_bgp_route_exchange.c index cb235515..e41da1ca 100644 --- a/tests/test_bgp_route_exchange.c +++ b/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;