/** * @file route_bgp.c * @brief BGP-like обмен маршрутами — исправленная версия под новую route_lib */ #include #include #include #include "../lib/platform_compat.h" #include "../lib/debug_config.h" #include "../lib/mem.h" #include "utun_instance.h" #include "etcp_api.h" #include "etcp.h" #include "etcp_connections.h" #include "etcp_debug.h" #include "config_parser.h" #include "route_node.h" #include "route_lib.h" #include "route_bgp.h" // ============================================================================ // Вспомогательные функции // ============================================================================ static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name); struct BGP_ROUTE_REQUEST* req = u_calloc(1, sizeof(struct BGP_ROUTE_REQUEST)); if (!req) return; req->cmd = ETCP_ID_ROUTE_ENTRY; req->subcmd = ROUTE_SUBCMD_REQUEST_TABLE; struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(req); return; } e->dgram = (uint8_t*)req; e->len = sizeof(struct BGP_ROUTE_REQUEST); etcp_send(conn, e); } static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id); static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static char* nodeinfo_format(const uint8_t* data, size_t len) { if (!data || len < sizeof(struct BGP_NODEINFO_PACKET)) return NULL; struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data; struct NODEINFO* ni = &pkt->node; uint64_t node_id = ni->node_id; const uint8_t* dyn = data + sizeof(struct BGP_NODEINFO_PACKET); size_t off = 0; char name_buf[64] = {0}; size_t nl = ni->node_name_len; if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct BGP_NODEINFO_PACKET)) { memcpy(name_buf, dyn + off, nl); off += nl; } off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET); char subs_buf[512] = {0}; if (ni->local_v4_subnets > 0) { const struct NODEINFO_IPV4_SUBNET* subs = (const struct NODEINFO_IPV4_SUBNET*)(dyn + off); size_t sl = 0; for (uint8_t i = 0; i < ni->local_v4_subnets && sl < 400; i++) { uint32_t addr = (subs[i].addr[0]<<24)|(subs[i].addr[1]<<16)|(subs[i].addr[2]<<8)|subs[i].addr[3]; char tmp[64]; snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%u ",(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,subs[i].prefix_length); strcat(subs_buf,tmp); sl += strlen(tmp); } } int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count); char* buf = u_malloc(need+1); if (!buf) return NULL; snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count); return buf; } /* Old full table sending removed - now using NODEINFO broadcast now */ // ============================================================================ // Broadcast / Withdraw: node_id - удаляемый узел, wd_source - от кого получена команда удалить // ============================================================================ static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude) { if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bgp is NULL"); return; } 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 = node_id; pkt->wd_source = wd_source; struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item && item->conn && item->conn != exclude) { struct ll_entry* copy = queue_entry_new(0); if (copy) { copy->dgram = u_malloc(sizeof(*pkt)); if (copy->dgram) memcpy(copy->dgram, pkt, sizeof(*pkt)); copy->len = sizeof(*pkt); etcp_send(item->conn, copy); } } e = e->next; } u_free(pkt); } // ============================================================================ // Приём пакетов // ============================================================================ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) { if (!from_conn || !entry || entry->len < 2) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } struct UTUN_INSTANCE* instance = from_conn->instance; if (!instance || !instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/bgp"); queue_dgram_free(entry); queue_entry_free(entry); return; } struct ROUTE_BGP* bgp = instance->bgp; uint8_t* data = entry->dgram; uint8_t cmd = data[0]; uint8_t subcmd = data[1]; if (cmd != ETCP_ID_ROUTE_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet from %s, subcmd=%d, len=%zu", from_conn->log_name, subcmd, entry->len); if (subcmd == ROUTE_SUBCMD_NODEINFO) { char* s = nodeinfo_format(data, entry->len); if (s) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s from %s", s, from_conn->log_name); u_free(s); } route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_WITHDRAW) { route_bgp_process_withdraw(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) { route_bgp_handle_request_table(bgp, from_conn); } queue_dgram_free(entry); queue_entry_free(entry); } // ============================================================================ // Init / Destroy / New / Remove conn // ============================================================================ // Callback для готовности ETCP соединения - отправляет маршруты static void route_bgp_on_conn_up(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON UP %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Connection %s on up ", conn->log_name); route_bgp_new_conn(conn); } } static void route_bgp_on_conn_down(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON DOWN %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Connection %s on down ", conn->log_name); route_bgp_remove_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_INFO(DEBUG_CATEGORY_DEBUG, "BGP SET CBKS %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Set ETCP ready callback for connection %s", conn->log_name); etcp_conn_set_up_cbk(conn, route_bgp_on_conn_up, conn->instance->bgp); etcp_conn_set_down_cbk(conn, route_bgp_on_conn_down, 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; // Create nodes queue with hash support for fast lookup by node_id bgp->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, "bgp_nodes"); if (!bgp->nodes) { u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: nodes queue creation failed"); return NULL; } bgp->senders_list = queue_new(instance->ua, 0, "BGP_senders"); if (!bgp->senders_list) { queue_free(bgp->nodes); u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: senders_list queue creation failed"); return NULL; } int vc = 0; struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; while (s) { if (s->ip.family == AF_INET) vc++; s = s->next; } size_t d = vc * sizeof(struct NODEINFO_IPV4_SUBNET); bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + d); if (!bgp->local_node) { queue_free(bgp->nodes); queue_free(bgp->senders_list); u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: local_node alloc failed"); return NULL; } bgp->local_node->node.node_id = instance->node_id; bgp->local_node->node.hop_count = 0; bgp->local_node->node.ver = 1; bgp->local_node->dirty = 0; route_bgp_update_my_nodeinfo(instance, bgp); etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk); // Устанавливаем callback для новых ETCP соединений etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized (NODEINFO based routing)"); 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); struct ll_entry* e; while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) { queue_entry_free(e); } queue_free(instance->bgp->senders_list); if (instance->bgp->local_node) { if (instance->bgp->local_node->paths) { queue_free(instance->bgp->local_node->paths); } u_free(instance->bgp->local_node); } if (instance->bgp->nodes) { queue_free(instance->bgp->nodes); } 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; route_bgp_add_to_senders(bgp, conn); route_bgp_send_table_request(bgp, conn); } 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; } struct ROUTE_BGP* bgp = conn->instance->bgp; // SAFETY: проверяем что conn ещё есть в senders_list bool found_in_list = 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) { found_in_list = true; break; } e = e->next; } if (!found_in_list) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: conn already removed or not in list, skipping"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx", (unsigned long long)conn->peer_node_id); struct ROUTE_TABLE* rt = conn->instance->rt; // Remove this connection from all nodes' path lists // and send WITHDRAW if a node becomes unreachable bool need_withdraw = false; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; while (node_entry) { struct ll_entry* next = node_entry->next; struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry; if (route_bgp_remove_path(nq, conn) == 1) { need_withdraw = true; if (rt) { route_delete(rt, nq); } nq->dirty = 1; if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = nq->node.node_id; struct ll_entry* entry = node_entry; if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key); } node_entry = next; } // Удаляем из списка рассылки 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; } if (need_withdraw) { route_bgp_broadcast_withdraw(bgp, conn->peer_node_id, conn->instance->node_id, NULL); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, paths updated"); } /* ================================================ * NEW NODEINFO BASED IMPLEMENTATION * ================================================ */ struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) { if (!bgp || !bgp->nodes) { return NULL; } uint64_t key = node_id; struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8); return e ? (struct NODEINFO_Q*)e : NULL; } int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count) { if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args"); return -1; } if (!nq->paths) { nq->paths = queue_new(conn->instance->ua, 0, "node_paths"); if (!nq->paths) return -1; } size_t hop_size = hop_count * 8; size_t path_size = sizeof(struct NODEINFO_PATH) - sizeof(struct ll_entry) + hop_size; struct ll_entry* pe = queue_entry_new(path_size); if (!pe) return -1; struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe; path->conn = conn; path->hop_count = hop_count; uint64_t* stored = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); memcpy(stored, hop_list, hop_size); queue_data_put(nq->paths, pe); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s with %d hops", (unsigned long long)nq->node.node_id, conn->log_name, hop_count); return 0; } int route_bgp_remove_path_by_hop(struct NODEINFO_Q* nq, uint64_t wd_source) { if (!nq || !nq->paths) return 0; int removed = 0; struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e; // parse hoplist in dyn after hop_count (after fixed PATH) uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); bool has_wd = false; for (uint8_t i = 0; i < path->hop_count; i++) { if (hop[i] == wd_source) { has_wd = true; break; } } if (has_wd) { struct ll_entry* next = e->next; queue_remove_data(nq->paths, e); queue_entry_free(e); removed++; e = next; continue; } e = e->next; } if (removed > 0 && nq->paths && queue_entry_count(nq->paths) == 0) { queue_free(nq->paths); nq->paths = NULL; return 1; // unreachable } return removed; } int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) { if (!nq || !conn || !nq->paths) { return -1; } // Remove conn from paths queue struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e; if (path->conn == conn) { queue_remove_data(nq->paths, e); queue_entry_free(e); break; } e = e->next; } int remaining = nq->paths ? queue_entry_count(nq->paths) : 0; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via %s, remaining paths: %d", (unsigned long long)nq->node.node_id, conn->log_name, remaining); // If no paths left - node is unreachable if (remaining == 0) { if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } return 1; // signal that node became unreachable } return 0; } int nodeinfo_dyn_size(struct NODEINFO* node) { return node->node_name_len + node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + node->local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) + node->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET) + node->tranzit_nodes * 8 + node->hop_count * 8; } int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len) { if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1; struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data; struct NODEINFO* ni = &pkt->node; int dyn_size=nodeinfo_dyn_size(ni); if (len!=dyn_size + sizeof(struct BGP_NODEINFO_PACKET)) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Incorrect packet size (%s)",from->log_name); return -1; } uint64_t node_id = ni->node_id; if (ni->hop_count >= MAX_HOPS) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)", from->log_name, ni->hop_count); return -1; } struct NODEINFO_Q* nodeinfo1 = route_bgp_get_node(bgp, node_id); uint8_t new_ver = ni->ver; if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) { DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO from %s ignored (old ver %d <= %d)", from->log_name, new_ver, nodeinfo1->last_ver); return 0; } int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8; struct ll_queue* paths=NULL; int need_alloc=0; if (nodeinfo1) {// remove old node paths=nodeinfo1->paths; if (nodeinfo1->ll.size < new_data_size) { need_alloc=1; } } else need_alloc=1; if (need_alloc) { if (nodeinfo1) { queue_remove_data(bgp->nodes, &nodeinfo1->ll); queue_entry_free(&nodeinfo1->ll); } nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size); paths = queue_new(bgp->instance->ua, 0, "node_paths"); memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); queue_data_put_with_index(bgp->nodes, &nodeinfo1->ll, offsetof(struct NODEINFO_Q, node.node_id)-sizeof(struct ll_entry), 8); } else memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver; // add last hop uint64_t* hop_list = (uint64_t*)((uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO) + dyn_size); uint64_t sender = from->peer_node_id; memcpy(hop_list, &sender, 8); nodeinfo1->node.hop_count++; // update node path route_bgp_remove_path_by_hop(nodeinfo1, from->peer_node_id); route_bgp_add_path(nodeinfo1, from, hop_list, nodeinfo1->node.hop_count); if (bgp->instance->rt) { route_insert(bgp->instance->rt, nodeinfo1); } int hop_count=nodeinfo1->node.hop_count; // рассылаем по узлам которых нет в hop_list этой ноды (loop prevention) struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn) { uint64_t id=item->conn->peer_node_id; int found=0; for (int i=0; iconn); } else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id); } e = e->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d)", from->log_name, (unsigned long long)node_id, new_ver, nodeinfo1->paths ? queue_entry_count(nodeinfo1->paths) : 0); return 0; } int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len) { if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) return -1; struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data; uint64_t node_id = wp->node_id; uint64_t wd_source = wp->wd_source; struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id); if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; } int ret=route_bgp_remove_path_by_hop(nq, wd_source); if (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) { if (bgp->instance && bgp->instance->rt) { route_delete(bgp->instance->rt, nq); } nq->dirty = 1; if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = node_id; struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8); if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after WITHDRAW", (unsigned long long)node_id); route_bgp_broadcast_withdraw(bgp, node_id, wd_source, sender); } return 0; } void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn) { if (!node || !conn) { return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name); int dyn = nodeinfo_dyn_size(&node->node); size_t ps = sizeof(struct BGP_NODEINFO_PACKET) + dyn; uint8_t* p = u_malloc(ps); if (!p) { return; } p[0] = ETCP_ID_ROUTE_ENTRY; p[1] = ROUTE_SUBCMD_NODEINFO; memcpy(p + 2, &node->node, sizeof(struct NODEINFO)); uint8_t* ds = (uint8_t*)&node->node + sizeof(struct NODEINFO); memcpy(p + sizeof(struct BGP_NODEINFO_PACKET), ds, dyn); struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(p); return; } e->dgram = p; e->len = ps; etcp_send(conn, e); } static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn || !bgp->senders_list) return; bool already = false; struct ll_entry* e = bgp->senders_list->head; while (e) { if (((struct ROUTE_BGP_CONN_ITEM*)e->data)->conn == conn) { already = true; break; } e = e->next; } if (!already) { struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM)); if (item_entry) { ((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn; queue_data_put(bgp->senders_list, item_entry); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added to senders_list"); } } } static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id) { if (!nq || !nq->paths) return false; struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e; uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); bool has_id = false; for (uint8_t i = 0; i < path->hop_count; i++) { if (hop[i] == target_id) { has_id = true; break; } } if (!has_id) return true; e = e->next; } return false; } static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; uint64_t target = conn->peer_node_id; struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL; while (e) { struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e; if (nq->node.hop_count == 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "local node in learned list"); e = e->next; continue; } if (!nq->paths) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; } if (route_bgp_should_send_to(nq, target)) { route_bgp_send_nodeinfo(nq, conn); } e = e->next; } } static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", conn->log_name); route_bgp_send_nodeinfo(bgp->local_node, conn); route_bgp_send_full_table(bgp, conn); route_bgp_add_to_senders(bgp, conn); }