Browse Source

в процессе доработки (tmp cpmmit)

nodeinfo-routing-update
jeka 4 weeks ago
parent
commit
bd33bf1d9f
  1. 599
      src/route_bgp.c
  2. 6
      src/route_bgp.h
  3. 545
      src/route_lib.c
  4. 39
      src/route_lib.h

599
src/route_bgp.c

@ -28,9 +28,18 @@
// Forward declarations
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
const struct ROUTE_ENTRY* route);
static void route_bgp_send_reroute(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
uint64_t old_node_id, const struct ROUTE_ENTRY* route);
static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_ENTRY* route,
struct ETCP_CONN* exclude);
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
struct ETCP_CONN* exclude);
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn, uint64_t node_id);
static void route_bgp_send_table_to_conn_internal(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY* entry,
int action, void* arg);
static void route_bgp_send_withdraw_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
static const char* route_type_to_str(route_type_t type) {
switch (type) {
@ -46,6 +55,19 @@ static void format_ip(uint32_t ip, char* buffer) {
snprintf(buffer, 16, "%u.%u.%u.%u", b[3], b[2], b[1], b[0]);
}
typedef struct {
uint8_t cmd;
uint8_t subcmd;
uint64_t node_id;
} BGP_WITHDRAW_PACKET __attribute__((packed));
typedef struct {
uint8_t cmd;
uint8_t subcmd;
uint64_t old_node_id;
struct ROUTE_PEER_INFO peer_info;
} BGP_REROUTE_PACKET __attribute__((packed));
/**
* @brief Рассылает маршрут всем соединениям (кроме exclude)
*/
@ -72,290 +94,409 @@ static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_
}
/**
* @brief Колбэк для приема роутинг-пакетов от ETCP
*
* Вызывается когда приходит пакет с ETCP_ID_ROUTE_ENTRY.
* Парсит пакет и добавляет маршрут в таблицу.
* @brief Рассылает withdraw всем соединениям (кроме exclude)
*/
static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry)
{
if (!from_conn || !entry || !entry->dgram || entry->len < sizeof(struct ROUTE_BGP_PACKET)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid BGP packet received");
if (entry) {
queue_dgram_free(entry);
queue_entry_free(entry);
}
return;
}
struct UTUN_INSTANCE* instance = from_conn->instance;
if (!instance || !instance->rt) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "No instance or routing table");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
// Проверяем команду
if (pkt->cmd != ETCP_ID_ROUTE_ENTRY) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP command: %d", pkt->cmd);
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
struct ETCP_CONN* exclude) {
if (!bgp) return;
char ip_buf[16];
uint32_t network = ntohl(pkt->network);
format_ip(network, ip_buf);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet: subcmd=%d, network=%s/%d, node_id=%016llx",
pkt->subcmd, ip_buf, pkt->prefix_length & 0x3F, (unsigned long long)pkt->node_id);
struct ll_entry* entry = bgp->senders_list->head;
int sent_count = 0;
switch (pkt->subcmd) {
case ROUTE_SUBCMD_ENTRY: {
// Создаем запись маршрута
struct ROUTE_ENTRY route;
memset(&route, 0, sizeof(route));
route.network = ntohl(pkt->network);
route.prefix_length = pkt->prefix_length & 0x3F; // Младшие 6 бит - длина префикса
route.next_hop = from_conn;
route.type = ROUTE_TYPE_LEARNED;
route.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_LEARNED;
// Метрики
route.metrics.hop_count = pkt->hop_count + 1; // Инкрементируем
route.metrics.latency_ms = pkt->latency / 10; // x0.1 ms -> ms
route.metrics.bandwidth_kbps = ntohl(pkt->bandwidth_kbps);
route.metrics.last_updated = 0; // Будет установлено при вставке
// BGP-специфичные поля
route.endpoint_ip = ntohl(pkt->endpoint_ip);
route.endpoint_port = ntohs(pkt->endpoint_port);
route.destination_node_id = pkt->node_id;
// Проверяем флаги
if (pkt->prefix_length & ROUTE_PREFIX_F_LOCAL) {
// Локальный маршрут - не пересылаем
route.flags |= ROUTE_FLAG_ADVERTISED;
}
// Вставляем в таблицу
char net_buf[16];
format_ip(route.network, net_buf);
size_t old_count = instance->rt->count;
if (route_table_insert(instance->rt, &route)) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added learned route: %s/%d via node %016llx (hops=%d)",
net_buf, route.prefix_length,
(unsigned long long)route.destination_node_id,
route.metrics.hop_count);
struct ROUTE_BGP* bgp = instance->bgp;
if (instance->rt->count > old_count && !(route.flags & ROUTE_FLAG_ADVERTISED)) {
// Новый маршрут - распространяем всем кроме источника
route_bgp_broadcast_route(bgp, &route, from_conn);
// Устанавливаем флаг, чтобы не повторять
route.flags |= ROUTE_FLAG_ADVERTISED;
}
} else {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert learned route: %s/%d",
net_buf, route.prefix_length);
}
break;
}
case ROUTE_SUBCMD_WITHDRAW: {
uint32_t network = ntohl(pkt->network);
uint8_t prefix_length = pkt->prefix_length & 0x3F;
if (route_table_delete_entry(instance->rt, network, prefix_length, from_conn)) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Withdrawn route: %s/%d from conn %p",
ip_buf, prefix_length, (void*)from_conn);
} else {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Withdraw failed: route %s/%d not found",
ip_buf, prefix_length);
}
break;
while (entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data;
if (item->conn != exclude) {
route_bgp_send_withdraw(bgp, item->conn, node_id);
sent_count++;
}
default:
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd);
break;
entry = entry->next;
}
queue_dgram_free(entry);
queue_entry_free(entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Broadcasted withdraw for node %016llx to %d connections",
(unsigned long long)node_id, sent_count);
}
/**
* @brief Отправляет маршрут конкретному соединению
*/
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
const struct ROUTE_ENTRY* route) {
const struct ROUTE_ENTRY* route) {
if (!bgp || !conn || !route) return;
// Создаем элемент для отправки
struct ll_entry* entry = queue_entry_new_from_pool(bgp->instance->pkt_pool);
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate entry for BGP send");
struct BGP_ENTRY_PACKET *pkt = u_calloc(1, sizeof(struct BGP_ENTRY_PACKET));
if (!pkt) return;
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_ENTRY;
pkt->peer_info.node_id = htobe64(route->node_id);
pkt->peer_info.network = htonl(route->network);
pkt->peer_info.prefix_length = route->prefix_length;
pkt->peer_info.hop_count = route->hop_count;
pkt->peer_info.latency = htons(route->latency);
// Add other fields if present, e.g., bandwidth
struct ll_entry *send_entry = queue_entry_new(0);
if (!send_entry) {
u_free(pkt);
return;
}
// Выделяем буфер для пакета
entry->dgram = u_malloc(BGP_PACKET_SIZE);
if (!entry->dgram) {
queue_entry_free(entry);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate dgram for BGP send");
return;
send_entry->dgram = (uint8_t*)pkt;
send_entry->len = sizeof(struct BGP_ENTRY_PACKET);
if (etcp_send(conn, send_entry) != 0) {
queue_entry_free(send_entry);
u_free(pkt);
}
entry->len = BGP_PACKET_SIZE;
entry->memlen = BGP_PACKET_SIZE;
}
/**
* @brief Отправляет reroute конкретному соединению
*/
static void route_bgp_send_reroute(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
uint64_t old_node_id, const struct ROUTE_ENTRY* route) {
if (!bgp || !conn || !route) return;
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
memset(pkt, 0, BGP_PACKET_SIZE);
struct BGP_REROUTE_PACKET *pkt = u_calloc(1, sizeof(struct BGP_REROUTE_PACKET));
if (!pkt) return;
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_ENTRY;
pkt->hop_count = route->metrics.hop_count;
pkt->prefix_length = route->prefix_length;
if (route->type == ROUTE_TYPE_LOCAL) {
pkt->prefix_length |= ROUTE_PREFIX_F_LOCAL;
pkt->subcmd = ROUTE_SUBCMD_ENTRY_REROUTE;
pkt->old_node_id = htobe64(old_node_id);
pkt->peer_info.node_id = htobe64(route->node_id);
pkt->peer_info.network = htonl(route->network);
pkt->peer_info.prefix_length = route->prefix_length;
pkt->peer_info.hop_count = route->hop_count;
pkt->peer_info.latency = htons(route->latency);
struct ll_entry *send_entry = queue_entry_new(0);
if (!send_entry) {
u_free(pkt);
return;
}
pkt->latency = route->metrics.latency_ms * 10; // ms -> x0.1 ms
pkt->bandwidth_kbps = htonl(route->metrics.bandwidth_kbps);
pkt->network = htonl(route->network);
pkt->endpoint_ip = htonl(route->endpoint_ip);
pkt->endpoint_port = htons(route->endpoint_port);
pkt->node_id = route->destination_node_id;
pkt->route_type = route->type;
send_entry->dgram = (uint8_t*)pkt;
send_entry->len = sizeof(struct BGP_REROUTE_PACKET);
// Отправляем через ETCP
if (etcp_send(conn, entry) != 0) {
queue_dgram_free(entry);
queue_entry_free(entry);
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to send BGP route to conn %p", (void*)conn);
} else {
char net_buf[16];
format_ip(route->network, net_buf);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sent BGP route: %s/%d to conn %p",
net_buf, route->prefix_length, (void*)conn);
if (etcp_send(conn, send_entry) != 0) {
queue_entry_free(send_entry);
u_free(pkt);
}
}
/**
* @brief Отправляет withdraw для конкретной подсети одному соединению
* @brief Отправляет withdraw конкретному соединению
*/
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
uint32_t network, uint8_t prefix_length) {
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn, uint64_t node_id) {
if (!bgp || !conn) return;
struct ll_entry* entry = queue_entry_new_from_pool(bgp->instance->pkt_pool);
if (!entry) return;
struct BGP_WITHDRAW_PACKET *pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET));
if (!pkt) return;
entry->dgram = u_malloc(BGP_PACKET_SIZE);
if (!entry->dgram) {
queue_entry_free(entry);
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
pkt->node_id = htobe64(node_id);
struct ll_entry *send_entry = queue_entry_new(0);
if (!send_entry) {
u_free(pkt);
return;
}
entry->len = BGP_PACKET_SIZE;
entry->memlen = BGP_PACKET_SIZE;
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
memset(pkt, 0, BGP_PACKET_SIZE);
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
pkt->network = htonl(network);
pkt->prefix_length = prefix_length;
send_entry->dgram = (uint8_t*)pkt;
send_entry->len = sizeof(struct BGP_WITHDRAW_PACKET);
if (etcp_send(conn, entry) != 0) {
queue_dgram_free(entry);
queue_entry_free(entry);
if (etcp_send(conn, send_entry) != 0) {
queue_entry_free(send_entry);
u_free(pkt);
}
}
/**
* @brief Рассылает withdraw всем соединениям (кроме exclude)
* @brief Отправляет withdraw для всех affected node_id при удалении conn
*/
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint32_t network,
uint8_t prefix_length, struct ETCP_CONN* exclude) {
if (!bgp) return;
struct ll_entry* entry = bgp->senders_list->head;
while (entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data;
if (item->conn != exclude) {
route_bgp_send_withdraw(bgp, item->conn, network, prefix_length);
static void route_bgp_send_withdraw_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn || !conn->instance || !conn->instance->rt) return;
struct ROUTE_TABLE* table = conn->instance->rt;
// Collect unique node_ids where next_hop == conn
uint64_t unique_nodes[1024]; // assume max
size_t unique_count = 0;
bool found;
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].next_hop == conn) {
uint64_t nid = table->entries[i].node_id;
found = false;
for (size_t j = 0; j < unique_count; j++) {
if (unique_nodes[j] == nid) {
found = true;
break;
}
}
if (!found && unique_count < 1024) {
unique_nodes[unique_count++] = nid;
}
}
entry = entry->next;
}
// Broadcast withdraw for each unique node_id
for (size_t j = 0; j < unique_count; j++) {
route_bgp_broadcast_withdraw(bgp, unique_nodes[j], conn); // exclude conn, but since removing, anyway
}
}
/**
* @brief Отправляет withdraw для всех маршрутов через закрытое соединение
* @brief Отправляет полную таблицу конкретному соединению
*/
static void route_bgp_send_withdraw_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !bgp->instance->rt) return;
static void route_bgp_send_table_to_conn_internal(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn || !bgp->instance || !bgp->instance->rt) return;
struct ROUTE_ENTRY* routes = NULL;
size_t count = 0;
struct ROUTE_TABLE* table = bgp->instance->rt;
// Получаем все маршруты (чтобы не итерать таблицу во время отправки)
for (size_t i = 0; i < bgp->instance->rt->count; i++) {
if (bgp->instance->rt->entries[i].next_hop == conn) {
count++;
}
for (size_t i = 0; i < table->count; i++) {
route_bgp_send_route(bgp, conn, &table->entries[i]);
}
if (count == 0) return;
routes = u_calloc(count, sizeof(struct ROUTE_ENTRY));
if (!routes) return;
size_t idx = 0;
for (size_t i = 0; i < bgp->instance->rt->count; i++) {
if (bgp->instance->rt->entries[i].next_hop == conn) {
routes[idx++] = bgp->instance->rt->entries[i];
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sent full routing table (%zu entries) to connection %p",
table->count, (void*)conn);
}
/**
* @brief Колбэк для приема роутинг-пакетов от ETCP
*
* Вызывается когда приходит пакет с ETCP_ID_ROUTE_ENTRY.
* Парсит пакет и добавляет маршрут в таблицу.
*/
static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry)
{
if (!from_conn || !entry || !entry->dgram || entry->len < 2) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid BGP packet received");
if (entry) {
queue_dgram_free(entry);
queue_entry_free(entry);
}
return;
}
// Рассылаем withdraw для каждого
struct ll_entry* entry = bgp->senders_list->head;
while (entry) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)entry->data;
if (item->conn != conn) {
for (size_t i = 0; i < count; i++) {
route_bgp_send_withdraw(bgp, item->conn, routes[i].network, routes[i].prefix_length);
}
}
entry = entry->next;
struct UTUN_INSTANCE* instance = from_conn->instance;
if (!instance || !instance->rt) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "No instance or routing table");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
u_free(routes);
}
/**
* @brief Отправляет полную таблицу маршрутов конкретному соединению
*/
static void route_bgp_send_table_to_conn_internal(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn || !bgp->instance->rt) return;
struct BGP_ENTRY_PACKET* pkt = (struct BGP_ENTRY_PACKET*)entry->dgram;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending full routing table to conn %p", (void*)conn);
// Проверяем команду
if (pkt->cmd != ETCP_ID_ROUTE_ENTRY) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP command: %d", pkt->cmd);
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
char ip_buf[16];
uint32_t network;
uint8_t prefix_length;
uint64_t node_id;
for (size_t i = 0; i < bgp->instance->rt->count; i++) {
const struct ROUTE_ENTRY* route = &bgp->instance->rt->entries[i];
if (route->type == ROUTE_TYPE_LOCAL ||
(route->type == ROUTE_TYPE_LEARNED && (route->flags & ROUTE_FLAG_ADVERTISED))) {
route_bgp_send_route(bgp, conn, route);
switch (pkt->subcmd) {
case ROUTE_SUBCMD_ENTRY:
case ROUTE_SUBCMD_ENTRY_REROUTE: {
uint64_t old_node_id = 0;
struct ROUTE_PEER_INFO *peer_info;
if (pkt->subcmd == ROUTE_SUBCMD_ENTRY_REROUTE) {
if (entry->len < sizeof(BGP_REROUTE_PACKET)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid REROUTE packet size");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
BGP_REROUTE_PACKET *reroute_pkt = (BGP_REROUTE_PACKET*)pkt;
old_node_id = be64toh(reroute_pkt->old_node_id);
peer_info = &reroute_pkt->peer_info;
} else {
if (entry->len < sizeof(BGP_ENTRY_PACKET)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid ENTRY packet size");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
peer_info = &pkt->peer_info;
}
node_id = be64toh(peer_info->node_id);
network = ntohl(peer_info->network);
prefix_length = peer_info->prefix_length;
uint8_t hop_count = peer_info->hop_count;
uint16_t latency = ntohs(peer_info->latency);
format_ip(network, ip_buf);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP %s: network=%s/%d, node_id=%016llx, hop=%d, lat=%u",
(pkt->subcmd == ROUTE_SUBCMD_ENTRY) ? "ENTRY" : "REROUTE",
ip_buf, prefix_length, (unsigned long long)node_id, hop_count, latency);
struct ROUTE_ENTRY new_route;
memset(&new_route, 0, sizeof(new_route));
new_route.node_id = node_id;
new_route.network = network;
new_route.prefix_length = prefix_length;
new_route.next_hop = from_conn;
new_route.type = ROUTE_TYPE_LEARNED;
new_route.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_LEARNED;
new_route.hop_count = hop_count + 1;
new_route.latency = latency + (from_conn->rtt_last * 10);
new_route.metrics.latency_ms = new_route.latency / 10;
new_route.metrics.hop_count = new_route.hop_count;
// Set defaults for other metrics
new_route.metrics.bandwidth_kbps = 100000; // example default
new_route.metrics.packet_loss_rate = 0;
route_update_quality(&new_route);
struct ROUTE_TABLE* table = instance->rt;
bool exists = false;
bool updated = false;
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY* existing = &table->entries[i];
if (existing->network == network && existing->prefix_length == prefix_length) {
exists = true;
if (new_route.hop_count < existing->hop_count) {
// Update to better route
existing->next_hop = new_route.next_hop;
existing->hop_count = new_route.hop_count;
existing->latency = new_route.latency;
existing->metrics = new_route.metrics;
existing->last_update = get_time_tb();
if (table->change_callback) {
table->change_callback(table, existing, 1, table->change_callback_arg);
}
updated = true;
}
break;
}
}
if (!exists) {
new_route.created_time = get_time_tb();
new_route.last_update = new_route.created_time;
new_route.metrics.last_updated = new_route.created_time;
if (route_table_insert(table, &new_route)) {
updated = true; // treat insert as update for broadcast
}
}
if (updated) {
route_bgp_broadcast_route(bgp, &new_route, from_conn);
}
break;
}
case ROUTE_SUBCMD_WITHDRAW: {
if (entry->len < sizeof(BGP_WITHDRAW_PACKET)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid WITHDRAW packet size");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
BGP_WITHDRAW_PACKET *withdraw_pkt = (BGP_WITHDRAW_PACKET*)pkt;
uint64_t withdrawn_node_id = be64toh(withdraw_pkt->node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received WITHDRAW for node %016llx",
(unsigned long long)withdrawn_node_id);
struct ROUTE_TABLE* table = instance->rt;
bool has_alternative = false;
size_t i = 0;
while (i < table->count) {
struct ROUTE_ENTRY* rte = &table->entries[i];
if (rte->node_id == withdrawn_node_id) {
if (rte->next_hop == from_conn) {
route_table_delete_entry(table, rte->network, rte->prefix_length, from_conn);
// Do not increment i, array shifted
} else {
has_alternative = true;
i++;
}
} else {
i++;
}
}
if (has_alternative) {
// Send reroute for each remaining route to from_conn
for (size_t j = 0; j < table->count; j++) {
if (table->entries[j].node_id == withdrawn_node_id) {
route_bgp_send_reroute(bgp, from_conn, withdrawn_node_id, &table->entries[j]);
}
}
} else {
// Propagate withdraw
route_bgp_broadcast_withdraw(bgp, withdrawn_node_id, from_conn);
}
break;
}
case ROUTE_SUBCMD_NODEINFO: {
if (entry->len < sizeof(struct BGP_NODEINFO_PACKET)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid NODEINFO packet size");
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
struct BGP_NODEINFO_PACKET *info_pkt = (struct BGP_NODEINFO_PACKET*)pkt;
struct NODE_CONNS_INFO *node_info = &info_pkt->node_info;
if (node_info->connlist_count == 0) break;
uint64_t target_node_id = be64toh(node_info->NODE_CONN_INFO[0].node_id); // assume first
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received NODEINFO for node %016llx, count=%d",
(unsigned long long)target_node_id, node_info->connlist_count);
struct ROUTE_TABLE* table = instance->rt;
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].node_id == target_node_id) {
size_t size = sizeof(struct NODE_CONNS_INFO) +
node_info->connlist_count * sizeof(struct NODE_CONN_INFO);
struct NODE_CONNS_INFO *new_list = u_malloc(size);
if (new_list) {
memcpy(new_list, node_info, size);
new_list->ref_count = 1;
if (table->entries[i].conn_list) {
if (--table->entries[i].conn_list->ref_count == 0) {
u_free(table->entries[i].conn_list);
}
}
table->entries[i].conn_list = new_list;
if (table->change_callback) {
table->change_callback(table, &table->entries[i], 1, table->change_callback_arg);
}
}
break;
}
}
break;
}
default:
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd);
break;
}
queue_dgram_free(entry);
queue_entry_free(entry);
}
/**
* @brief Callback на изменение таблицы маршрутизации
* @brief Callback на изменение маршрута (broadcast при insert/update)
*/
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
struct ROUTE_ENTRY* entry,
@ -364,22 +505,14 @@ static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
if (!bgp || !entry) return;
if (action == 0 || action == 1) { // insert or update
// Анонсируем только локальные и статические маршруты
if (entry->type == ROUTE_TYPE_LOCAL) {
route_bgp_broadcast_route(bgp, entry, NULL);
}
} else if (action == 2) { // delete
// Отправляем withdraw всем
route_bgp_broadcast_withdraw(bgp, entry->network, entry->prefix_length, NULL);
route_bgp_broadcast_route(bgp, entry, NULL);
}
// for delete, handled in remove_conn/withdraw
}
/**
* @brief Инициализация BGP модуля
*/
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance)
{
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot init BGP: no instance");
return NULL;
}

6
src/route_bgp.h

@ -21,9 +21,7 @@ struct ETCP_CONN;
/**
* @brief Пакет маршрутной информации (бинарный формат)
*/
struct PEER_INFO_ENTRY {
struct ROUTE_PEER_INFO;
}
// struct PEER_INFO_ENTRY -> struct ROUTE_PEER_INFO;
struct BGP_ENTRY_PACKET {
uint8_t cmd; // ETCP_CMD_ROUTE (1)
@ -34,7 +32,7 @@ struct BGP_ENTRY_PACKET {
struct BGP_NODEINFO_PACKET {
uint8_t cmd; // ETCP_CMD_ROUTE (1)
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO (4)
struct ROUTE_NODE_INFO node_info;
struct NODE_CONN_INFO conn_info;
} __attribute__((packed));

545
src/route_lib.c

@ -118,9 +118,11 @@ bool route_update_quality(struct ROUTE_ENTRY *entry) {
// Простая формула: latency * 2 + loss * 10 + hops * 50
// Меньше значение = лучше маршрут
entry->metrics.metric = entry->metrics.latency_ms * 2 +
// Используем latency из PEER_INFO (x0.1 ms) -> конвертируем в ms
uint16_t latency_ms = entry->latency / 10;
entry->metrics.metric = latency_ms * 2 +
entry->metrics.packet_loss_rate * 10 +
entry->metrics.hop_count * 50;
entry->hop_count * 50;
return true;
}
@ -148,15 +150,11 @@ static int compare_routes_by_quality(const void *a, const void *b) {
/**
* @brief Ищет запись в кеше по IP
*/
static struct ROUTE_ARRAY* cache_lookup(uint32_t dest_ip) {
uint64_t current_time = (uint64_t)time(NULL) * 1000000;
static struct ROUTE_ARRAY* route_cache_find(uint32_t ip) {
for (int i = 0; i < g_route_cache.count; i++) {
if (g_route_cache.keys[i] == dest_ip && g_route_cache.entries[i]) {
g_route_cache.access_time[i] = current_time;
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Cache hit for IP %s", ip_str);
if (g_route_cache.keys[i] == ip) {
// Update access time
g_route_cache.access_time[i] = get_time_tb();
return g_route_cache.entries[i];
}
}
@ -164,397 +162,278 @@ static struct ROUTE_ARRAY* cache_lookup(uint32_t dest_ip) {
}
/**
* @brief Добавляет результат в кеш (LRU замещение)
* @brief Добавляет или обновляет запись в кеше
*/
static void cache_insert(uint32_t dest_ip, struct ROUTE_ARRAY *result) {
uint64_t current_time = (uint64_t)time(NULL) * 1000000;
int insert_idx = -1;
// Ищем пустое место или самую старую запись
static void route_cache_add(uint32_t ip, struct ROUTE_ARRAY* array) {
if (g_route_cache.count < ROUTE_CACHE_SIZE) {
insert_idx = g_route_cache.count++;
int idx = g_route_cache.count++;
g_route_cache.keys[idx] = ip;
g_route_cache.entries[idx] = array;
g_route_cache.access_time[idx] = get_time_tb();
} else {
// LRU: находим запись с самым старым access_time
uint64_t oldest_time = g_route_cache.access_time[0];
insert_idx = 0;
// Find least recently used
int lru_idx = 0;
uint64_t min_time = g_route_cache.access_time[0];
for (int i = 1; i < ROUTE_CACHE_SIZE; i++) {
if (g_route_cache.access_time[i] < oldest_time) {
oldest_time = g_route_cache.access_time[i];
insert_idx = i;
if (g_route_cache.access_time[i] < min_time) {
min_time = g_route_cache.access_time[i];
lru_idx = i;
}
}
// Освобождаем старую запись
if (g_route_cache.entries[insert_idx]) {
route_array_destroy(g_route_cache.entries[insert_idx]);
}
// Replace LRU
route_array_destroy(g_route_cache.entries[lru_idx]);
g_route_cache.keys[lru_idx] = ip;
g_route_cache.entries[lru_idx] = array;
g_route_cache.access_time[lru_idx] = get_time_tb();
}
g_route_cache.keys[insert_idx] = dest_ip;
g_route_cache.entries[insert_idx] = result;
g_route_cache.access_time[insert_idx] = current_time;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Cache insert for IP %u at slot %d", dest_ip, insert_idx);
}
/**
* @brief Преобразует тип маршрута в строку
* @param type Тип маршрута
* @return Строковое представление типа
* @brief Очищает глобальный кеш маршрутов
*/
static const char *route_type_to_string(route_type_t type) {
switch (type) {
case ROUTE_TYPE_STATIC: return "STATIC";
case ROUTE_TYPE_DYNAMIC: return "DYNAMIC";
case ROUTE_TYPE_LOCAL: return "LOCAL";
case ROUTE_TYPE_LEARNED: return "LEARNED";
default: return "UNKNOWN";
void route_cache_clear(void) {
for (int i = 0; i < g_route_cache.count; i++) {
route_array_destroy(g_route_cache.entries[i]);
g_route_cache.entries[i] = NULL;
}
g_route_cache.count = 0;
}
/**
* @brief Создает новую таблицу маршрутизации
* @return Указатель на созданную таблицу или NULL в случае ошибки
*/
struct ROUTE_TABLE *route_table_create(void) {
struct ROUTE_TABLE *table = u_calloc(1, sizeof(struct ROUTE_TABLE));
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for routing table");
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate table");
return NULL;
}
table->capacity = INITIAL_ROUTE_CAPACITY;
table->entries = u_calloc(table->capacity, sizeof(struct ROUTE_ENTRY));
table->entries = u_calloc(INITIAL_ROUTE_CAPACITY, sizeof(struct ROUTE_ENTRY));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for %zu route entries", table->capacity);
u_free(table);
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate entries");
return NULL;
}
// Initialize subnet validation arrays
table->dynamic_subnets = u_calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
table->local_subnets = u_calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
if (!table->dynamic_subnets || !table->local_subnets) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for subnet validation arrays");
u_free(table->entries);
u_free(table->dynamic_subnets);
u_free(table->local_subnets);
u_free(table);
return NULL;
}
table->capacity = INITIAL_ROUTE_CAPACITY;
table->count = 0;
table->dynamic_subnets = NULL;
table->dynamic_subnet_count = 0;
table->local_subnets = NULL;
table->local_subnet_count = 0;
memset(&table->stats, 0, sizeof(table->stats));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table created with capacity %u", INITIAL_ROUTE_CAPACITY);
return table;
}
/**
* @brief Уничтожает таблицу маршрутизации и освобождает ресурсы
* @param table Указатель на таблицу маршрутизации
*/
void route_table_destroy(struct ROUTE_TABLE *table) {
if (!table) return;
// Free route entries
// Free entries
u_free(table->entries);
// Free validation ranges
u_free(table->dynamic_subnets);
u_free(table->local_subnets);
u_free(table);
}
if (table->dynamic_subnets) u_free(table->dynamic_subnets);
if (table->local_subnets) u_free(table->local_subnets);
/**
* @brief Очищает глобальный кеш маршрутов
*/
void route_cache_clear(void) {
for (int i = 0; i < g_route_cache.count; i++) {
if (g_route_cache.entries[i]) {
route_array_destroy(g_route_cache.entries[i]);
g_route_cache.entries[i] = NULL;
}
}
g_route_cache.count = 0;
u_free(table);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
}
/**
* @brief Валидирует маршрут на соответствие разрешенным подсетям
* @brief Вставляет новую запись в таблицу маршрутизации
* @param table Указатель на таблицу маршрутизации
* @param entry Указатель на вставляемую запись маршрута
* @return true если вставка успешна, false иначе
*/
static bool route_validate_route(struct ROUTE_TABLE *table, uint32_t network,
uint8_t prefix_length, route_type_t route_type) {
if (!table) return false;
uint32_t *validation_ranges = NULL;
size_t range_count = 0;
switch (route_type) {
case ROUTE_TYPE_STATIC:
case ROUTE_TYPE_LEARNED:
return true; // No validation for static or learned routes
case ROUTE_TYPE_DYNAMIC:
validation_ranges = table->dynamic_subnets;
range_count = table->dynamic_subnet_count;
break;
case ROUTE_TYPE_LOCAL:
validation_ranges = table->local_subnets;
range_count = table->local_subnet_count;
break;
default:
return false;
}
if (range_count == 0) return true; // No validation ranges configured
// Check if network falls within any validation range
for (size_t i = 0; i < range_count; i += 2) {
uint32_t range_network = validation_ranges[i];
uint8_t range_prefix = (uint8_t)validation_ranges[i + 1];
uint32_t mask = (range_prefix == 0) ? 0 : (0xFFFFFFFFU << (32 - range_prefix));
if ((network & mask) == (range_network & mask)) {
// Check if this route is more specific than or equal to the validation range
if (prefix_length >= range_prefix) {
return true;
}
}
}
return false;
}
bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *entry) {
if (!table || !entry) return false;
// Validate the route
if (!route_validate_route(table, entry->network, entry->prefix_length, entry->type)) {
char ip_str[16];
ip_to_string(entry->network, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Ignoring invalid route: %s/%d (type: %s)",
ip_str, entry->prefix_length, route_type_to_string(entry->type));
return false;
}
// Check if we need to expand the table
// Check capacity and expand if needed
if (table->count >= table->capacity) {
size_t new_capacity = table->capacity * ROUTE_EXPANSION_FACTOR;
uint32_t new_capacity = table->capacity * ROUTE_EXPANSION_FACTOR;
struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_capacity * sizeof(struct ROUTE_ENTRY));
if (!new_entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_insert: failed to expand routing table to %zu entries", new_capacity);
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_insert: failed to realloc entries");
return false;
}
// Zero new memory
memset(new_entries + table->capacity, 0, (new_capacity - table->capacity) * sizeof(struct ROUTE_ENTRY));
table->entries = new_entries;
table->capacity = new_capacity;
}
// Проверяем, не существует ли уже такой маршрут (по network + prefix_length + next_hop)
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].network == entry->network &&
table->entries[i].prefix_length == entry->prefix_length &&
table->entries[i].next_hop == entry->next_hop) {
// Обновляем существующий маршрут
table->entries[i] = *entry;
table->entries[i].last_update = (uint64_t)time(NULL) * 1000000;
// Вызываем callback (action=1 - update)
if (table->change_callback) {
table->change_callback(table, &table->entries[i], 1, table->change_callback_arg);
}
return true;
}
}
// Set timestamps
struct ROUTE_ENTRY new_entry = *entry;
new_entry.created_time = new_entry.last_update = (uint64_t)time(NULL) * 1000000;
// Insert at the end (order doesn't matter, lookup will sort)
table->entries[table->count++] = new_entry;
// Вызываем callback (action=0 - insert)
if (table->change_callback) {
table->change_callback(table, &table->entries[table->count - 1], 0, table->change_callback_arg);
}
// Copy entry
table->entries[table->count] = *entry;
// Update timestamps
uint64_t now = get_time_tb();
table->entries[table->count].created_time = now;
table->entries[table->count].last_update = now;
table->entries[table->count].metrics.last_updated = now;
// Update quality
route_update_quality(&table->entries[table->count]);
// Update stats
if (new_entry.type == ROUTE_TYPE_LOCAL) {
table->stats.local_routes++;
} else if (new_entry.type == ROUTE_TYPE_LEARNED) {
table->stats.learned_routes++;
if (entry->type == ROUTE_TYPE_LOCAL) table->stats.local_routes++;
else if (entry->type == ROUTE_TYPE_LEARNED) table->stats.learned_routes++;
table->count++;
// Invalidate cache if needed
route_cache_clear(); // Simple invalidation, can be optimized
// Call change callback if set
if (table->change_callback) {
table->change_callback(table, &table->entries[table->count - 1], 0 /* insert */, table->change_callback_arg);
}
char ip_str[16];
ip_to_string(new_entry.network, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Route inserted: %s/%d (%s)",
ip_str, new_entry.prefix_length, route_type_to_string(new_entry.type));
ip_to_string(entry->network, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Inserted route: %s/%d (type: %d)", ip_str, entry->prefix_length, entry->type);
return true;
}
/**
* @brief Удаляет запись из таблицы маршрутизации
* @param table Указатель на таблицу маршрутизации
* @param conn соединение все роуты которого надо удалить
*/
void route_table_delete(struct ROUTE_TABLE *table, struct ETCP_CONN* conn) {
if (!table || !conn) return;
size_t removed = 0;
size_t write_idx = 0;
for (size_t i = 0; i < table->count; i++) {
size_t i = 0;
while (i < table->count) {
if (table->entries[i].next_hop == conn) {
// Обновляем статистику
if (table->entries[i].type == ROUTE_TYPE_LOCAL) {
table->stats.local_routes--;
} else if (table->entries[i].type == ROUTE_TYPE_LEARNED) {
table->stats.learned_routes--;
// Call change callback before removal
if (table->change_callback) {
table->change_callback(table, &table->entries[i], 2 /* delete */, table->change_callback_arg);
}
removed++;
} else {
// Сохраняем запись
if (write_idx != i) {
table->entries[write_idx] = table->entries[i];
// Update stats
if (table->entries[i].type == ROUTE_TYPE_LOCAL) table->stats.local_routes--;
else if (table->entries[i].type == ROUTE_TYPE_LEARNED) table->stats.learned_routes--;
// Shift remaining entries
if (i < table->count - 1) {
memmove(&table->entries[i], &table->entries[i + 1], (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
}
write_idx++;
}
}
table->count = write_idx;
// Очищаем освободившиеся записи
for (size_t i = write_idx; i < write_idx + removed && i < table->capacity; i++) {
memset(&table->entries[i], 0, sizeof(struct ROUTE_ENTRY));
}
// Инвалидируем кеш
for (int i = 0; i < g_route_cache.count; i++) {
if (g_route_cache.entries[i]) {
route_array_destroy(g_route_cache.entries[i]);
g_route_cache.entries[i] = NULL;
table->count--;
} else {
i++;
}
}
g_route_cache.count = 0;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Removed %zu routes for connection %p", removed, (void*)conn);
// Invalidate cache
route_cache_clear();
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Deleted routes for connection %p", (void*)conn);
}
/**
* @brief Удаляет конкретный маршрут из таблицы
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса подсети
* @param next_hop Указатель на соединение (NULL = любой next_hop)
* @return true если маршрут найден и удален, false если не найден
*/
bool route_table_delete_entry(struct ROUTE_TABLE *table, uint32_t network,
uint8_t prefix_length, struct ETCP_CONN* next_hop) {
uint8_t prefix_length, struct ETCP_CONN* next_hop) {
if (!table) return false;
int found_idx = -1;
// Ищем маршрут
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].network == network &&
table->entries[i].prefix_length == prefix_length) {
// Если указан next_hop - проверяем его тоже
if (next_hop == NULL || table->entries[i].next_hop == next_hop) {
found_idx = (int)i;
break;
if (table->entries[i].network == network &&
table->entries[i].prefix_length == prefix_length &&
(next_hop == NULL || table->entries[i].next_hop == next_hop)) {
// Call change callback
if (table->change_callback) {
table->change_callback(table, &table->entries[i], 2 /* delete */, table->change_callback_arg);
}
// Update stats
if (table->entries[i].type == ROUTE_TYPE_LOCAL) table->stats.local_routes--;
else if (table->entries[i].type == ROUTE_TYPE_LEARNED) table->stats.learned_routes--;
// Shift remaining
if (i < table->count - 1) {
memmove(&table->entries[i], &table->entries[i + 1], (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
}
table->count--;
// Invalidate cache
route_cache_clear();
char ip_str[16];
ip_to_string(network, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Deleted specific route: %s/%d", ip_str, prefix_length);
return true;
}
}
if (found_idx == -1) {
// Маршрут не найден
char ip_str[16];
ip_to_string(network, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route to delete not found: %s/%d",
ip_str, prefix_length);
return false;
}
// Вызываем callback перед удалением (action=2 - delete)
if (table->change_callback) {
table->change_callback(table, &table->entries[found_idx], 2, table->change_callback_arg);
}
// Обновляем статистику
if (table->entries[found_idx].type == ROUTE_TYPE_LOCAL) {
table->stats.local_routes--;
} else if (table->entries[found_idx].type == ROUTE_TYPE_LEARNED) {
table->stats.learned_routes--;
}
// Сдвигаем все записи после удаляемой
for (size_t i = found_idx; i < table->count - 1; i++) {
table->entries[i] = table->entries[i + 1];
}
// Очищаем последнюю запись
memset(&table->entries[table->count - 1], 0, sizeof(struct ROUTE_ENTRY));
table->count--;
// Инвалидируем кеш
for (int i = 0; i < g_route_cache.count; i++) {
if (g_route_cache.entries[i]) {
route_array_destroy(g_route_cache.entries[i]);
g_route_cache.entries[i] = NULL;
}
}
g_route_cache.count = 0;
char ip_str[16];
ip_to_string(network, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Deleted route: %s/%d", ip_str, prefix_length);
return true;
return false;
}
/**
* @brief Выполняет поиск доступных маршрутов для заданного IP-адреса, отсортированных по quality
* при поиске ищет готовый результат в кеше или добавляет в кеш новый результат и возвращает запись из кеша
* @param table Указатель на таблицу маршрутизации
* @param dest_ip Целевой IP-адрес
* @return структура с найденными маршрутами
*/
struct ROUTE_ARRAY* route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
if (!table) return NULL;
table->stats.routes_lookup_hits++;
// Сначала проверяем кеш
struct ROUTE_ARRAY *cached = cache_lookup(dest_ip);
// Check cache first
struct ROUTE_ARRAY* cached = route_cache_find(dest_ip);
if (cached) {
return cached;
}
// Создаем новый результат
struct ROUTE_ARRAY *result = route_array_create(table->count);
if (!result) {
return NULL;
}
table->stats.routes_lookup_misses++;
// Create new array (initial capacity = table->count for worst case)
struct ROUTE_ARRAY* result = route_array_create(table->count);
if (!result) return NULL;
result->ip = dest_ip;
// Ищем все подходящие маршруты (longest prefix match)
// Find matching routes
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *entry = &table->entries[i];
// Проверяем что маршрут активен
if (!(entry->flags & ROUTE_FLAG_ACTIVE)) {
continue;
}
// Проверяем совпадение по подсети
uint32_t mask = (entry->prefix_length == 0) ? 0 :
(0xFFFFFFFFU << (32 - entry->prefix_length));
if ((dest_ip & mask) == (entry->network & mask)) {
// Добавляем в результат
if (!route_array_add(result, entry)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route array full, skipping additional routes");
break;
}
struct ROUTE_ENTRY* entry = &table->entries[i];
// Calculate netmask
uint32_t netmask = (uint32_t)(~0) << (32 - entry->prefix_length);
uint32_t entry_net = entry->network & netmask;
uint32_t dest_net = dest_ip & netmask;
if (entry_net == dest_net && (entry->flags & ROUTE_FLAG_ACTIVE)) {
route_array_add(result, entry);
}
}
if (result->routes == 0) {
// Нет маршрутов
table->stats.routes_lookup_misses++;
route_array_destroy(result);
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No route found for destination IP %s", ip_str);
return NULL;
}
// Обновляем quality для всех найденных маршрутов
for (uint32_t i = 0; i < result->routes; i++) {
route_update_quality(result->entries[i]);
}
// Сортируем по quality (лучший первый)
qsort(result->entries, result->routes, sizeof(struct ROUTE_ENTRY*),
compare_routes_by_quality);
// Сохраняем в кеш
cache_insert(dest_ip, result);
// Sort by quality
qsort(result->entries, result->routes, sizeof(struct ROUTE_ENTRY*), compare_routes_by_quality);
// Add to cache
route_cache_add(dest_ip, result);
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found %u routes for %s (best quality: %u)",
@ -563,8 +442,17 @@ struct ROUTE_ARRAY* route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_
return result;
}
/**
* @brief Добавляет диапазон подсетей для валидации (enhanced version)
* @param table таблица
* @param network сеть
* @param prefix_length префикс
* @param ranges указатель на массив (uint32_t[even]: network, odd: prefix)
* @param count указатель на счётчик (pairs * 2)
* @return true при успехе
*/
static bool enhanced_route_add_subnet_range(struct ROUTE_TABLE *table, uint32_t network,
uint8_t prefix_length, uint32_t **ranges, size_t *count) {
uint8_t prefix_length, uint32_t **ranges, size_t *count) {
if (!table || !ranges || !count) return false;
if (*count >= MAX_SUBNET_VALIDATION_RANGES - 2) return false;
@ -633,6 +521,10 @@ bool route_get_all_routes(const struct ROUTE_TABLE *table, uint32_t network, uin
return true;
}
/**
* @brief Печатает содержимое таблицы маршрутизации
* @param table Указатель на таблицу маршрутизации
*/
void route_table_print(const struct ROUTE_TABLE *table) {
if (!table) return;
@ -664,7 +556,7 @@ void route_table_print(const struct ROUTE_TABLE *table) {
entry->metrics.bandwidth_kbps,
entry->metrics.packet_loss_rate / 100.0,
entry->metrics.latency_ms,
entry->metrics.hop_count);
entry->hop_count);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s",
(entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "",
@ -676,3 +568,18 @@ void route_table_print(const struct ROUTE_TABLE *table) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No routes configured.");
}
}
/**
* @brief Преобразует тип маршрута в строку
* @param type Тип маршрута
* @return Строка с названием типа
*/
static const char* route_type_to_string(route_type_t type) {
switch (type) {
case ROUTE_TYPE_STATIC: return "STATIC";
case ROUTE_TYPE_DYNAMIC: return "DYNAMIC";
case ROUTE_TYPE_LOCAL: return "LOCAL";
case ROUTE_TYPE_LEARNED: return "LEARNED";
default: return "UNKNOWN";
}
}

39
src/route_lib.h

@ -29,6 +29,29 @@ typedef enum {
ROUTE_FLAG_LEARNED = (1 << 3) /**< Маршрут изучен */
} route_flags_t;
struct ROUTE_PEER_INFO {
uint64_t node_id; // ID узла назначения
uint32_t network; // Сетевой адрес (big-endian)
uint8_t prefix_length; // Длина префикса подсети
uint8_t hop_count; // Количество прыжков (инкриментирует получатель). 0 - локальный маршрут
uint16_t latency; // Задержка (x0.1 ms) - суммируется при распространении маршрута
} __attribute__((packed));
struct NODE_CONN_INFO {
uint64_t node_id; // ID узла назначения
uint32_t endpoint_ip; // IP для прямого подключения
uint16_t endpoint_port; // Порт для прямого подключения
uint8_t public_key[64]; // публичный ключ узла (для прямого подключения)
} __attribute__((packed));
struct NODE_CONNS_INFO {
uint8_t connlist_count; // число подключений к узлу
uint16_t ref_count; // счетчик ссылок с разных route_entry для освобождения
struct NODE_CONN_INFO conn_info[0]; // сами подключения
} __attribute__((packed));
/**
* @brief Расширенные метрики маршрута
*
@ -39,7 +62,7 @@ struct ROUTE_METRICS {
uint32_t bandwidth_kbps; /**< Пропускная способность в Кбит/с */
uint16_t packet_loss_rate; /**< Уровень потери пакетов (x0.1%) */
uint16_t latency_ms; /**< Задержка в миллисекундах */
uint8_t hop_count; /**< Количество прыжков */
// uint8_t hop_count; /**< Количество прыжков */
uint64_t last_updated; /**< Время последнего обновления (в микросекундах) */
};
@ -70,9 +93,11 @@ typedef void (*route_change_callback_fn)(struct ROUTE_TABLE* table,
* Структура представляет собой отдельную запись в таблице маршрутизации с детальной информацией о маршруте.
*/
struct ROUTE_ENTRY {
uint32_t network; /**< Сетевой адрес */
uint8_t prefix_length; /**< Длина префикса подсети */
struct ETCP_CONN* next_hop; /**< Указатель на подключение до следующего хопа */
struct ROUTE_PEER_INFO;// сюда перенесли часть полей:
// uint32_t network; /**< Сетевой адрес */ moved
// uint8_t prefix_length; /**< Длина префикса подсети */ moved
struct ETCP_CONN* next_hop; // Указатель на подключение до следующего хопа
struct NODE_CONNS_INFO* conn_list; // список прямых подключений к узлу. null если нет прямых подключений.
route_type_t type; /**< Тип маршрута */
uint8_t flags; /**< Флаги маршрута */
struct ROUTE_METRICS metrics; /**< Метрики маршрута */
@ -80,9 +105,9 @@ struct ROUTE_ENTRY {
uint64_t last_update; /**< Время последнего обновления (в микросекундах) */
// BGP поля - информация о конечном узле для прямого подключения
uint32_t endpoint_ip; /**< IP для прямого подключения (из BGP) */
uint16_t endpoint_port; /**< Порт для прямого подключения (из BGP) */
uint64_t destination_node_id; /**< ID узла назначения (из BGP) */
// uint32_t endpoint_ip; /**< IP для прямого подключения (из BGP) */
// uint16_t endpoint_port; /**< Порт для прямого подключения (из BGP) */
// uint64_t destination_node_id; /**< ID узла назначения (из BGP) */
};
/**

Loading…
Cancel
Save