в процессе доработки роутинга (не рабочий комит)
This commit is contained in:
1 parent
cad523f1d0
commit
7dd38d74c8
3 files changed
+229
-272
No files matched your search
+187
-255
@@ -34,8 +34,6 @@ static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
||||
|
||||
static const char* route_type_to_str(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";
|
||||
@@ -48,6 +46,31 @@ static void format_ip(uint32_t ip, char* buffer) {
|
||||
snprintf(buffer, 16, "%u.%u.%u.%u", b[3], b[2], b[1], b[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Рассылает маршрут всем соединениям (кроме exclude)
|
||||
*/
|
||||
static void route_bgp_broadcast_route(struct ROUTE_BGP* bgp, const struct ROUTE_ENTRY* route,
|
||||
struct ETCP_CONN* exclude) {
|
||||
if (!bgp || !route) 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_route(bgp, item->conn, route);
|
||||
sent_count++;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
char net_buf[16];
|
||||
format_ip(route->network, net_buf);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Broadcasted route %s/%d to %d connections",
|
||||
net_buf, route->prefix_length, sent_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Колбэк для приема роутинг-пакетов от ETCP
|
||||
*
|
||||
@@ -121,46 +144,41 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
|
||||
// Вставляем в таблицу
|
||||
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 route: %s/%d",
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert learned route: %s/%d",
|
||||
net_buf, route.prefix_length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ROUTE_SUBCMD_WITHDRAW: {
|
||||
// Удаление маршрута (withdrawal)
|
||||
uint32_t network = ntohl(pkt->network);
|
||||
uint8_t prefix_length = pkt->prefix_length & 0x3F;
|
||||
char w_net_buf[16];
|
||||
format_ip(network, w_net_buf);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing route withdrawal: %s/%d from conn %p",
|
||||
w_net_buf, prefix_length, (void*)from_conn);
|
||||
|
||||
if (route_table_delete_entry(instance->rt, network, prefix_length, from_conn)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route withdrawn successfully: %s/%d",
|
||||
w_net_buf, prefix_length);
|
||||
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, "Route to withdraw not found: %s/%d",
|
||||
w_net_buf, prefix_length);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Withdraw failed: route %s/%d not found",
|
||||
ip_buf, prefix_length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ROUTE_SUBCMD_REQUEST: {
|
||||
// Запрос полной таблицы - отправляем всё
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Full table request received from connection");
|
||||
// Получаем bgp из instance (нужно добавить поле в instance)
|
||||
// Пока просто игнорируем - таблица отправляется при new_conn
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Unknown BGP subcommand: %d", pkt->subcmd);
|
||||
break;
|
||||
@@ -171,281 +189,195 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправить withdraw (отзыв маршрута) через ETCP
|
||||
* @brief Отправляет маршрут конкретному соединению
|
||||
*/
|
||||
static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||
const struct ROUTE_ENTRY* route)
|
||||
{
|
||||
if (!bgp || !conn || !route) {
|
||||
return;
|
||||
}
|
||||
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||
const struct ROUTE_ENTRY* route) {
|
||||
if (!bgp || !conn || !route) return;
|
||||
|
||||
// Создаем ETCP пакет
|
||||
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct ROUTE_BGP_PACKET));
|
||||
// Создаем элемент для отправки
|
||||
struct ll_entry* entry = queue_entry_new_from_pool(bgp->instance->pkt_pool);
|
||||
if (!entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate withdraw packet");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate entry for BGP send");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
|
||||
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
|
||||
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
|
||||
pkt->hop_count = 0;
|
||||
pkt->prefix_length = route->prefix_length & 0x3F;
|
||||
pkt->latency = 0;
|
||||
pkt->endpoint_port = 0;
|
||||
pkt->endpoint_ip = 0;
|
||||
pkt->network = htonl(route->network);
|
||||
pkt->bandwidth_kbps = 0;
|
||||
pkt->node_id = route->destination_node_id;
|
||||
|
||||
entry->len = sizeof(struct ROUTE_BGP_PACKET);
|
||||
|
||||
char wd_net_buf[16];
|
||||
format_ip(route->network, wd_net_buf);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending withdraw: %s/%d to conn %p",
|
||||
wd_net_buf, route->prefix_length, (void*)conn);
|
||||
|
||||
// Отправляем через ETCP
|
||||
if (etcp_send(conn, entry) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to send withdraw via ETCP");
|
||||
queue_dgram_free(entry);
|
||||
// Выделяем буфер для пакета
|
||||
entry->dgram = u_malloc(BGP_PACKET_SIZE);
|
||||
if (!entry->dgram) {
|
||||
queue_entry_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправить withdraw для всех маршрутов через указанное соединение
|
||||
*/
|
||||
static void route_bgp_send_withdraw_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn)
|
||||
{
|
||||
if (!bgp || !conn || !bgp->instance->rt) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate dgram for BGP send");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ROUTE_TABLE* rt = bgp->instance->rt;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending withdraw for routes via conn %p to all peers", (void*)conn);
|
||||
|
||||
// Находим все маршруты через это соединение и отправляем withdraw
|
||||
for (size_t i = 0; i < rt->count; i++) {
|
||||
struct ROUTE_ENTRY* entry = &rt->entries[i];
|
||||
if (entry->next_hop == conn && entry->type == ROUTE_TYPE_LEARNED) {
|
||||
// Отправляем withdraw всем соединениям в senders_list
|
||||
struct ll_entry* item_entry = bgp->senders_list->head;
|
||||
while (item_entry) {
|
||||
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)item_entry->data;
|
||||
if (item->conn != conn) { // Не отправляем тому, кто прислал
|
||||
route_bgp_send_withdraw(bgp, item->conn, entry);
|
||||
}
|
||||
item_entry = item_entry->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Колбэк при изменении таблицы маршрутизации
|
||||
*
|
||||
* Рассылает обновления всем соединениям в senders_list
|
||||
*/
|
||||
static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
|
||||
struct ROUTE_ENTRY* entry,
|
||||
int action, void* arg)
|
||||
{
|
||||
struct ROUTE_BGP* bgp = (struct ROUTE_BGP*)arg;
|
||||
if (!bgp || !entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Не рассылаем локальные маршруты помеченные ROUTE_PREFIX_F_LOCAL
|
||||
if (entry->type == ROUTE_TYPE_LOCAL && (entry->prefix_length & ROUTE_PREFIX_F_LOCAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Не рассылаем маршруты через которые мы их получили (чтобы избежать петель)
|
||||
// Это делается в цикле ниже
|
||||
|
||||
char ch_net_buf[16];
|
||||
format_ip(entry->network, ch_net_buf);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Route change: action=%d, network=%s/%d, type=%d",
|
||||
action, ch_net_buf, entry->prefix_length, entry->type);
|
||||
|
||||
// Перебираем все соединения в senders_list
|
||||
struct ll_entry* item_entry = bgp->senders_list->head;
|
||||
while (item_entry) {
|
||||
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)item_entry->data;
|
||||
|
||||
// Не отправляем тому, от кого пришел маршрут
|
||||
if (entry->type == ROUTE_TYPE_LEARNED && entry->next_hop == item->conn) {
|
||||
item_entry = item_entry->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action == 2) { // delete - отправить withdraw
|
||||
route_bgp_send_withdraw(bgp, item->conn, entry);
|
||||
} else { // insert (0) или update (1) - отправить маршрут
|
||||
route_bgp_send_route(bgp, item->conn, entry);
|
||||
}
|
||||
|
||||
item_entry = item_entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправить один маршрут через ETCP (вспомогательная функция для отправки одного пакета)
|
||||
*/
|
||||
static int route_bgp_send_route_single(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||
const struct ROUTE_ENTRY* route,
|
||||
uint32_t endpoint_ip, uint16_t endpoint_port)
|
||||
{
|
||||
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct ROUTE_BGP_PACKET));
|
||||
if (!entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to allocate packet");
|
||||
return -1;
|
||||
}
|
||||
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 (endpoint_ip && endpoint_port) {
|
||||
pkt->prefix_length |= ROUTE_PREFIX_F_DIRECT;
|
||||
}
|
||||
// Если это локальный маршрут - отмечаем (не будет пересылаться дальше)
|
||||
if (route->type == ROUTE_TYPE_LOCAL) {
|
||||
pkt->prefix_length |= ROUTE_PREFIX_F_LOCAL;
|
||||
}
|
||||
|
||||
pkt->latency = htons(route->metrics.latency_ms * 10); // ms -> x0.1 ms
|
||||
pkt->endpoint_port = endpoint_port;
|
||||
pkt->endpoint_ip = endpoint_ip;
|
||||
pkt->network = htonl(route->network);
|
||||
pkt->latency = route->metrics.latency_ms * 10; // ms -> x0.1 ms
|
||||
pkt->bandwidth_kbps = htonl(route->metrics.bandwidth_kbps);
|
||||
pkt->node_id = bgp->instance->node_id; // Наш node_id
|
||||
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;
|
||||
|
||||
entry->len = sizeof(struct ROUTE_BGP_PACKET);
|
||||
|
||||
char sr_net_buf[16];
|
||||
char sr_ep_buf[16];
|
||||
format_ip(route->network, sr_net_buf);
|
||||
format_ip(ntohl(endpoint_ip), sr_ep_buf);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending route: %s/%d type=%s flags=0x%x "
|
||||
"metrics{hops=%u lat=%u kbps=%u loss=%u metric=%u} "
|
||||
"endpoint=%s:%u node_id=%016llx",
|
||||
sr_net_buf, route->prefix_length,
|
||||
route_type_to_str(route->type), route->flags,
|
||||
route->metrics.hop_count, route->metrics.latency_ms,
|
||||
route->metrics.bandwidth_kbps, route->metrics.packet_loss_rate,
|
||||
route->metrics.metric,
|
||||
sr_ep_buf, ntohs(endpoint_port),
|
||||
(unsigned long long)route->destination_node_id);
|
||||
|
||||
// Отправляем через ETCP
|
||||
if (etcp_send(conn, entry) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to send route via ETCP");
|
||||
queue_dgram_free(entry);
|
||||
queue_entry_free(entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправить один маршрут через ETCP
|
||||
*/
|
||||
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
|
||||
const struct ROUTE_ENTRY* route)
|
||||
{
|
||||
if (!bgp || !conn || !route) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Пропускаем неактивные маршруты
|
||||
if (!(route->flags & ROUTE_FLAG_ACTIVE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Пропускаем локальные маршруты если они не для анонса
|
||||
if ((route->flags & ROUTE_FLAG_ADVERTISED) && (route->prefix_length & ROUTE_PREFIX_F_LOCAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Для LOCAL маршрутов - отправляем с нескольких endpoints если есть PUBLIC или NAT серверы
|
||||
if (route->type == ROUTE_TYPE_LOCAL && conn->links) {
|
||||
// Сначала отправляем PUBLIC (приоритет), затем NAT
|
||||
struct ETCP_LINK* link = conn->links;
|
||||
int sent_any = 0;
|
||||
|
||||
// Первый проход - PUBLIC
|
||||
while (link) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending routine: scanning conn type=%d init=%d", link->conn->type, link->initialized);
|
||||
|
||||
if (link->conn && link->conn->type == CFG_SERVER_TYPE_PUBLIC && link->initialized) {
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)&link->conn->local_addr;
|
||||
uint32_t ip = *(uint32_t*)&sin->sin_addr;
|
||||
uint16_t port = sin->sin_port;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending routine [public]");
|
||||
route_bgp_send_route_single(bgp, conn, route, ip, port);
|
||||
sent_any = 1;
|
||||
}
|
||||
link = link->next;
|
||||
}
|
||||
|
||||
// Второй проход - NAT
|
||||
link = conn->links;
|
||||
while (link) {
|
||||
if (link->conn && link->conn->type == CFG_SERVER_TYPE_NAT && link->nat_ip && link->initialized) {
|
||||
uint32_t ip = link->nat_ip;
|
||||
uint16_t port = link->nat_port;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending routine [nat]");
|
||||
route_bgp_send_route_single(bgp, conn, route, ip, port);
|
||||
sent_any = 1;
|
||||
}
|
||||
link = link->next;
|
||||
}
|
||||
|
||||
// Если нет PUBLIC и NAT - отправляем без endpoint (0.0.0.0:0)
|
||||
if (!sent_any) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending routine [any]");
|
||||
route_bgp_send_route_single(bgp, conn, route, 0, 0);
|
||||
}
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to send BGP route to conn %p", (void*)conn);
|
||||
} else {
|
||||
// Для LEARNED маршрутов - используем endpoint из route
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending routine [learned]");
|
||||
route_bgp_send_route_single(bgp, conn, route,
|
||||
htonl(route->endpoint_ip),
|
||||
htons(route->endpoint_port));
|
||||
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 Отправить полную таблицу указанному соединению
|
||||
* @brief Отправляет withdraw для конкретной подсети одному соединению
|
||||
*/
|
||||
static void route_bgp_send_table_to_conn_internal(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn)
|
||||
{
|
||||
if (!bgp || !conn || !bgp->instance->rt) {
|
||||
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_TABLE* rt = bgp->instance->rt;
|
||||
struct ROUTE_BGP_PACKET* pkt = (struct ROUTE_BGP_PACKET*)entry->dgram;
|
||||
memset(pkt, 0, BGP_PACKET_SIZE);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending full routing table (%zu routes) to connection",
|
||||
rt->count);
|
||||
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
|
||||
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
|
||||
pkt->network = htonl(network);
|
||||
pkt->prefix_length = prefix_length;
|
||||
|
||||
// Отправляем все маршруты из таблицы
|
||||
for (size_t i = 0; i < rt->count; i++) {
|
||||
route_bgp_send_route(bgp, conn, &rt->entries[i]);
|
||||
if (etcp_send(conn, entry) != 0) {
|
||||
queue_dgram_free(entry);
|
||||
queue_entry_free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance)
|
||||
{
|
||||
/**
|
||||
* @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,
|
||||
struct ROUTE_ENTRY* entry,
|
||||
int action, void* arg) {
|
||||
struct ROUTE_BGP* bgp = (struct ROUTE_BGP*)arg;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Инициализация BGP модуля
|
||||
*/
|
||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot init BGP: no instance");
|
||||
return NULL;
|
||||
|
||||
+16
-16
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "route_lib.h"
|
||||
|
||||
// Forward declarations
|
||||
struct UTUN_INSTANCE;
|
||||
@@ -13,28 +14,27 @@ struct ETCP_CONN;
|
||||
|
||||
// Sub-команды для маршрутных пакетов
|
||||
#define ROUTE_SUBCMD_ENTRY 0x01 // Элемент таблицы
|
||||
#define ROUTE_SUBCMD_WITHDRAW 0x02 // Отзыв маршрута
|
||||
#define ROUTE_SUBCMD_REQUEST 0x03 // Запрос полной таблицы
|
||||
|
||||
// Флаги в prefix_length (2 старших бита)
|
||||
#define ROUTE_PREFIX_F_DIRECT 0x40 // Можно подключиться напрямую
|
||||
#define ROUTE_PREFIX_F_LOCAL 0x80 // Локальный маршрут (не пересылать)
|
||||
#define ROUTE_SUBCMD_ENTRY_REROUTE 0x02 // old_node_id + элемент таблицы - когда найден альтернативный маршрут при WITHDRAW
|
||||
#define ROUTE_SUBCMD_WITHDRAW 0x03 // Отзыв маршрута
|
||||
#define ROUTE_SUBCMD_NODEINFO 0x04 // информация об узле для прямого подключения
|
||||
|
||||
/**
|
||||
* @brief Пакет маршрутной информации (бинарный формат)
|
||||
*/
|
||||
struct ROUTE_BGP_PACKET {
|
||||
struct PEER_INFO_ENTRY {
|
||||
struct ROUTE_PEER_INFO;
|
||||
}
|
||||
|
||||
struct BGP_ENTRY_PACKET {
|
||||
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_ENTRY (1)
|
||||
uint8_t hop_count; // Количество прыжков (инкриментирует получатель)
|
||||
uint8_t prefix_length; // Длина префикса подсети + флаги (2 старших бита)
|
||||
uint16_t latency; // Задержка (x0.1 ms)
|
||||
uint16_t endpoint_port; // Порт для прямого подключения
|
||||
uint32_t endpoint_ip; // IP для прямого подключения
|
||||
uint32_t network; // Сетевой адрес (big-endian)
|
||||
uint32_t bandwidth_kbps; // Пропускная способность
|
||||
uint64_t node_id; // ID узла назначения
|
||||
uint8_t route_type; // ROUTE_TYPE_LOCAL / LEARNED
|
||||
struct ROUTE_PEER_INFO peer_info;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct BGP_NODEINFO_PACKET {
|
||||
uint8_t cmd; // ETCP_CMD_ROUTE (1)
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO (4)
|
||||
struct ROUTE_NODE_INFO node_info;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
|
||||
+26
-1
@@ -38,4 +38,29 @@ subcmd:
|
||||
|
||||
удаленный узел считает свои хеши и сравнивает. где не совпало смотрит сколько записей.
|
||||
если записей не много - передает эти записи.
|
||||
если записей много - добавляет 4 бита к хеш таблице и строит субтаблицу для
|
||||
если записей много - добавляет 4 бита к хеш таблице и строит субтаблицу для
|
||||
|
||||
|
||||
|
||||
Как работает роутинг:
|
||||
1. типов маршрута бывает два: learned и local.
|
||||
local - маршруты из конфига (my_subnet)
|
||||
при инициализации они сразу добавляются в роутинг таблицу.
|
||||
|
||||
2. При установке подключения к новому узлу мы отправляем этому узлу полностью свою таблицу маршрутизации (local + learned узлы)
|
||||
|
||||
3. При получении маршрута мы смотрим есть ли такой маршрут уже в таблице. Если такоц маршрут есть - пропускаем. иначе добавляем/обновляем (стремимся получить маршрут с минимальным hop count).
|
||||
Добавляем так:
|
||||
инкрементируем hop_count
|
||||
устанавливая etcp линк с которого приняли как next_hop.
|
||||
рассылаем по всем активным линкам кроме линка с которого получили
|
||||
|
||||
4. При отключении от узла мы
|
||||
Удаляем все маршруты узла
|
||||
Рассылаем withdraw для узла hop_id.
|
||||
|
||||
Логика рассылки withdraw:
|
||||
Если получен withdraw и next hop маршрутов узла = hop id - удаляем маршрут и распространяем withdraw дальше по доступным линкам.
|
||||
А если next hop у нас другой - отправляем в ответ наш маршрут командой reoute (для распространения альтернативного найденного маршрута) в линк с которого пришел withdraw.
|
||||
reroute содержит дополнительное поле - old_hop_id
|
||||
|
||||
Reference in new issue
Block a user