/** * @file route_bgp.c * @brief BGP-like обмен маршрутами — исправленная версия под новую route_lib */ #include #include #include "../lib/platform_compat.h" #include "route_bgp.h" #include "route_lib.h" #include "etcp_api.h" #include "etcp.h" #include "etcp_connections.h" #include "etcp_debug.h" #include "utun_instance.h" #include "config_parser.h" #include "../lib/debug_config.h" #include "../lib/mem.h" // ============================================================================ // Вспомогательные функции // ============================================================================ // ============================================================================ // Отправка (только preferred_conn + hop_list) // ============================================================================ static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn, const struct ROUTE_ENTRY* route, uint8_t subcmd) { if (!bgp || !conn || !route) return; size_t pkt_size; struct BGP_ROUTE_PACKET* pkt; if (!route->conn_list) { pkt_size = sizeof(struct BGP_ROUTE_PACKET); pkt = u_calloc(1, pkt_size); if (!pkt) return; pkt->node_id = htobe64(bgp->instance->node_id); pkt->hop_count = 0; } else { uint8_t pref = route->conn_list->preferred_conn; if (pref >= route->conn_list->conninfo_count) pref = 0; struct NODE_CONN_INFO* ci = &route->conn_list->conn_info[pref]; pkt_size = sizeof(struct BGP_ROUTE_PACKET) + ci->hop_count * sizeof(uint64_t); pkt = u_calloc(1, pkt_size); if (!pkt) return; pkt->node_id = htobe64(route->conn_list->node_id); pkt->hop_count = ci->hop_count; if (ci->hop_count > 0 && ci->hop_list) memcpy(pkt->hop_list, ci->hop_list, ci->hop_count * sizeof(uint64_t)); } pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->subcmd = subcmd; // ← теперь используем REROUTE pkt->network = htonl(route->network); pkt->prefix_length = route->prefix_length; pkt->latency = 0; struct ll_entry* send_entry = queue_entry_new(0); if (!send_entry) { u_free(pkt); return; } send_entry->dgram = (uint8_t*)pkt; send_entry->len = (uint16_t)pkt_size; if (etcp_send(conn, send_entry) != 0) { queue_entry_free(send_entry); u_free(pkt); } else { const char* subcmd_name = (subcmd == ROUTE_SUBCMD_ENTRY) ? "ENTRY" : (subcmd == ROUTE_SUBCMD_WITHDRAW) ? "WITHDRAW" : "REROUTE"; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending route %s/%d, peer %s (%s)", ip_to_string(route->network).a, route->prefix_length, conn->log_name, subcmd_name); } } // ============================================================================ // Broadcast / Withdraw // ============================================================================ static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_ENTRY* route, struct ETCP_CONN* exclude, uint8_t subcmd) { if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_route: bgp is NULL"); return; } if (!route) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_route: route is NULL"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_route: network=%s/%d exclude=%p subcmd=%d", ip_to_string(route->network).a, route->prefix_length, (void*)exclude, subcmd); struct ll_entry* entry = bgp->senders_list->head; int sent_count = 0; while (entry) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data; if (item->conn != exclude) { route_bgp_send_route(bgp, item->conn, route, subcmd); sent_count++; } entry = entry->next; } DEBUG_TRACE(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_route: sent to %d peers", sent_count); } static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id, struct ETCP_CONN* exclude) { if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: bgp is NULL"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: node_id=%016llx exclude=%p", (unsigned long long)node_id, (void*)exclude); struct BGP_WITHDRAW_PACKET* pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET)); if (!pkt) return; pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->subcmd = ROUTE_SUBCMD_WITHDRAW; pkt->node_id = htobe64(node_id); struct ll_entry* send_entry = queue_entry_new(0); if (!send_entry) { u_free(pkt); return; } send_entry->dgram = (uint8_t*)pkt; send_entry->len = sizeof(struct BGP_WITHDRAW_PACKET); struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn != exclude) { struct ll_entry* copy = queue_entry_new(0); if (copy) { copy->dgram = u_malloc(send_entry->len); memcpy(copy->dgram, send_entry->dgram, send_entry->len); copy->len = send_entry->len; etcp_send(item->conn, copy); } } e = e->next; } queue_entry_free(send_entry); u_free(pkt); } // ============================================================================ // Callback на изменение таблицы (insert / update / delete) // ============================================================================ static void route_bgp_on_route_change(struct ROUTE_TABLE* table, struct ROUTE_ENTRY* entry, int action, uint64_t changed_from, void* arg) { struct ROUTE_BGP* bgp = (struct ROUTE_BGP*)arg; if (!bgp || !entry) return; // changed_from - это peer_node_id от кого пришёл маршрут (0 для локальных) // находим conn по peer_node_id для использования как exclude struct ETCP_CONN* exclude_conn = NULL; if (changed_from != 0) { struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn->peer_node_id == changed_from) { exclude_conn = item->conn; break; } e = e->next; } } if (action == 0) { // insert route_bgp_broadcast_route(bgp, entry, exclude_conn, ROUTE_SUBCMD_ENTRY); } else if (action == 1) { // update / reroute / hop_list changed route_bgp_broadcast_route(bgp, entry, exclude_conn, ROUTE_SUBCMD_ENTRY_REROUTE); } else if (action == 2) { // withdraw uint64_t node_id = entry->conn_list ? entry->conn_list->node_id : 0; route_bgp_broadcast_withdraw(bgp, node_id, exclude_conn); } } // ============================================================================ // Приём пакетов // ============================================================================ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) { if (!from_conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: from_conn is NULL"); if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } if (!entry || entry->len < 2) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: from=%016llx len=%zu", (unsigned long long)from_conn->peer_node_id, entry->len); struct UTUN_INSTANCE* instance = from_conn->instance; if (!instance || !instance->rt || !instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/rt/bgp"); queue_dgram_free(entry); queue_entry_free(entry); return; } struct ROUTE_BGP* bgp = instance->bgp; uint8_t* data = entry->dgram; if (data[0] != ETCP_ID_ROUTE_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; } uint8_t subcmd = data[1]; if (subcmd == ROUTE_SUBCMD_ENTRY || subcmd == ROUTE_SUBCMD_ENTRY_REROUTE) { if (entry->len < sizeof(struct BGP_ROUTE_PACKET)) { queue_dgram_free(entry); queue_entry_free(entry); return; } struct BGP_ROUTE_PACKET* pkt = (struct BGP_ROUTE_PACKET*)data; uint32_t network = ntohl(pkt->network); uint8_t prefix = pkt->prefix_length; uint64_t owner_node_id = be64toh(pkt->node_id); uint8_t recv_hop_count = pkt->hop_count; if (recv_hop_count > MAX_HOPS - 1) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Too many hops (%d)", recv_hop_count); queue_dgram_free(entry); queue_entry_free(entry); return; } // Строим новый hop_list: next_hop = отправитель + полученный список uint8_t new_hop_count = recv_hop_count + 1; uint64_t new_hop_list[MAX_HOPS]; new_hop_list[0] = from_conn->peer_node_id; if (recv_hop_count > 0) { memcpy(&new_hop_list[1], pkt->hop_list, recv_hop_count * sizeof(uint64_t)); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received route %s/%d from %016llx (hops=%d)", ip_to_string(network).a, prefix, (unsigned long long)owner_node_id, new_hop_count); struct ROUTE_ENTRY new_route = {0}; new_route.network = network; new_route.prefix_length = prefix; bool inserted = route_insert(instance->rt, &new_route, from_conn, owner_node_id, instance->node_id, new_hop_list, new_hop_count); if (!inserted) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Route insert rejected (loop/overlap/owner conflict)"); } else { DEBUG_TRACE(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: INSERTED"); } } else if (subcmd == ROUTE_SUBCMD_WITHDRAW) { if (entry->len < sizeof(struct BGP_WITHDRAW_PACKET)) { queue_dgram_free(entry); queue_entry_free(entry); return; } struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data; uint64_t node_id = be64toh(wp->node_id); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received WITHDRAW for node %016llx", (unsigned long long)node_id); // === ИСПРАВЛЕНИЕ: поддержка резервных путей === route_remove_path(instance->rt, from_conn, node_id); // callback внутри route_remove_path уже сделает broadcast (withdraw или reroute) } queue_dgram_free(entry); queue_entry_free(entry); } // ============================================================================ // Init / Destroy / New / Remove conn // ============================================================================ // Callback для готовности ETCP соединения - отправляет маршруты static void route_bgp_on_conn_ready(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Connection %s ready ", conn->log_name); route_bgp_new_conn(conn); } } // Callback для новых ETCP соединений - устанавливает ready callback static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Set ETCP ready callback for connection %s", conn->log_name); etcp_conn_set_ready_cbk(conn, route_bgp_on_conn_ready, conn->instance->bgp); } } struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) { if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: instance is NULL"); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx", (unsigned long long)instance->node_id); struct ROUTE_BGP* bgp = u_calloc(1, sizeof(struct ROUTE_BGP)); if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: alloc failed"); return NULL; } bgp->instance = instance; bgp->senders_list = queue_new(instance->ua, 0, "BGP_senders"); if (!bgp->senders_list) { u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: queue_new failed"); return NULL; } etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk); if (instance->rt) { instance->rt->change_callback = route_bgp_on_route_change; instance->rt->change_callback_arg = bgp; } // Устанавливаем callback для новых ETCP соединений etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized (with hop_list support)"); return bgp; } void route_bgp_destroy(struct UTUN_INSTANCE* instance) { if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_destroy: instance is NULL"); return; } if (!instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_destroy: bgp is NULL"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx", (unsigned long long)instance->node_id); etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); if (instance->rt && instance->rt->change_callback == route_bgp_on_route_change) { instance->rt->change_callback = NULL; instance->rt->change_callback_arg = NULL; } // очистка списка struct ll_entry* e; while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) { queue_entry_free(e); } queue_free(instance->bgp->senders_list); u_free(instance->bgp); instance->bgp = NULL; } void route_bgp_new_conn(struct ETCP_CONN* conn) { if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn is NULL"); return; } if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance is NULL"); return; } if (!conn->instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance->bgp is NULL"); return; } if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance->rt is NULL"); return; } struct ROUTE_BGP* bgp = conn->instance->bgp; struct ROUTE_TABLE* rt = conn->instance->rt; // === 1. Проверяем, уже есть ли это соединение в списке === bool already_exists = false; struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn == conn) { already_exists = true; break; } e = e->next; } // === 2. Если нет — добавляем (только один раз) === if (!already_exists) { struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM)); if (!item_entry) return; ((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn; queue_data_put(bgp->senders_list, item_entry, 0); DEBUG_INFO(DEBUG_CATEGORY_BGP, "New connection added to senders_list"); } // === 3. В любом случае отправляем полную таблицу (это и нужно при переподключении) === for (size_t i = 0; i < rt->count; i++) { route_bgp_send_route(bgp, conn, &rt->entries[i], ROUTE_SUBCMD_ENTRY); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sent full routing table to %s (reconnect OK)", conn->log_name); } void route_bgp_remove_conn(struct ETCP_CONN* conn) { if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: conn is NULL"); return; } if (!conn->instance || !conn->instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: invalid instance/bgp"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx", (unsigned long long)conn->peer_node_id); struct ROUTE_BGP* bgp = conn->instance->bgp; struct ROUTE_TABLE* rt = conn->instance->rt; // ← КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: теперь используем route_remove_conn if (rt) { route_remove_conn(rt, conn); // автоматически вызовет reroute / withdraw через callback } // Удаляем из списка рассылки struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn == conn) { queue_remove_data(bgp->senders_list, e); queue_entry_free(e); break; } e = e->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, routes updated via route_remove_conn"); } void route_bgp_close_conn(struct ETCP_CONN* conn) { route_bgp_remove_conn(conn); }