в процессе доработки (tmp cpmmit)
This commit is contained in:
1 parent
7dd38d74c8
commit
bd33bf1d9f
4 files changed
+661
-598
No files matched your search
+376
-243
@@ -28,9 +28,18 @@
|
|||||||
// Forward declarations
|
// Forward declarations
|
||||||
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||||
const struct ROUTE_ENTRY* route);
|
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,
|
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
||||||
struct ROUTE_ENTRY* entry,
|
struct ROUTE_ENTRY* entry,
|
||||||
int action, void* arg);
|
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) {
|
static const char* route_type_to_str(route_type_t type) {
|
||||||
switch (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]);
|
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)
|
* @brief Рассылает маршрут всем соединениям (кроме exclude)
|
||||||
*/
|
*/
|
||||||
@@ -71,6 +93,176 @@ static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_
|
|||||||
net_buf, route->prefix_length, sent_count);
|
net_buf, route->prefix_length, sent_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Рассылает withdraw всем соединениям (кроме exclude)
|
||||||
|
*/
|
||||||
|
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
|
||||||
|
struct ETCP_CONN* exclude) {
|
||||||
|
if (!bgp) return;
|
||||||
|
|
||||||
|
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_withdraw(bgp, item->conn, node_id);
|
||||||
|
sent_count++;
|
||||||
|
}
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (!bgp || !conn || !route) return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_entry->dgram = (uint8_t*)pkt;
|
||||||
|
send_entry->len = sizeof(struct BGP_REROUTE_PACKET);
|
||||||
|
|
||||||
|
if (etcp_send(conn, send_entry) != 0) {
|
||||||
|
queue_entry_free(send_entry);
|
||||||
|
u_free(pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Отправляет withdraw конкретному соединению
|
||||||
|
*/
|
||||||
|
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn, uint64_t node_id) {
|
||||||
|
if (!bgp || !conn) return;
|
||||||
|
|
||||||
|
struct BGP_WITHDRAW_PACKET *pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET));
|
||||||
|
if (!pkt) return;
|
||||||
|
|
||||||
|
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
|
||||||
|
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
|
||||||
|
pkt->node_id = htobe64(node_id);
|
||||||
|
|
||||||
|
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 = sizeof(struct BGP_WITHDRAW_PACKET);
|
||||||
|
|
||||||
|
if (etcp_send(conn, send_entry) != 0) {
|
||||||
|
queue_entry_free(send_entry);
|
||||||
|
u_free(pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Отправляет withdraw для всех affected node_id при удалении conn
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Отправляет полную таблицу конкретному соединению
|
||||||
|
*/
|
||||||
|
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_TABLE* table = bgp->instance->rt;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < table->count; i++) {
|
||||||
|
route_bgp_send_route(bgp, conn, &table->entries[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sent full routing table (%zu entries) to connection %p",
|
||||||
|
table->count, (void*)conn);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Колбэк для приема роутинг-пакетов от ETCP
|
* @brief Колбэк для приема роутинг-пакетов от ETCP
|
||||||
*
|
*
|
||||||
@@ -79,7 +271,7 @@ static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_
|
|||||||
*/
|
*/
|
||||||
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 || !entry || !entry->dgram || entry->len < sizeof(struct ROUTE_BGP_PACKET)) {
|
if (!from_conn || !entry || !entry->dgram || entry->len < 2) {
|
||||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid BGP packet received");
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid BGP packet received");
|
||||||
if (entry) {
|
if (entry) {
|
||||||
queue_dgram_free(entry);
|
queue_dgram_free(entry);
|
||||||
@@ -96,7 +288,7 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
|
struct BGP_ENTRY_PACKET* pkt = (struct BGP_ENTRY_PACKET*)entry->dgram;
|
||||||
|
|
||||||
// Проверяем команду
|
// Проверяем команду
|
||||||
if (pkt->cmd != ETCP_ID_ROUTE_ENTRY) {
|
if (pkt->cmd != ETCP_ID_ROUTE_ENTRY) {
|
||||||
@@ -107,78 +299,193 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
|
|||||||
}
|
}
|
||||||
|
|
||||||
char ip_buf[16];
|
char ip_buf[16];
|
||||||
uint32_t network = ntohl(pkt->network);
|
uint32_t network;
|
||||||
format_ip(network, ip_buf);
|
uint8_t prefix_length;
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet: subcmd=%d, network=%s/%d, node_id=%016llx",
|
uint64_t node_id;
|
||||||
pkt->subcmd, ip_buf, pkt->prefix_length & 0x3F, (unsigned long long)pkt->node_id);
|
|
||||||
|
|
||||||
switch (pkt->subcmd) {
|
switch (pkt->subcmd) {
|
||||||
case ROUTE_SUBCMD_ENTRY: {
|
case ROUTE_SUBCMD_ENTRY:
|
||||||
// Создаем запись маршрута
|
case ROUTE_SUBCMD_ENTRY_REROUTE: {
|
||||||
struct ROUTE_ENTRY route;
|
uint64_t old_node_id = 0;
|
||||||
memset(&route, 0, sizeof(route));
|
struct ROUTE_PEER_INFO *peer_info;
|
||||||
|
if (pkt->subcmd == ROUTE_SUBCMD_ENTRY_REROUTE) {
|
||||||
route.network = ntohl(pkt->network);
|
if (entry->len < sizeof(BGP_REROUTE_PACKET)) {
|
||||||
route.prefix_length = pkt->prefix_length & 0x3F; // Младшие 6 бит - длина префикса
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid REROUTE packet size");
|
||||||
route.next_hop = from_conn;
|
queue_dgram_free(entry);
|
||||||
route.type = ROUTE_TYPE_LEARNED;
|
queue_entry_free(entry);
|
||||||
route.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_LEARNED;
|
return;
|
||||||
|
}
|
||||||
// Метрики
|
BGP_REROUTE_PACKET *reroute_pkt = (BGP_REROUTE_PACKET*)pkt;
|
||||||
route.metrics.hop_count = pkt->hop_count + 1; // Инкрементируем
|
old_node_id = be64toh(reroute_pkt->old_node_id);
|
||||||
route.metrics.latency_ms = pkt->latency / 10; // x0.1 ms -> ms
|
peer_info = &reroute_pkt->peer_info;
|
||||||
route.metrics.bandwidth_kbps = ntohl(pkt->bandwidth_kbps);
|
} else {
|
||||||
route.metrics.last_updated = 0; // Будет установлено при вставке
|
if (entry->len < sizeof(BGP_ENTRY_PACKET)) {
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Invalid ENTRY packet size");
|
||||||
// BGP-специфичные поля
|
queue_dgram_free(entry);
|
||||||
route.endpoint_ip = ntohl(pkt->endpoint_ip);
|
queue_entry_free(entry);
|
||||||
route.endpoint_port = ntohs(pkt->endpoint_port);
|
return;
|
||||||
route.destination_node_id = pkt->node_id;
|
}
|
||||||
|
peer_info = &pkt->peer_info;
|
||||||
// Проверяем флаги
|
|
||||||
if (pkt->prefix_length & ROUTE_PREFIX_F_LOCAL) {
|
|
||||||
// Локальный маршрут - не пересылаем
|
|
||||||
route.flags |= ROUTE_FLAG_ADVERTISED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вставляем в таблицу
|
node_id = be64toh(peer_info->node_id);
|
||||||
char net_buf[16];
|
network = ntohl(peer_info->network);
|
||||||
format_ip(route.network, net_buf);
|
prefix_length = peer_info->prefix_length;
|
||||||
size_t old_count = instance->rt->count;
|
uint8_t hop_count = peer_info->hop_count;
|
||||||
if (route_table_insert(instance->rt, &route)) {
|
uint16_t latency = ntohs(peer_info->latency);
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added learned route: %s/%d via node %016llx (hops=%d)",
|
|
||||||
net_buf, route.prefix_length,
|
format_ip(network, ip_buf);
|
||||||
(unsigned long long)route.destination_node_id,
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP %s: network=%s/%d, node_id=%016llx, hop=%d, lat=%u",
|
||||||
route.metrics.hop_count);
|
(pkt->subcmd == ROUTE_SUBCMD_ENTRY) ? "ENTRY" : "REROUTE",
|
||||||
|
ip_buf, prefix_length, (unsigned long long)node_id, hop_count, latency);
|
||||||
struct ROUTE_BGP* bgp = instance->bgp;
|
|
||||||
if (instance->rt->count > old_count && !(route.flags & ROUTE_FLAG_ADVERTISED)) {
|
struct ROUTE_ENTRY new_route;
|
||||||
// Новый маршрут - распространяем всем кроме источника
|
memset(&new_route, 0, sizeof(new_route));
|
||||||
route_bgp_broadcast_route(bgp, &route, from_conn);
|
|
||||||
// Устанавливаем флаг, чтобы не повторять
|
new_route.node_id = node_id;
|
||||||
route.flags |= ROUTE_FLAG_ADVERTISED;
|
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 {
|
} else {
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert learned route: %s/%d",
|
// Propagate withdraw
|
||||||
net_buf, route.prefix_length);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd);
|
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd);
|
||||||
break;
|
break;
|
||||||
@@ -189,173 +496,7 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Отправляет маршрут конкретному соединению
|
* @brief Callback на изменение маршрута (broadcast при insert/update)
|
||||||
*/
|
|
||||||
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
|
||||||
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");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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_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->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;
|
|
||||||
|
|
||||||
// Отправляем через 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Отправляет withdraw для конкретной подсети одному соединению
|
|
||||||
*/
|
|
||||||
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
|
||||||
uint32_t network, uint8_t prefix_length) {
|
|
||||||
if (!bgp || !conn) return;
|
|
||||||
|
|
||||||
struct ll_entry* entry = queue_entry_new_from_pool(bgp->instance->pkt_pool);
|
|
||||||
if (!entry) return;
|
|
||||||
|
|
||||||
entry->dgram = u_malloc(BGP_PACKET_SIZE);
|
|
||||||
if (!entry->dgram) {
|
|
||||||
queue_entry_free(entry);
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (etcp_send(conn, entry) != 0) {
|
|
||||||
queue_dgram_free(entry);
|
|
||||||
queue_entry_free(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Рассылает withdraw всем соединениям (кроме exclude)
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Отправляет withdraw для всех маршрутов через закрытое соединение
|
|
||||||
*/
|
|
||||||
static void route_bgp_send_withdraw_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
|
|
||||||
if (!bgp || !bgp->instance->rt) return;
|
|
||||||
|
|
||||||
struct ROUTE_ENTRY* routes = NULL;
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
// Получаем все маршруты (чтобы не итерать таблицу во время отправки)
|
|
||||||
for (size_t i = 0; i < bgp->instance->rt->count; i++) {
|
|
||||||
if (bgp->instance->rt->entries[i].next_hop == conn) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Рассылаем 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending full routing table to conn %p", (void*)conn);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Callback на изменение таблицы маршрутизации
|
|
||||||
*/
|
*/
|
||||||
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
||||||
struct ROUTE_ENTRY* entry,
|
struct ROUTE_ENTRY* entry,
|
||||||
@@ -364,22 +505,14 @@ static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
|||||||
if (!bgp || !entry) return;
|
if (!bgp || !entry) return;
|
||||||
|
|
||||||
if (action == 0 || action == 1) { // insert or update
|
if (action == 0 || action == 1) { // insert or update
|
||||||
// Анонсируем только локальные и статические маршруты
|
route_bgp_broadcast_route(bgp, entry, NULL);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
// for delete, handled in remove_conn/withdraw
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance)
|
||||||
* @brief Инициализация BGP модуля
|
{
|
||||||
*/
|
|
||||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot init BGP: no instance");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -21,9 +21,7 @@ struct ETCP_CONN;
|
|||||||
/**
|
/**
|
||||||
* @brief Пакет маршрутной информации (бинарный формат)
|
* @brief Пакет маршрутной информации (бинарный формат)
|
||||||
*/
|
*/
|
||||||
struct PEER_INFO_ENTRY {
|
// struct PEER_INFO_ENTRY -> struct ROUTE_PEER_INFO;
|
||||||
struct ROUTE_PEER_INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BGP_ENTRY_PACKET {
|
struct BGP_ENTRY_PACKET {
|
||||||
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
||||||
@@ -34,7 +32,7 @@ struct BGP_ENTRY_PACKET {
|
|||||||
struct BGP_NODEINFO_PACKET {
|
struct BGP_NODEINFO_PACKET {
|
||||||
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
||||||
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO (4)
|
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO (4)
|
||||||
struct ROUTE_NODE_INFO node_info;
|
struct NODE_CONN_INFO conn_info;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+251
-344
@@ -118,9 +118,11 @@ bool route_update_quality(struct ROUTE_ENTRY *entry) {
|
|||||||
|
|
||||||
// Простая формула: latency * 2 + loss * 10 + hops * 50
|
// Простая формула: 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.packet_loss_rate * 10 +
|
||||||
entry->metrics.hop_count * 50;
|
entry->hop_count * 50;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -148,15 +150,11 @@ static int compare_routes_by_quality(const void *a, const void *b) {
|
|||||||
/**
|
/**
|
||||||
* @brief Ищет запись в кеше по IP
|
* @brief Ищет запись в кеше по IP
|
||||||
*/
|
*/
|
||||||
static struct ROUTE_ARRAY* cache_lookup(uint32_t dest_ip) {
|
static struct ROUTE_ARRAY* route_cache_find(uint32_t ip) {
|
||||||
uint64_t current_time = (uint64_t)time(NULL) * 1000000;
|
|
||||||
|
|
||||||
for (int i = 0; i < g_route_cache.count; i++) {
|
for (int i = 0; i < g_route_cache.count; i++) {
|
||||||
if (g_route_cache.keys[i] == dest_ip && g_route_cache.entries[i]) {
|
if (g_route_cache.keys[i] == ip) {
|
||||||
g_route_cache.access_time[i] = current_time;
|
// Update access time
|
||||||
char ip_str[16];
|
g_route_cache.access_time[i] = get_time_tb();
|
||||||
ip_to_string(dest_ip, ip_str);
|
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Cache hit for IP %s", ip_str);
|
|
||||||
return g_route_cache.entries[i];
|
return g_route_cache.entries[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,95 +162,30 @@ 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) {
|
static void route_cache_add(uint32_t ip, struct ROUTE_ARRAY* array) {
|
||||||
uint64_t current_time = (uint64_t)time(NULL) * 1000000;
|
|
||||||
int insert_idx = -1;
|
|
||||||
|
|
||||||
// Ищем пустое место или самую старую запись
|
|
||||||
if (g_route_cache.count < ROUTE_CACHE_SIZE) {
|
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 {
|
} else {
|
||||||
// LRU: находим запись с самым старым access_time
|
// Find least recently used
|
||||||
uint64_t oldest_time = g_route_cache.access_time[0];
|
int lru_idx = 0;
|
||||||
insert_idx = 0;
|
uint64_t min_time = g_route_cache.access_time[0];
|
||||||
for (int i = 1; i < ROUTE_CACHE_SIZE; i++) {
|
for (int i = 1; i < ROUTE_CACHE_SIZE; i++) {
|
||||||
if (g_route_cache.access_time[i] < oldest_time) {
|
if (g_route_cache.access_time[i] < min_time) {
|
||||||
oldest_time = g_route_cache.access_time[i];
|
min_time = g_route_cache.access_time[i];
|
||||||
insert_idx = i;
|
lru_idx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Освобождаем старую запись
|
// Replace LRU
|
||||||
if (g_route_cache.entries[insert_idx]) {
|
route_array_destroy(g_route_cache.entries[lru_idx]);
|
||||||
route_array_destroy(g_route_cache.entries[insert_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 Строковое представление типа
|
|
||||||
*/
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
table->capacity = INITIAL_ROUTE_CAPACITY;
|
|
||||||
table->entries = u_calloc(table->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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
void route_table_destroy(struct ROUTE_TABLE *table) {
|
|
||||||
if (!table) return;
|
|
||||||
|
|
||||||
// Free route entries
|
|
||||||
u_free(table->entries);
|
|
||||||
|
|
||||||
// Free validation ranges
|
|
||||||
u_free(table->dynamic_subnets);
|
|
||||||
u_free(table->local_subnets);
|
|
||||||
|
|
||||||
u_free(table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,301 +193,247 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
|
|||||||
*/
|
*/
|
||||||
void route_cache_clear(void) {
|
void route_cache_clear(void) {
|
||||||
for (int i = 0; i < g_route_cache.count; i++) {
|
for (int i = 0; i < g_route_cache.count; i++) {
|
||||||
if (g_route_cache.entries[i]) {
|
route_array_destroy(g_route_cache.entries[i]);
|
||||||
route_array_destroy(g_route_cache.entries[i]);
|
g_route_cache.entries[i] = NULL;
|
||||||
g_route_cache.entries[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
g_route_cache.count = 0;
|
g_route_cache.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Валидирует маршрут на соответствие разрешенным подсетям
|
* @brief Создает новую таблицу маршрутизации
|
||||||
|
* @return Указатель на созданную таблицу или NULL в случае ошибки
|
||||||
*/
|
*/
|
||||||
static bool route_validate_route(struct ROUTE_TABLE *table, uint32_t network,
|
struct ROUTE_TABLE *route_table_create(void) {
|
||||||
uint8_t prefix_length, route_type_t route_type) {
|
struct ROUTE_TABLE *table = u_calloc(1, sizeof(struct ROUTE_TABLE));
|
||||||
if (!table) return false;
|
if (!table) {
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate table");
|
||||||
uint32_t *validation_ranges = NULL;
|
return 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
|
table->entries = u_calloc(INITIAL_ROUTE_CAPACITY, sizeof(struct ROUTE_ENTRY));
|
||||||
|
if (!table->entries) {
|
||||||
// Check if network falls within any validation range
|
u_free(table);
|
||||||
for (size_t i = 0; i < range_count; i += 2) {
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate entries");
|
||||||
uint32_t range_network = validation_ranges[i];
|
return NULL;
|
||||||
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;
|
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 entries
|
||||||
|
u_free(table->entries);
|
||||||
|
|
||||||
|
// Free validation ranges
|
||||||
|
if (table->dynamic_subnets) u_free(table->dynamic_subnets);
|
||||||
|
if (table->local_subnets) u_free(table->local_subnets);
|
||||||
|
|
||||||
|
u_free(table);
|
||||||
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Вставляет новую запись в таблицу маршрутизации
|
||||||
|
* @param table Указатель на таблицу маршрутизации
|
||||||
|
* @param entry Указатель на вставляемую запись маршрута
|
||||||
|
* @return true если вставка успешна, false иначе
|
||||||
|
*/
|
||||||
bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *entry) {
|
bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *entry) {
|
||||||
if (!table || !entry) return false;
|
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) {
|
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));
|
struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_capacity * sizeof(struct ROUTE_ENTRY));
|
||||||
if (!new_entries) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero new memory
|
|
||||||
memset(new_entries + table->capacity, 0, (new_capacity - table->capacity) * sizeof(struct ROUTE_ENTRY));
|
|
||||||
table->entries = new_entries;
|
table->entries = new_entries;
|
||||||
table->capacity = new_capacity;
|
table->capacity = new_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, не существует ли уже такой маршрут (по network + prefix_length + next_hop)
|
// Copy entry
|
||||||
for (size_t i = 0; i < table->count; i++) {
|
table->entries[table->count] = *entry;
|
||||||
if (table->entries[i].network == entry->network &&
|
|
||||||
table->entries[i].prefix_length == entry->prefix_length &&
|
// Update timestamps
|
||||||
table->entries[i].next_hop == entry->next_hop) {
|
uint64_t now = get_time_tb();
|
||||||
// Обновляем существующий маршрут
|
table->entries[table->count].created_time = now;
|
||||||
table->entries[i] = *entry;
|
table->entries[table->count].last_update = now;
|
||||||
table->entries[i].last_update = (uint64_t)time(NULL) * 1000000;
|
table->entries[table->count].metrics.last_updated = now;
|
||||||
|
|
||||||
// Вызываем callback (action=1 - update)
|
// Update quality
|
||||||
|
route_update_quality(&table->entries[table->count]);
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
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(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 i = 0;
|
||||||
|
while (i < table->count) {
|
||||||
|
if (table->entries[i].next_hop == conn) {
|
||||||
|
// Call change callback before removal
|
||||||
if (table->change_callback) {
|
if (table->change_callback) {
|
||||||
table->change_callback(table, &table->entries[i], 1, table->change_callback_arg);
|
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 entries
|
||||||
|
if (i < table->count - 1) {
|
||||||
|
memmove(&table->entries[i], &table->entries[i + 1], (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
|
||||||
|
}
|
||||||
|
table->count--;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (!table) return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < table->count; i++) {
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
|
||||||
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--;
|
|
||||||
}
|
|
||||||
removed++;
|
|
||||||
} else {
|
|
||||||
// Сохраняем запись
|
|
||||||
if (write_idx != i) {
|
|
||||||
table->entries[write_idx] = table->entries[i];
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_route_cache.count = 0;
|
|
||||||
|
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Removed %zu routes for connection %p", removed, (void*)conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool route_table_delete_entry(struct ROUTE_TABLE *table, uint32_t network,
|
|
||||||
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 (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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Выполняет поиск доступных маршрутов для заданного IP-адреса, отсортированных по quality
|
||||||
|
* при поиске ищет готовый результат в кеше или добавляет в кеш новый результат и возвращает запись из кеша
|
||||||
|
* @param table Указатель на таблицу маршрутизации
|
||||||
|
* @param dest_ip Целевой IP-адрес
|
||||||
|
* @return структура с найденными маршрутами
|
||||||
|
*/
|
||||||
struct ROUTE_ARRAY* route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
|
struct ROUTE_ARRAY* route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
|
||||||
if (!table) return NULL;
|
if (!table) return NULL;
|
||||||
|
|
||||||
table->stats.routes_lookup_hits++;
|
table->stats.routes_lookup_hits++;
|
||||||
|
|
||||||
// Сначала проверяем кеш
|
// Check cache first
|
||||||
struct ROUTE_ARRAY *cached = cache_lookup(dest_ip);
|
struct ROUTE_ARRAY* cached = route_cache_find(dest_ip);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем новый результат
|
table->stats.routes_lookup_misses++;
|
||||||
struct ROUTE_ARRAY *result = route_array_create(table->count);
|
|
||||||
if (!result) {
|
// Create new array (initial capacity = table->count for worst case)
|
||||||
return NULL;
|
struct ROUTE_ARRAY* result = route_array_create(table->count);
|
||||||
}
|
if (!result) return NULL;
|
||||||
|
|
||||||
result->ip = dest_ip;
|
result->ip = dest_ip;
|
||||||
|
|
||||||
// Ищем все подходящие маршруты (longest prefix match)
|
// Find matching routes
|
||||||
for (size_t i = 0; i < table->count; i++) {
|
for (size_t i = 0; i < table->count; i++) {
|
||||||
struct ROUTE_ENTRY *entry = &table->entries[i];
|
struct ROUTE_ENTRY* entry = &table->entries[i];
|
||||||
|
|
||||||
// Проверяем что маршрут активен
|
// Calculate netmask
|
||||||
if (!(entry->flags & ROUTE_FLAG_ACTIVE)) {
|
uint32_t netmask = (uint32_t)(~0) << (32 - entry->prefix_length);
|
||||||
continue;
|
uint32_t entry_net = entry->network & netmask;
|
||||||
}
|
uint32_t dest_net = dest_ip & netmask;
|
||||||
|
|
||||||
// Проверяем совпадение по подсети
|
if (entry_net == dest_net && (entry->flags & ROUTE_FLAG_ACTIVE)) {
|
||||||
uint32_t mask = (entry->prefix_length == 0) ? 0 :
|
route_array_add(result, entry);
|
||||||
(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result->routes == 0) {
|
if (result->routes == 0) {
|
||||||
// Нет маршрутов
|
|
||||||
table->stats.routes_lookup_misses++;
|
|
||||||
route_array_destroy(result);
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем quality для всех найденных маршрутов
|
// Sort by quality
|
||||||
for (uint32_t i = 0; i < result->routes; i++) {
|
qsort(result->entries, result->routes, sizeof(struct ROUTE_ENTRY*), compare_routes_by_quality);
|
||||||
route_update_quality(result->entries[i]);
|
|
||||||
}
|
// Add to cache
|
||||||
|
route_cache_add(dest_ip, result);
|
||||||
// Сортируем по quality (лучший первый)
|
|
||||||
qsort(result->entries, result->routes, sizeof(struct ROUTE_ENTRY*),
|
|
||||||
compare_routes_by_quality);
|
|
||||||
|
|
||||||
// Сохраняем в кеш
|
|
||||||
cache_insert(dest_ip, result);
|
|
||||||
|
|
||||||
char ip_str[16];
|
char ip_str[16];
|
||||||
ip_to_string(dest_ip, ip_str);
|
ip_to_string(dest_ip, ip_str);
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found %u routes for %s (best quality: %u)",
|
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;
|
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,
|
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 (!table || !ranges || !count) return false;
|
||||||
|
|
||||||
if (*count >= MAX_SUBNET_VALIDATION_RANGES - 2) 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Печатает содержимое таблицы маршрутизации
|
||||||
|
* @param table Указатель на таблицу маршрутизации
|
||||||
|
*/
|
||||||
void route_table_print(const struct ROUTE_TABLE *table) {
|
void route_table_print(const struct ROUTE_TABLE *table) {
|
||||||
if (!table) return;
|
if (!table) return;
|
||||||
|
|
||||||
@@ -664,7 +556,7 @@ void route_table_print(const struct ROUTE_TABLE *table) {
|
|||||||
entry->metrics.bandwidth_kbps,
|
entry->metrics.bandwidth_kbps,
|
||||||
entry->metrics.packet_loss_rate / 100.0,
|
entry->metrics.packet_loss_rate / 100.0,
|
||||||
entry->metrics.latency_ms,
|
entry->metrics.latency_ms,
|
||||||
entry->metrics.hop_count);
|
entry->hop_count);
|
||||||
|
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s",
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s",
|
||||||
(entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "",
|
(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.");
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-7
@@ -29,6 +29,29 @@ typedef enum {
|
|||||||
ROUTE_FLAG_LEARNED = (1 << 3) /**< Маршрут изучен */
|
ROUTE_FLAG_LEARNED = (1 << 3) /**< Маршрут изучен */
|
||||||
} route_flags_t;
|
} 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 Расширенные метрики маршрута
|
* @brief Расширенные метрики маршрута
|
||||||
*
|
*
|
||||||
@@ -39,7 +62,7 @@ struct ROUTE_METRICS {
|
|||||||
uint32_t bandwidth_kbps; /**< Пропускная способность в Кбит/с */
|
uint32_t bandwidth_kbps; /**< Пропускная способность в Кбит/с */
|
||||||
uint16_t packet_loss_rate; /**< Уровень потери пакетов (x0.1%) */
|
uint16_t packet_loss_rate; /**< Уровень потери пакетов (x0.1%) */
|
||||||
uint16_t latency_ms; /**< Задержка в миллисекундах */
|
uint16_t latency_ms; /**< Задержка в миллисекундах */
|
||||||
uint8_t hop_count; /**< Количество прыжков */
|
// uint8_t hop_count; /**< Количество прыжков */
|
||||||
uint64_t last_updated; /**< Время последнего обновления (в микросекундах) */
|
uint64_t last_updated; /**< Время последнего обновления (в микросекундах) */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,9 +93,11 @@ typedef void (*route_change_callback_fn)(struct ROUTE_TABLE* table,
|
|||||||
* Структура представляет собой отдельную запись в таблице маршрутизации с детальной информацией о маршруте.
|
* Структура представляет собой отдельную запись в таблице маршрутизации с детальной информацией о маршруте.
|
||||||
*/
|
*/
|
||||||
struct ROUTE_ENTRY {
|
struct ROUTE_ENTRY {
|
||||||
uint32_t network; /**< Сетевой адрес */
|
struct ROUTE_PEER_INFO;// сюда перенесли часть полей:
|
||||||
uint8_t prefix_length; /**< Длина префикса подсети */
|
// uint32_t network; /**< Сетевой адрес */ moved
|
||||||
struct ETCP_CONN* next_hop; /**< Указатель на подключение до следующего хопа */
|
// uint8_t prefix_length; /**< Длина префикса подсети */ moved
|
||||||
|
struct ETCP_CONN* next_hop; // Указатель на подключение до следующего хопа
|
||||||
|
struct NODE_CONNS_INFO* conn_list; // список прямых подключений к узлу. null если нет прямых подключений.
|
||||||
route_type_t type; /**< Тип маршрута */
|
route_type_t type; /**< Тип маршрута */
|
||||||
uint8_t flags; /**< Флаги маршрута */
|
uint8_t flags; /**< Флаги маршрута */
|
||||||
struct ROUTE_METRICS metrics; /**< Метрики маршрута */
|
struct ROUTE_METRICS metrics; /**< Метрики маршрута */
|
||||||
@@ -80,9 +105,9 @@ struct ROUTE_ENTRY {
|
|||||||
uint64_t last_update; /**< Время последнего обновления (в микросекундах) */
|
uint64_t last_update; /**< Время последнего обновления (в микросекундах) */
|
||||||
|
|
||||||
// BGP поля - информация о конечном узле для прямого подключения
|
// BGP поля - информация о конечном узле для прямого подключения
|
||||||
uint32_t endpoint_ip; /**< IP для прямого подключения (из BGP) */
|
// uint32_t endpoint_ip; /**< IP для прямого подключения (из BGP) */
|
||||||
uint16_t endpoint_port; /**< Порт для прямого подключения (из BGP) */
|
// uint16_t endpoint_port; /**< Порт для прямого подключения (из BGP) */
|
||||||
uint64_t destination_node_id; /**< ID узла назначения (из BGP) */
|
// uint64_t destination_node_id; /**< ID узла назначения (из BGP) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user