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