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
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); |
|
}
|
|
|