You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

737 lines
26 KiB

/**
* @file route_bgp.c
* @brief BGP-like обмен маршрутами — исправленная версия под новую route_lib
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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; i<hop_count; i++) if (hop_list[i]==id) found=1;
if (found==0) {
route_bgp_send_nodeinfo(nodeinfo1, item->conn);
}
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);
}