Browse Source

route: transition from BGP_ROUTE_PACKET to NODEINFO based routing

- Added paths list to NODEINFO_Q structure
- Updated route_bgp.c/h with NODEINFO packet handling
- Updated routing.c with new routing logic
- Updated route_lib.c/h with NODEINFO support
- Updated utun_instance.c for new routing changes
- Updated Makefile.am with new source files
nodeinfo-routing-update
Evgeny 5 days ago
parent
commit
5c9e9ba144
  1. 1
      src/Makefile.am
  2. 486
      src/route_bgp.c
  3. 86
      src/route_bgp.h
  4. 80
      src/route_bgp.txt
  5. 693
      src/route_lib.c
  6. 81
      src/route_lib.h
  7. 17
      src/route_lib.txt
  8. 25
      src/routing.c
  9. 6
      src/utun_instance.c

1
src/Makefile.am

@ -8,6 +8,7 @@ utun_CORE_SOURCES = \
config_updater.c \ config_updater.c \
route_lib.c \ route_lib.c \
route_bgp.c \ route_bgp.c \
route_node.c \
routing.c \ routing.c \
tun_if.c \ tun_if.c \
tun_route.c \ tun_route.c \

486
src/route_bgp.c

@ -7,8 +7,9 @@
#include <string.h> #include <string.h>
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include "route_bgp.h" #include "route_node.h"
#include "route_lib.h" #include "route_lib.h"
#include "route_bgp.h"
#include "etcp_api.h" #include "etcp_api.h"
#include "etcp.h" #include "etcp.h"
#include "etcp_connections.h" #include "etcp_connections.h"
@ -26,136 +27,23 @@
// Отправка (только preferred_conn + hop_list) // Отправка (только preferred_conn + hop_list)
// ============================================================================ // ============================================================================
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn, /* Old route packet sending function removed - using NODEINFO only now */
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);
}
}
static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn) return; 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)); route_bgp_send_nodeinfo(bgp, conn);
if (!req) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_send_table_request: alloc failed");
return;
}
req->cmd = ETCP_ID_ROUTE_ENTRY;
req->subcmd = ROUTE_SUBCMD_REQUEST_TABLE;
req->version = 0;
struct ll_entry* send_entry = queue_entry_new(0);
if (!send_entry) {
u_free(req);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_send_table_request: queue_entry_new failed");
return;
}
send_entry->dgram = (uint8_t*)req;
send_entry->len = sizeof(struct BGP_ROUTE_REQUEST);
if (etcp_send(conn, send_entry) != 0) {
queue_entry_free(send_entry);
u_free(req);
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to send table request to %s", conn ? conn->log_name : "unknown");
} else {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sent table request to %s (version=0)", conn->log_name);
}
} }
static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { /* Old full table sending removed - now using NODEINFO */
if (!bgp || !conn || !conn->instance || !conn->instance->rt) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_bgp_send_full_table: invalid args");
return;
}
struct ROUTE_TABLE* rt = conn->instance->rt;
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 (version=%u)",
conn->log_name, bgp->table_version);
}
// ============================================================================ // ============================================================================
// Broadcast / Withdraw // Broadcast / Withdraw
// ============================================================================ // ============================================================================
static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_ENTRY* route, /* Old broadcast route function removed - using NODEINFO broadcast now */
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, static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
struct ETCP_CONN* exclude) { struct ETCP_CONN* exclude) {
@ -201,152 +89,58 @@ static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id
// Callback на изменение таблицы (insert / update / delete) // Callback на изменение таблицы (insert / update / delete)
// ============================================================================ // ============================================================================
static void route_bgp_on_route_change(struct ROUTE_TABLE* table, /* Old route change callback removed - now using NODEINFO updates */
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) { static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) {
if (!from_conn) { if (!from_conn || !entry || entry->len < 2) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: from_conn is NULL"); if (entry) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } queue_dgram_free(entry);
return; queue_entry_free(entry);
} }
if (!entry || entry->len < 2) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
return; return;
} }
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RECV route from %s", from_conn->log_name);
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; struct UTUN_INSTANCE* instance = from_conn->instance;
if (!instance || !instance->rt || !instance->bgp) { if (!instance || !instance->bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/rt/bgp"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/bgp");
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry);
queue_entry_free(entry);
return; return;
} }
struct ROUTE_BGP* bgp = instance->bgp; struct ROUTE_BGP* bgp = instance->bgp;
uint8_t* data = entry->dgram; uint8_t* data = entry->dgram;
uint8_t cmd = data[0];
uint8_t subcmd = data[1];
if (data[0] != ETCP_ID_ROUTE_ENTRY) { if (cmd != ETCP_ID_ROUTE_ENTRY) {
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry);
queue_entry_free(entry);
return; return;
} }
uint8_t subcmd = data[1]; 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_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");
}
if (subcmd == ROUTE_SUBCMD_NODEINFO) {
route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_WITHDRAW) { } else if (subcmd == ROUTE_SUBCMD_WITHDRAW) {
if (entry->len < sizeof(struct BGP_WITHDRAW_PACKET)) { route_bgp_process_withdraw(bgp, data, entry->len);
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)
} else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) { } else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) {
if (entry->len < sizeof(struct BGP_ROUTE_REQUEST)) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", from_conn->log_name);
queue_dgram_free(entry); queue_entry_free(entry); route_bgp_send_nodeinfo(bgp, from_conn);
return;
}
struct BGP_ROUTE_REQUEST* req = (struct BGP_ROUTE_REQUEST*)data;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s (version=%u), sending full table",
from_conn->log_name, req->version);
route_bgp_send_full_table(bgp, from_conn);
} }
queue_dgram_free(entry); queue_dgram_free(entry);
queue_entry_free(entry); queue_entry_free(entry);
} }
// ============================================================================ // ============================================================================
// Init / Destroy / New / Remove conn // Init / Destroy / New / Remove conn
// ============================================================================ // ============================================================================
@ -381,102 +175,6 @@ static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) {
} }
} }
// Build MY_NODEINFO packet for this instance
static int route_bgp_build_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) {
if (!instance || !bgp) return -1;
// Count IPv4 sockets
int v4_socket_count = 0;
struct ETCP_SOCKET* sock = instance->etcp_sockets;
while (sock) {
if (sock->local_addr.ss_family == AF_INET) {
v4_socket_count++;
}
sock = sock->next;
}
// Count IPv4 routes (my_subnets)
int v4_route_count = 0;
struct CFG_ROUTE_ENTRY* subnet = instance->config->my_subnets;
while (subnet) {
if (subnet->ip.family == AF_INET) {
v4_route_count++;
}
subnet = subnet->next;
}
// Get name length
size_t name_len = strlen(instance->name);
// Calculate total size
size_t pkt_size = sizeof(struct BGP_NODEINFO_PACKET)
+ name_len
+ v4_socket_count * sizeof(struct BGP_NODEINFO_IPV4_SOCKET)
+ v4_route_count * sizeof(struct BGP_NODEINFO_IPV4_ROUTE);
// Allocate
struct BGP_NODEINFO_PACKET* pkt = u_malloc(pkt_size);
if (!pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_build_my_nodeinfo: malloc failed");
return -1;
}
// Fill fixed fields
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_NODEINFO;
pkt->node_id = htobe64(instance->node_id);
pkt->ver = 1;
memcpy(pkt->public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE);
pkt->node_name_len = (uint8_t)name_len;
pkt->local_v4_sockets = (uint8_t)v4_socket_count;
pkt->local_v6_sockets = 0;
pkt->local_v4_routes = (uint8_t)v4_route_count;
pkt->local_v6_routes = 0;
// Fill variable data
uint8_t* ptr = (uint8_t*)pkt + sizeof(struct BGP_NODEINFO_PACKET);
// Name
if (name_len > 0) {
memcpy(ptr, instance->name, name_len);
ptr += name_len;
}
// IPv4 sockets
struct BGP_NODEINFO_IPV4_SOCKET* sock_arr = (struct BGP_NODEINFO_IPV4_SOCKET*)ptr;
sock = instance->etcp_sockets;
while (sock) {
if (sock->local_addr.ss_family == AF_INET) {
struct sockaddr_in* in = (struct sockaddr_in*)&sock->local_addr;
memcpy(sock_arr->addr, &in->sin_addr, 4);
sock_arr->port = in->sin_port;
sock_arr++;
}
sock = sock->next;
}
ptr = (uint8_t*)sock_arr;
// IPv4 routes
struct BGP_NODEINFO_IPV4_ROUTE* route_arr = (struct BGP_NODEINFO_IPV4_ROUTE*)ptr;
subnet = instance->config->my_subnets;
while (subnet) {
if (subnet->ip.family == AF_INET) {
memcpy(route_arr->addr, &subnet->ip.addr.v4, 4);
route_arr->prefix_length = subnet->netmask;
route_arr++;
}
subnet = subnet->next;
}
bgp->my_nodeinfo = pkt;
bgp->my_nodeinfo_size = pkt_size;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Built my_nodeinfo: name='%s', v4_sockets=%d, v4_routes=%d",
instance->name, v4_socket_count, v4_route_count);
return 0;
}
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) { struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
if (!instance) { if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: instance is NULL"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: instance is NULL");
@ -492,34 +190,34 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
} }
bgp->instance = instance; bgp->instance = instance;
bgp->table_version = 1;
// Build my_nodeinfo packet // Create nodes queue with hash support for fast lookup by node_id
if (route_bgp_build_my_nodeinfo(instance, bgp) != 0) { bgp->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, "bgp_nodes");
queue_free(bgp->senders_list); if (!bgp->nodes) {
u_free(bgp); u_free(bgp);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: build_my_nodeinfo failed"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: nodes queue creation failed");
return NULL; return NULL;
} }
bgp->senders_list = queue_new(instance->ua, 0, "BGP_senders"); bgp->senders_list = queue_new(instance->ua, 0, "BGP_senders");
if (!bgp->senders_list) { if (!bgp->senders_list) {
queue_free(bgp->nodes);
u_free(bgp); u_free(bgp);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: queue_new failed"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: senders_list queue creation failed");
return NULL; return NULL;
} }
etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk); // Build initial my_nodeinfo
if (route_bgp_build_my_nodeinfo(instance, bgp) != 0) {
if (instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: build_my_nodeinfo failed");
instance->rt->change_callback = route_bgp_on_route_change;
instance->rt->change_callback_arg = bgp;
} }
etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk);
// Устанавливаем callback для новых ETCP соединений // Устанавливаем callback для новых ETCP соединений
etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL); etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized (with hop_list support)"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized (NODEINFO based routing)");
return bgp; return bgp;
} }
@ -537,18 +235,18 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY);
if (instance->rt && instance->rt->change_callback == route_bgp_on_route_change) { // очистка списка senders
instance->rt->change_callback = NULL;
instance->rt->change_callback_arg = NULL;
}
// очистка списка
struct ll_entry* e; struct ll_entry* e;
while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) { while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) {
queue_entry_free(e); queue_entry_free(e);
} }
queue_free(instance->bgp->senders_list); queue_free(instance->bgp->senders_list);
// очистка nodes
if (instance->bgp->nodes) {
queue_free(instance->bgp->nodes);
}
// Free my_nodeinfo // Free my_nodeinfo
if (instance->bgp->my_nodeinfo) { if (instance->bgp->my_nodeinfo) {
u_free(instance->bgp->my_nodeinfo); u_free(instance->bgp->my_nodeinfo);
@ -638,9 +336,13 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
struct ROUTE_TABLE* rt = conn->instance->rt; struct ROUTE_TABLE* rt = conn->instance->rt;
// ← КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: теперь используем route_remove_conn // Remove this connection from all nodes' path lists
if (rt) { // and send WITHDRAW if a node becomes unreachable
route_remove_conn(rt, conn); // автоматически вызовет reroute / withdraw через callback struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL;
while (node_entry) {
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry->data;
route_bgp_remove_path(nq, conn);
node_entry = node_entry->next;
} }
// Удаляем из списка рассылки // Удаляем из списка рассылки
@ -655,5 +357,81 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
e = e->next; e = e->next;
} }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, routes updated via route_remove_conn"); 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 = htobe64(node_id);
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8);
return e ? (struct NODEINFO_Q*)e->data : NULL;
}
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) {
if (!nq || !conn) return -1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s",
(unsigned long long)nq->node.node_id, conn->log_name);
return 0;
}
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) {
if (!nq || !conn) return -1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via conn %s",
(unsigned long long)nq->node.node_id, conn->log_name);
return 0;
}
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;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing NODEINFO from %s", from->log_name);
return 0;
}
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, 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 = be64toh(wp->node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing WITHDRAW for node %016llx", (unsigned long long)node_id);
return 0;
}
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn || !bgp->my_nodeinfo) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name);
struct ll_entry* entry = queue_entry_new(0);
if (!entry) return;
entry->dgram = u_malloc(bgp->my_nodeinfo_size);
if (!entry->dgram) {
queue_entry_free(entry);
return;
}
memcpy(entry->dgram, bgp->my_nodeinfo, bgp->my_nodeinfo_size);
entry->len = bgp->my_nodeinfo_size;
etcp_send(conn, entry);
}
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude) {
if (!bgp) return;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Broadcasting NODEINFO");
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 && item->conn != exclude) {
route_bgp_send_nodeinfo(bgp, item->conn);
}
e = e->next;
}
}
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending WITHDRAW for node %016llx", (unsigned long long)node_id);
} }

86
src/route_bgp.h

@ -5,76 +5,28 @@
#include <stddef.h> #include <stddef.h>
#include "../lib/ll_queue.h" #include "../lib/ll_queue.h"
#include "route_lib.h" #include "route_lib.h"
#include "route_node.h"
#include "secure_channel.h" #include "secure_channel.h"
// ETCP ID для маршрутных пакетов // ETCP ID для маршрутных пакетов
#define ETCP_ID_ROUTE_ENTRY 0x01 #define ETCP_ID_ROUTE_ENTRY 0x01
// Sub-команды // Sub-команды
#define ROUTE_SUBCMD_ENTRY 0x01 // Обычное обновление маршрута #define ROUTE_SUBCMD_NODEINFO 0x04 // полная информация об узле + маршруты
#define ROUTE_SUBCMD_ENTRY_REROUTE 0x02 // Reroute (preferred_conn изменился) — пока обрабатывается как ENTRY #define ROUTE_SUBCMD_REQUEST_TABLE 0x05 // запрос полной таблицы
#define ROUTE_SUBCMD_WITHDRAW 0x03 #define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
#define ROUTE_SUBCMD_NODEINFO 0x04
#define ROUTE_SUBCMD_REQUEST_TABLE 0x05 // запрос полной таблицы (version=0 в запросе)
#define MAX_HOPS 16 #define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 64
/** /**
* @brief Основной пакет маршрута (переменной длины) * @brief Пакет с информацией об узле (NODEINFO)
*/
struct BGP_ROUTE_PACKET {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
uint8_t subcmd; // ENTRY / REROUTE
uint32_t network; // big-endian
uint8_t prefix_length;
uint64_t node_id; // владелец префикса (big-endian)
uint8_t hop_count; // количество хопов
uint16_t latency; // зарезервировано (0)
uint64_t hop_list[0]; // flexible array: next_hop → ... → destination
} __attribute__((packed));
/**
* @brief Основной пакет маршрута (переменной длины)
*/ */
struct BGP_NODEINFO_PACKET { struct BGP_NODEINFO_PACKET {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO
uint64_t node_id; // (big-endian) struct NODEINFO node;
uint8_t ver; // версия пакета (сейчас 1)
uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey
uint8_t node_name_len; // размер в байтах (без null терминации)
uint8_t local_v4_sockets; // BGP_NODEINFO_IPV4_SOCKET число локальных сокетов ipv4 (для incoming connections)
uint8_t local_v6_sockets; // BGP_NODEINFO_IPV6_SOCKET число локальных сокетов ipv6 (для incoming connections) (пока 0)
uint8_t local_v4_routes; // BGP_NODEINFO_IPV4_ROUTE число локальных маршрутов ipv4
uint8_t local_v6_routes; // BGP_NODEINFO_IPV6_ROUTE число локальных маршрутов ipv6 (пока 0)
uint8_t tranzit_nodes; // BGP_NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды (минимальный пинг / лучшее качество каналов)
// далее идут по порядку следования полей в структуре: node_name, сокеты, роуты
} __attribute__((packed));
struct BGP_NODEINFO_IPV4_SOCKET {
uint8_t addr[4];// network byte order
uint16_t port;
} __attribute__((packed));
struct BGP_NODEINFO_IPV6_SOCKET {
uint8_t addr[16];
uint16_t port;
} __attribute__((packed));
struct BGP_NODEINFO_IPV4_ROUTE {
uint8_t addr[4];// network byte order
uint8_t prefix_length;
} __attribute__((packed));
struct BGP_NODEINFO_IPV6_ROUTE {
uint8_t addr[16];
uint8_t prefix_length;
} __attribute__((packed));
struct BGP_NODEINFO_TRANZIT_NODE {
uint64_t node_id; // (big-endian)
uint16_t rtt; // x0.1 ms
uint16_t link_q; // меньше - лучше (потери + 1/BW)
} __attribute__((packed)); } __attribute__((packed));
/** /**
@ -90,9 +42,8 @@ struct BGP_WITHDRAW_PACKET {
* @brief Пакет запроса полной таблицы * @brief Пакет запроса полной таблицы
*/ */
struct BGP_ROUTE_REQUEST { struct BGP_ROUTE_REQUEST {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY uint8_t cmd;
uint8_t subcmd; // ROUTE_SUBCMD_REQUEST_TABLE uint8_t subcmd;
uint32_t version; // 0 для запроса
} __attribute__((packed)); } __attribute__((packed));
struct ROUTE_BGP_CONN_ITEM { struct ROUTE_BGP_CONN_ITEM {
@ -102,10 +53,10 @@ struct ROUTE_BGP_CONN_ITEM {
struct ROUTE_BGP { struct ROUTE_BGP {
struct UTUN_INSTANCE* instance; struct UTUN_INSTANCE* instance;
struct BGP_NODEINFO_PACKET* my_nodeinfo; struct BGP_NODEINFO_PACKET* my_nodeinfo; // собранный пакет для отправки
uint16_t my_nodeinfo_size; uint16_t my_nodeinfo_size;
struct ll_queue* senders_list; struct ll_queue* senders_list; // список активных соединений
uint32_t table_version; // starts at 1, for future use struct ll_queue* nodes; // NODEINFO_Q с hash-таблицей по node_id
}; };
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance); struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance);
@ -114,4 +65,15 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance);
void route_bgp_new_conn(struct ETCP_CONN* conn); void route_bgp_new_conn(struct ETCP_CONN* conn);
void route_bgp_remove_conn(struct ETCP_CONN* conn); void route_bgp_remove_conn(struct ETCP_CONN* conn);
/* Новые функции для работы с NODEINFO */
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from,
const uint8_t* data, size_t len);
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, const uint8_t* data, size_t len);
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude);
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id);
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
#endif // ROUTE_BGP_H #endif // ROUTE_BGP_H

80
src/route_bgp.txt

@ -1,71 +1,9 @@
подсистема роутинга Ключевые изменения в понимании
utun - это сеть узлов. у каждого узла есть собственные локальные подсети.
глобальная задача: создать у каждого узла полную таблицу маршрутизации. tranzit_nodes — это динамический список лучших транзитных узлов для данного node.
Узлы преимущественно создают связь напрямую друг с другом. Но если это не получается отправляют трафик транзитом через доступные узлы. При conn down:
Иногда бывает что через транзитные узлы метрики лучше чем напрямую. Используем приоритетно узлы с лучшей метриков, при нехватке bandwidth используем разные каналы (агрегируем). Находим узел, связанный с этим соединением.
Динамически обновлем метрики каналов чтобы при отказе быстро переключаться на другие и не фризить обмен из-за отказов. Удаляем конкретный путь (этот транзитный узел) из списка tranzit_nodes.
Если после удаления tranzit_nodes стало пусто (нет активных путей) → узел считается недоступным → route_delete() + отправка WITHDRAW.
При получении NODEINFO — добавляем информацию о транзитных узлах (их может быть несколько).
и узлы обмениваются таблицой маршрутов между собой так чтобы у каждого была актуальная таблица подсетей всех узлов. При получении WITHDRAW — удаляем узел/маршруты и распространяем дальше.
маршрутами меняются клиенты, подключения которых которые взяты из конфига. и сервера принявшие подключения если клиент инициировал обмен маршрутами.
инициируется подключение, клиент отправляет свою таблицу. когда сервер принимает таблицу - сервер помечает что по этому маршруту надо обмениваться маршрутами, далее;
- добавляет узел в список рассылки обновлений маршрутов
- отправляет свою таблицу
- добавляет в свою таблицу отсутствующие маршруты
- если что-то добавил:
- рассылает измененные маршруты по списку рассылки
- список рассылки - это linked-list очередей (также на базе ll_queue - каждый элемент = подписчик). один маршрут = одна отправленная кодограмма
при подключении узла или изменении таблицы: узел шлёт свою таблицу
формат кодограммы: [0x01 - routing module] [subcmd] [data]
subcmd:
1 [route] - отправка маршрута
2, без данных - больше данных нет
если сервер получил кодограмму маршрута - он помечает флаг в etcp что с узлом надо обмениваться маршрутами (etcp_conn->routing_exchange_active=2) и добавляет в очередь рассылки маршрутов
=========================================
механизм инкрементальной синхронизации (реализация - потом, пока мысли)
1. вычисляем хеш каждой записи в роутинг таблице. используем ip+mask+node_uid
2. потом из этих хешей создаем хеш таблицу (старшие n бит номер ячейки). далее вычисляются хеши каждой ячейки.
на первом этапе n=16, на втором - n=16*16 на третьем n=16*16*16.
отправляем хеши удаленному узлу в формате: [n, 1 байт] ([индекс хеша 2 байта - используется n старших бит][хеш - 8 байт])
удаленный узел считает свои хеши и сравнивает. где не совпало смотрит сколько записей.
если записей не много - передает эти записи.
если записей много - добавляет 4 бита к хеш таблице и строит субтаблицу для
==========================================
Формат роутинга:
Таблица узлов состоит из записей:
- uid
- name
- links
- 3 транзитных узла с метриками (RTT)
- маршруты узла
- текущая загрузка линка (за последние 10 сек) можно частоту адаптировать под размер сети
- bandidth limit
- transit bandwidth limit
две группы узлов:
- узлы за nat. подключаются через транзитные узлы. измеряют пинги до транзитных и выбирают N (3 default) лучшие линки. 3 лучших используем для распространения маршрутов
- транзитные узлы. имеют линки с загрузкой.
добавить кодограмму - отменить распространение маршрутов по линку (+ сделать важным линком)
карта маршрутизации:
План:
- сделать передачу роутинга в
- сделать фоновый probe для узлов (условно 1 нода в секунду). выигравшие по качеству соатновятся основными
- сделать etcp дизконнект:
- отправить disconnect request + дождаться ack дальше master удаляет, slave удаляет по down.

693
src/route_lib.c

@ -1,19 +1,17 @@
#include "route_node.h"
#include "route_lib.h" #include "route_lib.h"
#include "etcp.h"
#include "etcp_debug.h" #include "etcp_debug.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
#include "../lib/mem.h" #include "../lib/mem.h"
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#define INITIAL_CAPACITY 100 #define INITIAL_CAPACITY 100
#define CONN_INFO_INIT_SIZE 4
// ============================================================================ // ============================================================================
// Вспомогательные функции (без изменений) // Вспомогательные функции
// ============================================================================ // ============================================================================
static uint32_t prefix_to_mask(uint8_t prefix) { static uint32_t prefix_to_mask(uint8_t prefix) {
@ -39,152 +37,8 @@ static bool check_route_overlap_in_table(uint32_t network, uint8_t prefix,
return false; return false;
} }
static struct NODE_CONNS_INFO* find_node_conns(struct ROUTE_TABLE *table, uint64_t node_id) {
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].conn_list && table->entries[i].conn_list->node_id == node_id) {
return table->entries[i].conn_list;
}
}
return NULL;
}
// ============================================================================ // ============================================================================
// NODE_CONNS_INFO — исправленный realloc + preferred_conn // Поиск (LPM)
// ============================================================================
static struct NODE_CONNS_INFO* node_conns_info_create(uint64_t node_id, uint8_t init_size) {
size_t total_size = sizeof(struct NODE_CONNS_INFO) + init_size * sizeof(struct NODE_CONN_INFO);
struct NODE_CONNS_INFO *info = u_calloc(1, total_size);
if (!info) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "node_conns_info_create: alloc failed");
return NULL;
}
info->node_id = node_id;
info->conninfo_count = 0;
info->conninfo_memsize = init_size; // исправлена опечатка
info->ref_count = 1;
info->preferred_conn = 0;
info->flags = ROUTE_FLAG_ACTIVE;
return info;
}
static struct NODE_CONNS_INFO* node_conns_info_add(struct ROUTE_TABLE *table,
struct NODE_CONNS_INFO *info,
uint32_t endpoint_ip, uint16_t endpoint_port,
const uint8_t *public_key,
struct ETCP_CONN *conn,
uint64_t *hop_list, uint8_t hop_count,
bool *out_needs_reroute) {
if (!info) return NULL;
if (out_needs_reroute) *out_needs_reroute = false;
// === 1. Обновление существующего подключения (решение проблемы #2) ===
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].conn_id == conn) {
struct NODE_CONN_INFO *ci = &info->conn_info[i];
// освобождаем старый hop_list
if (ci->hop_list) {
u_free(ci->hop_list);
ci->hop_list = NULL;
}
ci->endpoint_ip = endpoint_ip;
ci->endpoint_port = endpoint_port;
ci->hop_count = hop_count;
if (hop_count > 0 && hop_list) {
ci->hop_list = u_malloc(hop_count * sizeof(uint64_t));
if (ci->hop_list)
memcpy(ci->hop_list, hop_list, hop_count * sizeof(uint64_t));
}
if (public_key)
memcpy(ci->public_key, public_key, 64);
// если обновился preferred_conn — нужен reroute
if (i == info->preferred_conn && out_needs_reroute)
*out_needs_reroute = true;
return info;
}
}
// === 2. Добавление нового подключения (оригинальная логика) ===
if (info->conninfo_count >= info->conninfo_memsize) {
uint8_t new_size = info->conninfo_memsize * 2;
size_t new_total = sizeof(struct NODE_CONNS_INFO) + new_size * sizeof(struct NODE_CONN_INFO);
struct NODE_CONNS_INFO *new_info = u_realloc(info, new_total);
if (!new_info) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "node_conns_info_add: realloc failed");
return NULL;
}
new_info->conninfo_memsize = new_size;
// обновляем все указатели в таблице
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].conn_list == info)
table->entries[i].conn_list = new_info;
}
info = new_info;
}
struct NODE_CONN_INFO *ci = &info->conn_info[info->conninfo_count];
ci->endpoint_ip = endpoint_ip;
ci->endpoint_port = endpoint_port;
ci->conn_id = conn;
ci->hop_count = hop_count;
ci->hop_list = NULL;
if (hop_count > 0 && hop_list) {
ci->hop_list = u_malloc(hop_count * sizeof(uint64_t));
if (ci->hop_list)
memcpy(ci->hop_list, hop_list, hop_count * sizeof(uint64_t));
}
if (public_key)
memcpy(ci->public_key, public_key, 64);
info->conninfo_count++;
return info;
}
static bool node_conns_info_remove(struct NODE_CONNS_INFO *info, struct ETCP_CONN *conn) {
if (!info || !conn) return false;
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].conn_id == conn) {
if (info->conn_info[i].hop_list) {
u_free(info->conn_info[i].hop_list);
}
bool was_preferred = (i == info->preferred_conn);
if (i < info->conninfo_count - 1) {
memmove(&info->conn_info[i], &info->conn_info[i + 1],
(info->conninfo_count - i - 1) * sizeof(struct NODE_CONN_INFO));
}
info->conninfo_count--;
if (info->preferred_conn > i) info->preferred_conn--;
if (info->preferred_conn >= info->conninfo_count && info->conninfo_count > 0) {
info->preferred_conn = 0;
}
return was_preferred; // нужен reroute?
}
}
return false;
}
static void node_conns_info_destroy(struct NODE_CONNS_INFO *info) {
if (!info) return;
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].hop_list) u_free(info->conn_info[i].hop_list);
}
u_free(info);
}
// ============================================================================
// Поиск
// ============================================================================ // ============================================================================
static int binary_search_insert_pos(struct ROUTE_ENTRY *entries, size_t count, static int binary_search_insert_pos(struct ROUTE_ENTRY *entries, size_t count,
@ -195,7 +49,7 @@ static int binary_search_insert_pos(struct ROUTE_ENTRY *entries, size_t count,
int mid = left + (right - left) / 2; int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *e = &entries[mid]; struct ROUTE_ENTRY *e = &entries[mid];
if (e->network < network || if (e->network < network ||
(e->network == network && e->prefix_length > prefix_length)) { // longer prefix first (e->network == network && e->prefix_length > prefix_length)) {
left = mid + 1; left = mid + 1;
} else { } else {
right = mid; right = mid;
@ -204,50 +58,24 @@ static int binary_search_insert_pos(struct ROUTE_ENTRY *entries, size_t count,
return left; return left;
} }
static struct ROUTE_ENTRY* find_exact(struct ROUTE_TABLE *table,
uint32_t network, uint8_t prefix) {
if (!table) return NULL;
int left = 0, right = (int)table->count;
while (left < right) {
int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *e = &table->entries[mid];
if (e->network < network || (e->network == network && e->prefix_length >= prefix)) {
left = mid + 1;
} else {
right = mid;
}
}
if (left < (int)table->count &&
table->entries[left].network == network &&
table->entries[left].prefix_length == prefix) {
return &table->entries[left];
}
if (left > 0 && table->entries[left-1].network == network &&
table->entries[left-1].prefix_length == prefix) {
return &table->entries[left-1];
}
return NULL;
}
static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t dest_ip) { static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t dest_ip) {
if (table->count == 0) return NULL; if (table->count == 0) return NULL;
int left = 0; int left = 0;
int right = (int)table->count - 1; int right = (int)table->count - 1;
struct ROUTE_ENTRY *best = NULL; struct ROUTE_ENTRY *best = NULL;
uint8_t best_prefix = 0; uint8_t best_prefix = 0;
while (left <= right) { while (left <= right) {
int mid = left + (right - left) / 2; int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *entry = &table->entries[mid]; struct ROUTE_ENTRY *entry = &table->entries[mid];
uint32_t mask = prefix_to_mask(entry->prefix_length); uint32_t mask = prefix_to_mask(entry->prefix_length);
if ((dest_ip & mask) == (entry->network & mask)) { if ((dest_ip & mask) == (entry->network & mask)) {
if (entry->prefix_length > best_prefix) { if (entry->prefix_length > best_prefix) {
best_prefix = entry->prefix_length; best_prefix = entry->prefix_length;
best = entry; best = entry;
} }
if (mid + 1 < (int)table->count) { if (mid + 1 < (int)table->count) {
struct ROUTE_ENTRY *next = &table->entries[mid + 1]; struct ROUTE_ENTRY *next = &table->entries[mid + 1];
uint32_t next_mask = prefix_to_mask(next->prefix_length); uint32_t next_mask = prefix_to_mask(next->prefix_length);
@ -262,35 +90,21 @@ static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t
} else if (dest_ip > entry->network) { } else if (dest_ip > entry->network) {
left = mid + 1; left = mid + 1;
} else { } else {
if (mid > 0) {
struct ROUTE_ENTRY *prev = &table->entries[mid - 1];
uint32_t prev_mask = prefix_to_mask(prev->prefix_length);
if ((dest_ip & prev_mask) == (prev->network & prev_mask)) {
if (prev->prefix_length > best_prefix) {
best_prefix = prev->prefix_length;
best = prev;
}
}
}
right = mid - 1; right = mid - 1;
} }
} }
if (!best && left > 0 && left <= (int)table->count) { // fallback проверка предыдущей записи
int check = left - 1; if (!best && left > 0) {
if (check >= 0) { struct ROUTE_ENTRY *entry = &table->entries[left - 1];
struct ROUTE_ENTRY *entry = &table->entries[check]; uint32_t mask = prefix_to_mask(entry->prefix_length);
uint32_t mask = prefix_to_mask(entry->prefix_length); if ((dest_ip & mask) == (entry->network & mask)) {
if ((dest_ip & mask) == (entry->network & mask)) { best = entry;
best = entry;
}
} }
} }
return best; return best;
} }
// ============================================================================ // ============================================================================
// Основные функции // Основные функции
// ============================================================================ // ============================================================================
@ -301,146 +115,73 @@ struct ROUTE_TABLE *route_table_create(void) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc failed"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc failed");
return NULL; return NULL;
} }
table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY)); table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY));
if (!table->entries) { if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc entries failed"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc entries failed");
u_free(table); u_free(table);
return NULL; return NULL;
} }
table->capacity = INITIAL_CAPACITY; table->capacity = INITIAL_CAPACITY;
table->count = 0; table->count = 0;
table->dynamic_subnet_count = 0; table->dynamic_subnet_count = 0;
table->local_subnet_count = 0; table->local_subnet_count = 0;
memset(&table->stats, 0, sizeof(table->stats)); memset(&table->stats, 0, sizeof(table->stats));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table created, capacity %d", INITIAL_CAPACITY); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table created (simplified), capacity %d", INITIAL_CAPACITY);
return table; return table;
} }
void route_table_destroy(struct ROUTE_TABLE *table) { void route_table_destroy(struct ROUTE_TABLE *table) {
if (!table) { if (!table) return;
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_destroy: table is NULL");
return;
}
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
// Правильно освобождаем NODE_CONNS_INFO с учётом ref_count DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
// (один info может быть у нескольких префиксов)
for (size_t i = 0; i < table->count; i++) {
struct NODE_CONNS_INFO *info = table->entries[i].conn_list;
if (info) {
table->entries[i].conn_list = NULL; // защита от повторного использования
if (--info->ref_count == 0) {
node_conns_info_destroy(info);
}
}
}
// Освобождаем динамические/локальные подсети (если они были выделены) // v_node_info — внешний объект (BGP_NODEINFO_Q), память им не управляем
u_free(table->dynamic_subnets); u_free(table->dynamic_subnets);
u_free(table->local_subnets); u_free(table->local_subnets);
u_free(table->entries); u_free(table->entries);
u_free(table); u_free(table);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
} }
/* ====================== НОВАЯ ОСНОВНАЯ ФУНКЦИЯ ====================== */ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
bool route_insert(struct ROUTE_TABLE *table, if (!table || !node) {
const struct ROUTE_ENTRY *entry, DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments");
struct ETCP_CONN *conn,
uint64_t node_id,
uint64_t my_node_id,
uint64_t *hop_list,
uint8_t hop_count) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: table is NULL");
return false; return false;
} }
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: entry is NULL");
return false;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: network=%s prefix=%d conn=%p node_id=%016llx",
ip_to_string(entry->network).a, entry->prefix_length, (void*)conn, (unsigned long long)node_id);
// === 0. Loop prevention — ВСЕГДА (исправление проблемы #3) ===
if (conn && my_node_id && hop_list && hop_count > 0) {
for (uint8_t i = 0; i < hop_count; i++) {
if (hop_list[i] == my_node_id) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Loop detected: %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (loop detected)");
return false;
}
}
}
uint64_t peer_node_id=0;
if (conn) {
peer_node_id=conn->peer_node_id;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "peer: %s %016llx", conn->log_name, (unsigned long long)peer_node_id);
}
else DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "no peer");
// === Вызов сторонней функции (теперь без malloc) ===
const struct NODEINFO_IPV4_SUBNET *subnets = NULL;
int cnt = get_node_routes(node, &subnets);
// === 1. Точный матч префикса === if (cnt <= 0) {
struct ROUTE_ENTRY *existing = find_exact(table, entry->network, entry->prefix_length); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: get_node_routes returned %d subnets", cnt);
return false;
if (existing) { }
bool existing_local = (existing->conn_list == NULL);
bool new_local = (conn == NULL);
if (existing_local != new_local || size_t count = (size_t)cnt;
(!existing_local && existing->conn_list->node_id != node_id)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route conflict (different owner): %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (conflict)");
return false;
}
// === LOCAL route update === // === Проверка пересечений ===
if (new_local) { for (size_t i = 0; i < count; i++) {
existing->last_update = get_time_tb(); uint32_t network;
if (table->change_callback) memcpy(&network, subnets[i].addr, 4); // addr уже в network byte order (big-endian)
table->change_callback(table, existing, 1, peer_node_id, table->change_callback_arg); uint8_t prefix = subnets[i].prefix_length;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: UPDATED local route %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
return true; if (check_route_overlap_in_table(network, prefix,
} table->entries, table->count)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: overlap detected for %s/%d",
// === LEARNED route: обновляем hop_list или добавляем новый путь (исправление проблемы #2) === ip_to_string(network).a, prefix);
struct NODE_CONNS_INFO *info = existing->conn_list;
bool needs_reroute = false; // не используется здесь, но нужен для сигнатуры
info = node_conns_info_add(table, info, 0, 0, NULL, conn, hop_list, hop_count, &needs_reroute);
if (!info) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: FAILED (node_conns_info_add)");
return false; return false;
} }
existing->conn_list = info;
existing->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, existing, 1, peer_node_id, table->change_callback_arg);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: UPDATED learned route %s/%d node_id=%016llx",
ip_to_string(entry->network).a, entry->prefix_length, (unsigned long long)node_id);
return true;
} }
// === 2. Новый префикс === // === Реаллокация при необходимости ===
if (check_route_overlap_in_table(entry->network, entry->prefix_length, if (table->count + count > table->capacity) {
table->entries, table->count)) { size_t new_cap = table->capacity * 2;
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route overlap rejected: %s/%d", ip_to_string(entry->network).a, entry->prefix_length); while (new_cap < table->count + count) new_cap *= 2;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (overlap)");
return false;
}
// === 3. Реаллокация таблицы при необходимости ===
if (table->count >= table->capacity) {
uint32_t new_cap = table->capacity * 2;
struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_cap * sizeof(struct ROUTE_ENTRY)); struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_cap * sizeof(struct ROUTE_ENTRY));
if (!new_entries) { if (!new_entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: realloc failed"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: realloc failed");
@ -450,320 +191,118 @@ bool route_insert(struct ROUTE_TABLE *table,
table->capacity = new_cap; table->capacity = new_cap;
} }
// === 4. Поиск позиции для вставки (сортировка) === // === Вставка каждого префикса (таблица остаётся отсортированной) ===
int pos = binary_search_insert_pos(table->entries, table->count, for (size_t i = 0; i < count; i++) {
entry->network, entry->prefix_length); uint32_t network;
memcpy(&network, subnets[i].addr, 4);
// === 5. Создаём новую запись === uint8_t prefix_length = subnets[i].prefix_length;
struct ROUTE_ENTRY new_entry = *entry;
new_entry.created_time = get_time_tb();
new_entry.last_update = get_time_tb();
new_entry.conn_list = NULL;
if (conn) { int pos = binary_search_insert_pos(table->entries, table->count, network, prefix_length);
// === LEARNED route ===
uint64_t owner_node_id = node_id;
struct NODE_CONNS_INFO *node_info = find_node_conns(table, owner_node_id);
if (!node_info) { if (pos < (int)table->count) {
node_info = node_conns_info_create(owner_node_id, CONN_INFO_INIT_SIZE); memmove(&table->entries[pos + 1], &table->entries[pos],
if (!node_info) return false; (table->count - pos) * sizeof(struct ROUTE_ENTRY));
} else {
node_info->ref_count++;
} }
new_entry.conn_list = node_info; struct ROUTE_ENTRY *e = &table->entries[pos];
e->network = network;
bool dummy = false; e->prefix_length = prefix_length;
node_info = node_conns_info_add(table, node_info, 0, 0, NULL, conn, hop_list, hop_count, &dummy); e->v_node_info = node; // все префиксы узла ссылаются на один объект
if (!node_info) return false;
new_entry.conn_list = node_info; table->count++;
} else { table->stats.learned_routes++;
// === LOCAL route ===
table->stats.local_routes++;
}
// === 6. Вставка в отсортированный массив ===
if (pos < (int)table->count) {
memmove(&table->entries[pos + 1], &table->entries[pos],
(table->count - pos) * sizeof(struct ROUTE_ENTRY));
} }
table->entries[pos] = new_entry;
table->count++;
// === 7. Callback ===
if (table->change_callback) {
table->change_callback(table, &table->entries[pos], 0, peer_node_id, table->change_callback_arg);
}
// === 8. Отладочное сообщение ===
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Route %s: %s/%d (node %016llx)",
conn ? "learned" : "local", ip_to_string(entry->network).a, entry->prefix_length,
(unsigned long long)node_id);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: INSERTED %s %s/%d", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: added %zu route(s) for node_info=%p",
conn ? "learned" : "local", ip_to_string(entry->network).a, entry->prefix_length); count, (void*)node);
return true; return true;
} }
/* ====================== НОВАЯ ФУНКЦИЯ ====================== */ void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
void route_remove_conn(struct ROUTE_TABLE *table, struct ETCP_CONN *conn) { if (!table || !node) return;
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_conn: table is NULL");
return;
}
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_conn: conn is NULL");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: conn=%p peer=%016llx table_count=%zu",
(void*)conn, (unsigned long long)conn->peer_node_id, table->count);
// DIAGNOSTIC: поиск conn во всех entries перед началом DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removing all routes for node_info=%p", (void*)node);
size_t entries_with_conn = 0;
for (size_t i = 0; i < table->count; i++) {
struct NODE_CONNS_INFO *info = table->entries[i].conn_list;
if (!info) continue;
for (uint8_t j = 0; j < info->conninfo_count; j++) {
if (info->conn_info[j].conn_id == conn) {
entries_with_conn++;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, " pre_check: entry[%zu] node_id=%016llx conninfo_count=%d has_conn_at_index=%d",
i, (unsigned long long)info->node_id, info->conninfo_count, j);
}
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: pre_check found conn in %zu entries", entries_with_conn);
if (entries_with_conn == 0) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_remove_conn: conn NOT FOUND in any entry - skipping");
return;
}
struct NODE_CONNS_INFO* affected[512];
bool needs_reroute[512] = {0};
size_t aff_count = 0;
// Pass 1: удаляем conn (один раз на node_id)
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: Pass1 - removing conn from NODE_CONNS_INFO");
for (size_t i = 0; i < table->count; i++) {
struct NODE_CONNS_INFO *info = table->entries[i].conn_list;
if (!info) continue;
bool has_conn = false;
uint8_t conn_idx = 0;
for (uint8_t j = 0; j < info->conninfo_count; j++) {
if (info->conn_info[j].conn_id == conn) { has_conn = true; conn_idx = j; break; }
}
if (!has_conn) continue;
bool already = false;
for (size_t k = 0; k < aff_count; k++) {
if (affected[k] == info) { already = true; break; }
}
if (already) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, " entry[%zu] node_id=%016llx: already processed", i, (unsigned long long)info->node_id);
continue;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " entry[%zu] node_id=%016llx: removing conn at index=%d, conninfo_count was=%d ref_count=%d",
i, (unsigned long long)info->node_id, conn_idx, info->conninfo_count, info->ref_count);
bool rer = node_conns_info_remove(info, conn);
affected[aff_count] = info;
needs_reroute[aff_count] = rer;
aff_count++;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " after remove: conninfo_count=%d ref_count=%d was_preferred=%s",
info->conninfo_count, info->ref_count, rer ? "yes" : "no");
if (aff_count >= 512) break;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: Pass1 complete - affected %zu NODE_CONNS_INFO", aff_count);
// Pass 2: удаляем записи без оставшихся подключений
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: Pass2 - compacting entries");
size_t j = 0;
size_t entries_removed = 0;
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (e->conn_list && e->conn_list->conninfo_count == 0) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " removing entry[%zu] network=%s/%d node_id=%016llx ref_count=%d",
i, ip_to_string(e->network).a, e->prefix_length,
(unsigned long long)e->conn_list->node_id, e->conn_list->ref_count);
if (table->change_callback)
table->change_callback(table, e, 2, 0, table->change_callback_arg);
struct NODE_CONNS_INFO *info = e->conn_list;
e->conn_list = NULL;
if (--info->ref_count == 0) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " destroying NODE_CONNS_INFO (ref_count=0)");
node_conns_info_destroy(info);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " NODE_CONNS_INFO ref_count now=%d (still referenced)", info->ref_count);
}
entries_removed++;
continue;
}
if (i != j) table->entries[j] = table->entries[i];
j++;
}
table->count = j;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: Pass2 complete - removed %zu entries, new_count=%zu", entries_removed, table->count);
// Pass 3: reroute только если изменился preferred_conn
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: Pass3 - reroute check");
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (!e->conn_list) continue;
for (size_t k = 0; k < aff_count; k++) {
if (affected[k] == e->conn_list && needs_reroute[k]) {
e->last_update = get_time_tb();
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " entry[%zu] network=%s/%d: triggering REROUTE callback",
i, ip_to_string(e->network).a, e->prefix_length);
if (table->change_callback)
table->change_callback(table, e, 1, 0, table->change_callback_arg);
break;
}
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: DONE - affected %zu nodes, final_count=%zu", aff_count, table->count);
}
/* ====================== НОВАЯ ФУНКЦИЯ ДЛЯ WITHDRAW ====================== */
bool route_remove_path(struct ROUTE_TABLE *table,
struct ETCP_CONN *conn,
uint64_t node_id) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_path: table is NULL");
return false;
}
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_path: conn is NULL");
return false;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: node_id=%016llx conn=%p",
(unsigned long long)node_id, (void*)conn);
struct NODE_CONNS_INFO* info = find_node_conns(table, node_id);
if (!info) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_path: node not found");
return false;
}
bool was_preferred = node_conns_info_remove(info, conn);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_path: removed, was_preferred=%d remaining_conns=%d",
was_preferred, info->conninfo_count);
if (info->conninfo_count == 0) {
// Последний путь к узлу — полный withdraw
route_delete(table, node_id); // уже вызывает callback(2) и очищает память
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: WITHDRAW node_id=%016llx", (unsigned long long)node_id);
return true;
} else if (was_preferred) {
// Изменился preferred_conn — уведомляем о reroute для всех префиксов этого узла
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (e->conn_list == info) {
e->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, e, 1, 0, table->change_callback_arg);
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: REROUTE node_id=%016llx", (unsigned long long)node_id);
}
return false;
}
void route_delete(struct ROUTE_TABLE *table, uint64_t node_id) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_delete: table is NULL");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: node_id=%016llx", (unsigned long long)node_id);
size_t i = 0; size_t i = 0;
size_t removed = 0;
while (i < table->count) { while (i < table->count) {
struct ROUTE_ENTRY *entry = &table->entries[i]; if (table->entries[i].v_node_info == node) {
bool deleted = false; // v_node_info — внешний, не освобождаем
if (entry->conn_list && entry->conn_list->node_id == node_id) {
for (uint8_t j = 0; j < entry->conn_list->conninfo_count; j++) {
if (entry->conn_list->conn_info[j].hop_list) {
u_free(entry->conn_list->conn_info[j].hop_list);
}
}
entry->conn_list->conninfo_count = 0;
entry->conn_list->ref_count--;
if (entry->conn_list->ref_count == 0) {
node_conns_info_destroy(entry->conn_list);
}
entry->conn_list = NULL;
if (table->change_callback) {
table->change_callback(table, entry, 2, 0, table->change_callback_arg);
}
if (i < table->count - 1) { if (i < table->count - 1) {
memmove(&table->entries[i], &table->entries[i + 1], memmove(&table->entries[i], &table->entries[i + 1],
(table->count - i - 1) * sizeof(struct ROUTE_ENTRY)); (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
} }
table->count--; table->count--;
deleted = true; table->stats.learned_routes--;
} removed++;
continue;
if (!deleted) {
i++;
} }
i++;
}
if (removed > 0) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removed %zu route(s)", removed);
} }
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routes deleted for node %016llx", (unsigned long long)node_id);
} }
struct ROUTE_ENTRY* route_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) { struct ROUTE_ENTRY* route_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
if (!table) { if (!table) return NULL;
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_lookup: table is NULL");
return NULL;
}
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: dest_ip=%s", ip_to_string(dest_ip).a); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: dest_ip=%s", ip_to_string(dest_ip).a);
struct ROUTE_ENTRY *result = binary_search_lpm(table, dest_ip); struct ROUTE_ENTRY *result = binary_search_lpm(table, dest_ip);
if (result) { if (result) {
table->stats.routes_lookup_hits++; table->stats.routes_lookup_hits++;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: FOUND %s/%d node_id=%016llx", DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: FOUND %s/%d (node_info=%p)",
ip_to_string(result->network).a, result->prefix_length, ip_to_string(result->network).a, result->prefix_length,
result->conn_list ? (unsigned long long)result->conn_list->node_id : 0); (void*)result->v_node_info);
} else { } else {
table->stats.routes_lookup_misses++; table->stats.routes_lookup_misses++;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: NOT FOUND"); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: NOT FOUND");
} }
return result; return result;
} }
void route_table_print(const struct ROUTE_TABLE *table) { void route_table_print(const struct ROUTE_TABLE *table) {
if (!table) return; if (!table) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table ==="); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table (simplified) ===");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Count: %zu, Capacity: %zu", table->count, table->capacity); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Count: %zu / Capacity: %zu", table->count, table->capacity);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Local routes: %lu, Learned: %lu", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Learned routes: %lu", table->stats.learned_routes);
table->stats.local_routes, table->stats.learned_routes);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Lookup hits: %lu, misses: %lu", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Lookup hits: %lu, misses: %lu",
table->stats.routes_lookup_hits, table->stats.routes_lookup_misses); table->stats.routes_lookup_hits, table->stats.routes_lookup_misses);
for (size_t i = 0; i < table->count; i++) { for (size_t i = 0; i < table->count; i++) {
const struct ROUTE_ENTRY *entry = &table->entries[i]; const struct ROUTE_ENTRY *entry = &table->entries[i];
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d", i + 1, ip_to_string(entry->network).a, entry->prefix_length); i + 1, ip_to_string(entry->network).a, entry->prefix_length);
if (entry->conn_list) { if (entry->v_node_info) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " node_id=%016llx, conns=%d, ref=%d", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info);
(unsigned long long)entry->conn_list->node_id,
entry->conn_list->conninfo_count,
entry->conn_list->ref_count);
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL");
} }
} }
} }
int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) {
if (!subnet_str || !network || !prefix_length) return -1;
char buf[64];
strncpy(buf, subnet_str, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *slash = strchr(buf, '/');
if (!slash) return -1;
*slash = '\0';
if (inet_pton(AF_INET, buf, network) != 1) return -1;
*network = ntohl(*network); // приводим к big-endian как требует структура
int p = atoi(slash + 1);
if (p < 0 || p > 32) return -1;
*prefix_length = (uint8_t)p;
return 0;
}

81
src/route_lib.h

@ -3,6 +3,8 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
// задача модуля: поиск по таблице маршрутизации нужной записи, вставка-удаление маршрутов
// Forward declarations // Forward declarations
struct ETCP_CONNECTIONS; struct ETCP_CONNECTIONS;
@ -18,42 +20,6 @@ typedef enum {
} route_flags_t; } route_flags_t;
struct NODE_CONN_INFO {
uint32_t endpoint_ip; // IP для прямого подключения (к nexthop_node_id). 0 - нет IP (если мы - сервер)
uint16_t endpoint_port; // Порт для прямого подключения (к nexthop_node_id). 0 - нет PORT (если мы - сервер)
uint8_t public_key[64]; // публичный ключ узла (для прямого подключения)
struct ETCP_CONN* conn_id; // Указатель на подключение к next_hop, может быть null.
// next_hop - это первый элемент в hop_list.
// hop list может быть разный для разных подключений. Это надо понимать и учитывать.
uint64_t* hop_list; // маршрут до узла (next hop -> ... -> destination_hop): список промежуточных узлов (NODE ID, кол-во - hop_count) включая конечный узел, не включая наш узел. null - локальный маршрут
uint8_t hop_count; // Количество узлов до узла назначения. 0 - я (not used), 1 - direct connect (hoplist 1 запись nexthop_node_id = id узла назначения)
};
struct NODE_CONNS_INFO {// Один NODE_CONNS_INFO на один node_id. несколько маршрутов с одинаковым node_id должны ссылаться на один экземпляр NODE_CONNS_INFO.
uint64_t node_id; // ID узла назначения. Если = моему ID - локальный маршрут.
uint8_t flags; // флаги узла
uint8_t preferred_conn; // выбранное соединение (его будем распространять далее по BGP). по умолчанию 0. при изменении conn_info надо за ним присмотреть.
uint8_t conninfo_count; // число подключений к узлу
uint8_t conninfo_memsize; // размер выделенной памяти (оптимизация realloc)
uint16_t ref_count; // счетчик ссылок с разных route_entry для освобождения
struct NODE_CONN_INFO conn_info[0]; // сами подключения. управлять памятью своими силами. использовать u_malloc, u_reclloc, u_free (совместимо с stdlib). максимально просто - только увеличиваем при нехватке, не уменьшаем.
};
/**
* @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,
uint64_t changed_from, /**< peer_node_id от кого пришло изменение, 0 если локальное */
void* arg);
/** /**
* @brief Расширенная запись маршрута * @brief Расширенная запись маршрута
* *
@ -62,11 +28,7 @@ typedef void (*route_change_callback_fn)(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY { struct ROUTE_ENTRY {
uint32_t network; // Сетевой адрес (big-endian) uint32_t network; // Сетевой адрес (big-endian)
uint8_t prefix_length; // Длина префикса подсети uint8_t prefix_length; // Длина префикса подсети
struct NODE_CONNS_INFO* conn_list; // список прямых подключений к узлу. null если - локальный маршрут. struct BGP_NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
// node_id - в conn_list. Если нет доступных подключений - маршрут удаляем как бесполезный
// hop list / hop count - это относится к подключению. может быть разный для разных подключений.
uint64_t created_time; /**< Время создания (в timebase - 0.1ms) - use get_time_tb();*/
uint64_t last_update; /**< Время последнего обновления (в timebase - 0.1ms) */
}; };
/** /**
@ -89,8 +51,6 @@ struct ROUTE_TABLE {
uint64_t routes_lookup_hits; /**< Количество попаданий в поиск маршрутов */ uint64_t routes_lookup_hits; /**< Количество попаданий в поиск маршрутов */
uint64_t routes_lookup_misses; /**< Количество промахов в поиск маршрутов */ uint64_t routes_lookup_misses; /**< Количество промахов в поиск маршрутов */
} stats; /**< Статистика таблицы маршрутизации */ } stats; /**< Статистика таблицы маршрутизации */
route_change_callback_fn change_callback; /**< Callback при изменении маршрута */
void* change_callback_arg; /**< Аргумент для callback */
}; };
/** /**
@ -108,45 +68,20 @@ struct ROUTE_TABLE *route_table_create(void);
void route_table_destroy(struct ROUTE_TABLE *table); void route_table_destroy(struct ROUTE_TABLE *table);
/** /**
* @brief Вставляет новую запись в таблицу маршрутизации * @brief Вставляет в таблицу маршрутизации все подсети для указанного узла
* @param node узел, все маршруты которого нужно добавить
* *
* @param table Указатель на таблицу маршрутизации
* @param entry Указатель на вставляемую запись (conn_list должен быть NULL)
* @param conn Соединение (next_hop). NULL = локальный маршрут
* @param node_id ID узла-владельца префикса (из пакета BGP)
* @param my_node_id Наш собственный node_id (для обнаружения петель, 0 = отключить проверку)
* @param hop_list Список хопов (уже с препроцессингом)
* @param hop_count Количество хопов
* @return true если вставка/обновление успешно * @return true если вставка/обновление успешно
*/ */
bool route_insert(struct ROUTE_TABLE *table, bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
const struct ROUTE_ENTRY *entry,
struct ETCP_CONN *conn,
uint64_t node_id, // владелец префикса
uint64_t my_node_id, // наш node_id (для loop detection)
uint64_t *hop_list,
uint8_t hop_count);
/** Удаляет конкретное подключение из всех маршрутов (при падении линка) */
void route_remove_conn(struct ROUTE_TABLE *table, struct ETCP_CONN *conn);
/**
* @brief Удаляет один путь (через conn) к указанному node_id.
* Используется при получении WITHDRAW от соседа.
* Если путей больше не осталось полностью withdraw + callback(2).
* Если изменился preferred_conn reroute + callback(1).
*/
bool route_remove_path(struct ROUTE_TABLE *table,
struct ETCP_CONN *conn,
uint64_t node_id);
/** /**
* @brief Удаляет все записи из таблицы маршрутизации для указанного узла * @brief Удаляет все записи из таблицы маршрутизации для указанного узла
* *
* @param table Указатель на таблицу маршрутизации * @param table Указатель на таблицу маршрутизации
* @param node_id ID узла, все маршруты которого нужно удалить * @param node узел, все маршруты которого нужно удалить
*/ */
void route_delete(struct ROUTE_TABLE *table, uint64_t node_id); void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
/** /**
* @brief Выполняет поиск маршрута для заданного IP-адреса * @brief Выполняет поиск маршрута для заданного IP-адреса

17
src/route_lib.txt

@ -1,26 +1,31 @@
В таблице роутинга маршруты не пересекаются. Т.е. не может быть одновременно 192.168.1.1/24 и 192.168.1.100/30 В таблице роутинга маршруты не пересекаются. Т.е. не может быть одновременно 192.168.1.1/24 и 192.168.1.100/30
Как работает роутинг: Как работает роутинг:
1. типов маршрута бывает два: learned и local. 1. типов маршрута бывает два: learned (роутятся в ETCP instance) и local (роутятся в TUN).
local - локальные маршруты из конфига (опция конфига my_subnet=IP/Mask) local - локальные маршруты из конфига (опция конфига my_subnet=IP/Mask)
при инициализации они сразу добавляются в роутинг таблицу (и используются для отправки пакетов в tun интерфейс). при инициализации local сразу добавляются в роутинг таблицу (ипользуя запись BGP_NODEINFO_PACKET* my_nodeinfo).
2. При установке подключения к новому узлу мы отправляем этому узлу полностью свою таблицу маршрутизации (local + learned узлы). Если несколько доступных линков - отправляем только один - preferred_conn BGP_NODEINFO собирается на узле владельце node, далее распространяется по остальным узлам.
2. При установке подключения к новому узлу мы отправляем этому узлу все nodeinfo. Если несколько доступных линков - отправляем только один - preferred_conn
3. При изменении preferred_conn рассылаем reroute (например старый preferred_conn удален) 3. При изменении preferred_conn рассылаем reroute (например старый preferred_conn удален)
3. При удалении всех подключений рассылаем withdraw. 3. При удалении всех подключений рассылаем withdraw.
3. При получении маршрута мы смотрим есть ли такой маршрут уже в таблице. Если такой маршрут есть и ID узла другой - игнорируем с ошибкой. иначе добавляем/обновляем (для одного узла может быть несколько маршрутов). Игнорируем маршрут если наш ID есть в списке узлов (hop_list). 3. При получении BGP_NODEINFO_PACKET мы смотрим есть ли такой узел уже в таблице. Если узла нет - добавляем, если есть - обновляем информацию о узле включая его маршруты и список next_hop через которые доступен узел (nb_routes).
Добавляем так: Добавляем так:
инкрементируем hop_count инкрементируем hop_count
устанавливая etcp линк с которого приняли как next_hop, добавляем его в hop_list. устанавливая etcp линк с которого приняли как next_hop, добавляем его в hop_list.
рассылаем по всем активным линкам кроме линка с которого получили (обязательно) рассылаем по всем активным линкам кроме линка с которого получили (обязательно)
Обновление: обновляем hop_list если поменялся Обновление: обновляем hop_list если поменялся
4. При отключении от узла мы 4. При отключении от узла (если у node не осталось nb_routes) мы:
Удаляем все маршруты узла Удаляем node и вложенные структуры: все маршруты узла
Рассылаем withdraw для узла hop_id. Рассылаем withdraw для узла hop_id.
Логика рассылки withdraw: Логика рассылки withdraw:
Если получен withdraw - удаляем этот маршрут и распространяем withdraw или reroute по всем линкам кроме того с которого получили. В зависимости от изменений (остались ли резервные линки или изменился preferred_conn). Если получен withdraw - удаляем этот маршрут и распространяем withdraw или reroute по всем линкам кроме того с которого получили. В зависимости от изменений (остались ли резервные линки или изменился preferred_conn).
Метрик маршрута пока нет. используется первый доступный. Метрик маршрута пока нет. используется первый доступный.
Как работаеут маршрутизация:
destination IP -> route table lookup -> route entry (ip/mask, node_info*) -> выбор лучшего next_hop -> отправка в next_hop

25
src/routing.c

@ -2,6 +2,7 @@
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include "routing.h" #include "routing.h"
#include "route_lib.h" #include "route_lib.h"
#include "route_node.h"
#include "tun_if.h" #include "tun_if.h"
#include "packet_dump.h" #include "packet_dump.h"
#include "etcp.h" #include "etcp.h"
@ -139,21 +140,23 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
return; return;
} }
// Determine destination node ID // Determine destination node ID and connection
uint64_t dst_node_id = instance->node_id; // Default to local node uint64_t dst_node_id = instance->node_id; // Default to local node
struct ETCP_CONN* conn = NULL; struct ETCP_CONN* conn = NULL;
// Check route type: conn_list == NULL means local route if (route->v_node_info == NULL) {
if (route->conn_list == NULL) { // Local route - send to TUN
// Local route - send to TUN (entry has [cmd=0][IP data], TUN skips cmd byte) DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str);
// dst_node_id is already instance->node_id
} else { } else {
// Learned route - use first conn from conn_list // Learned route - use first available path from NODEINFO_Q
if (route->conn_list->conninfo_count > 0) { struct NODEINFO_Q* nq = route->v_node_info;
conn = route->conn_list->conn_info[0].conn_id; if (nq->paths && nq->paths->head) {
struct ll_entry* path_entry = nq->paths->head;
conn = (struct ETCP_CONN*)path_entry->data;
} }
if (!conn) { if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: route to %s has no next_hop, dropping", ip_to_str(&addr, AF_INET).str); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: no path for node %016llx",
(unsigned long long)nq->node.node_id);
instance->dropped_packets++; instance->dropped_packets++;
queue_entry_free(entry); queue_entry_free(entry);
queue_dgram_free(entry); queue_dgram_free(entry);
@ -162,7 +165,7 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
dst_node_id = conn->peer_node_id; dst_node_id = conn->peer_node_id;
if (!conn->normalizer) { if (!conn->normalizer) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: connection for %s has no normalizer, dropping", ip_to_str(&addr, AF_INET).str); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: connection for %s has no normalizer", ip_to_str(&addr, AF_INET).str);
instance->dropped_packets++; instance->dropped_packets++;
queue_entry_free(entry); queue_entry_free(entry);
queue_dgram_free(entry); queue_dgram_free(entry);
@ -176,7 +179,7 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
} }
else DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "NODE %016llx -> NODE %016llx", (unsigned long long)src_node_id, (unsigned long long)dst_node_id); else DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "NODE %016llx -> NODE %016llx", (unsigned long long)src_node_id, (unsigned long long)dst_node_id);
if (route->conn_list == NULL) { if (route->v_node_info == NULL) {
// Local route - send to TUN (entry has [cmd=0][IP data], TUN skips cmd byte) // Local route - send to TUN (entry has [cmd=0][IP data], TUN skips cmd byte)
int put_err = queue_data_put(instance->tun->input_queue, entry); int put_err = queue_data_put(instance->tun->input_queue, entry);
if (put_err != 0) { if (put_err != 0) {

6
src/utun_instance.c

@ -4,6 +4,7 @@
#include "config_updater.h" #include "config_updater.h"
#include "tun_if.h" #include "tun_if.h"
#include "tun_route.h" #include "tun_route.h"
#include "route_node.h"
#include "route_lib.h" #include "route_lib.h"
#include "routing.h" #include "routing.h"
#include "route_bgp.h" #include "route_bgp.h"
@ -78,10 +79,11 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
struct CFG_ROUTE_ENTRY* subnet = config->my_subnets; struct CFG_ROUTE_ENTRY* subnet = config->my_subnets;
while (subnet) { while (subnet) {
struct ROUTE_ENTRY entry = {0}; struct ROUTE_ENTRY entry = {0};
entry.network = ntohl(subnet->ip.addr.v4.s_addr); // Convert network byte order to host entry.network = ntohl(subnet->ip.addr.v4.s_addr);
entry.prefix_length = subnet->netmask; entry.prefix_length = subnet->netmask;
entry.v_node_info = NULL; // local route
if (route_insert(instance->rt, &entry, NULL, 0, instance->node_id, NULL, 0)) { if (route_insert(instance->rt, NULL)) { // TODO: create local NODEINFO_Q for local routes
struct in_addr addr; struct in_addr addr;
addr.s_addr = htonl(entry.network); addr.s_addr = htonl(entry.network);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d",

Loading…
Cancel
Save