You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
849 lines
47 KiB
849 lines
47 KiB
/** |
|
* @file topo_group.c |
|
* @brief BGP-like обмен топологией узлов (группы) |
|
*/ |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include "../lib/platform_compat.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/memory_pool.h" |
|
#include "utun_instance.h" |
|
#include "etcp_api.h" |
|
#include "etcp.h" |
|
#include "etcp_connections.h" |
|
#include "etcp_debug.h" |
|
#include "config_parser.h" |
|
#include "topo_node.h" |
|
#include "topo_node_sqlite.h" |
|
#include "route_lib.h" |
|
#include "topo_group.h" |
|
#include "route_ping.h" |
|
#include "route_connectivity.h" |
|
#include "control_server.h" |
|
|
|
|
|
#define TOPO_GROUP_UTUN 0x8000000000000000ULL |
|
|
|
|
|
// ============================================================================ |
|
// Вспомогательные функции |
|
// ============================================================================ |
|
|
|
static void topo_group_send_table_request(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) return; |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name); |
|
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ)); |
|
if (!req) return; |
|
req->cmd = ETCP_ID_TOPO_ENTRY; |
|
req->subcmd = TOPO_SUBCMD_REQUEST_TABLE; |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { u_free(req); return; } |
|
e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_TABLE_REQ); |
|
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); } |
|
} |
|
|
|
static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) return; |
|
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ)); |
|
if (!req) return; |
|
req->cmd = ETCP_ID_TOPO_ENTRY; |
|
req->subcmd = TOPO_SUBCMD_TABLE_COMPLETE; |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { u_free(req); return; } |
|
e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_TABLE_REQ); |
|
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); } |
|
} |
|
|
|
static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id); |
|
static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
|
|
static void nodeinfo_dump_log(const uint8_t* data, size_t len) { |
|
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return; |
|
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data; |
|
struct TOPOMSG_NODE* ni = &pkt->node; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NODEINFO nid=%016llx ver=%u v4s=%u v4a=%u hops=%u", |
|
(unsigned long long)ni->node_id, ni->ver, |
|
ni->local_v4_sockets, ni->local_v4_addrs, ni->hop_count); |
|
} |
|
|
|
static char* nodeinfo_format(const uint8_t* data, size_t len) { |
|
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return NULL; |
|
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data; |
|
struct TOPOMSG_NODE* ni = &pkt->node; |
|
uint64_t node_id = ni->node_id; |
|
const uint8_t* dyn = data + sizeof(struct TOPOMSG_NODEINFO_PKT); |
|
size_t off = 0; |
|
char name_buf[64] = {0}; |
|
size_t nl = ni->node_name_len; |
|
if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct TOPOMSG_NODEINFO_PKT)) { |
|
memcpy(name_buf, dyn + off, nl); off += nl; |
|
} |
|
off += ni->local_v4_sockets * sizeof(struct TOPOMSG_SOCKMETA4); |
|
off += ni->local_v4_addrs * sizeof(struct TOPOMSG_ADDR4); |
|
off += ni->local_v6_sockets * sizeof(struct TOPOMSG_SOCKMETA6); |
|
off += ni->local_v6_addrs * sizeof(struct TOPOMSG_ADDR6); |
|
char subs_buf[512] = {0}; |
|
if (ni->local_v4_subnets > 0) { |
|
const struct TOPOMSG_SUBNET4* subs = (const struct TOPOMSG_SUBNET4*)(dyn + off); |
|
size_t sl = 0; |
|
for (uint8_t i = 0; i < ni->local_v4_subnets && sl < 400; i++) { |
|
uint32_t addr = (subs[i].addr[0]<<24)|(subs[i].addr[1]<<16)|(subs[i].addr[2]<<8)|subs[i].addr[3]; |
|
char tmp[64]; snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%u ",(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,subs[i].prefix_length); |
|
strcat(subs_buf,tmp); sl += strlen(tmp); |
|
} |
|
} |
|
int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count); |
|
char* buf = u_malloc(need+1); if (!buf) return NULL; |
|
snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count); |
|
return buf; |
|
} |
|
|
|
static const char* group_subcmd_name(uint8_t subcmd) { |
|
switch (subcmd) { |
|
case TOPO_SUBCMD_NODEINFO: return "NODEINFO"; |
|
case TOPO_SUBCMD_REQUEST_TABLE: return "REQ_TABLE"; |
|
case TOPO_SUBCMD_WITHDRAW: return "WITHDRAW"; |
|
case TOPO_SUBCMD_PING_REQ: return "PING_REQ"; |
|
case TOPO_SUBCMD_PING_RESP: return "PING_RESP"; |
|
case TOPO_SUBCMD_NAT_INFO: return "NAT_INFO"; |
|
case TOPO_SUBCMD_NAT_CHECK_REQ: return "NAT_CHECK_REQ"; |
|
case TOPO_SUBCMD_TABLE_COMPLETE: return "TABLE_COMPLETE"; |
|
case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH"; |
|
default: return "?"; |
|
} |
|
} |
|
|
|
|
|
// ============================================================================ |
|
// Broadcast / Withdraw |
|
// ============================================================================ |
|
|
|
static void topo_group_broadcast_withdraw(struct TOPO_GROUP* group, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude) { |
|
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group is NULL"); return; } |
|
struct TOPOMSG_WITHDRAW_PKT* pkt = u_calloc(1, sizeof(struct TOPOMSG_WITHDRAW_PKT)); |
|
if (!pkt) return; |
|
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_WITHDRAW; |
|
pkt->node_id = node_id; pkt->wd_source = wd_source; |
|
struct ll_entry* e = group->senders_list ? group->senders_list->head : NULL; |
|
while (e) { |
|
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data; |
|
if (item && item->conn && item->conn != exclude) { |
|
struct ll_entry* copy = queue_entry_new(0); |
|
if (copy) { copy->dgram = u_malloc(sizeof(*pkt)); if (copy->dgram) memcpy(copy->dgram, pkt, sizeof(*pkt)); copy->len = sizeof(*pkt); etcp_send(item->conn, copy); } |
|
} |
|
e = e->next; |
|
} |
|
u_free(pkt); |
|
} |
|
|
|
|
|
// ============================================================================ |
|
// Приём пакетов |
|
// ============================================================================ |
|
|
|
static void topo_group_handle_nat_info(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len); |
|
static void topo_group_handle_nat_check_req(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len); |
|
|
|
static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) { |
|
if (!from_conn || !entry || entry->len < 2) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } |
|
struct UTUN_INSTANCE* instance = from_conn->instance; |
|
if (!instance || !instance->topo_groups) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid instance/topo_groups"); queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups); |
|
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "no default group"); queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
uint8_t* data = entry->dgram; uint8_t cmd = data[0]; uint8_t subcmd = data[1]; |
|
if (cmd != ETCP_ID_TOPO_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP recv %s from %s len=%zu", group_subcmd_name(subcmd), from_conn->log_name, entry->len); |
|
|
|
if (subcmd == TOPO_SUBCMD_NODEINFO) { nodeinfo_dump_log(data, entry->len); topo_group_process_nodeinfo(group, from_conn, data, entry->len); } |
|
else if (subcmd == TOPO_SUBCMD_WITHDRAW) topo_group_process_withdraw(group, from_conn, data, entry->len); |
|
else if (subcmd == TOPO_SUBCMD_REQUEST_TABLE) topo_group_handle_request_table(group, from_conn); |
|
else if (subcmd == TOPO_SUBCMD_PING_REQ) route_ping_handle_req(group, from_conn, data, entry->len); |
|
else if (subcmd == TOPO_SUBCMD_PING_RESP) route_ping_handle_resp(group, from_conn, data, entry->len); |
|
else if (subcmd == TOPO_SUBCMD_NAT_INFO) topo_group_handle_nat_info(group, from_conn, data, entry->len); |
|
else if (subcmd == TOPO_SUBCMD_NAT_CHECK_REQ) topo_group_handle_nat_check_req(group, from_conn, data, entry->len); |
|
else if (subcmd == TOPO_SUBCMD_TABLE_COMPLETE) { etcp_set_routing_exchange_state(from_conn, 3); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP initial sync complete with %s", from_conn->log_name); } |
|
else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH) { |
|
if (entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) { |
|
struct TOPOMSG_ERR_GROUP_MISMATCH* err = (struct TOPOMSG_ERR_GROUP_MISMATCH*)data; |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Group type mismatch from %s: expected_type=%u received_flags=0x%02x", |
|
from_conn->log_name, err->expected_type, err->received_flags); |
|
} |
|
} |
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
} |
|
|
|
|
|
// ============================================================================ |
|
// Init / Destroy / New / Remove conn |
|
// ============================================================================ |
|
|
|
static void topo_group_on_conn_up(struct ETCP_CONN* conn, void* arg) { |
|
(void)arg; |
|
if (conn && conn->instance && conn->instance->topo_groups) { |
|
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups); |
|
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer UP: %s peer=%016llx", conn->log_name, (unsigned long long)conn->peer_node_id); topo_group_new_conn(group, conn); } |
|
} |
|
} |
|
static void topo_group_on_conn_down(struct ETCP_CONN* conn, void* arg) { |
|
(void)arg; |
|
if (conn && conn->instance && conn->instance->topo_groups) { |
|
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups); |
|
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer DOWN: %s peer=%016llx", conn->log_name, (unsigned long long)conn->peer_node_id); topo_group_remove_conn(group, conn); } |
|
} |
|
} |
|
static void topo_group_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) { |
|
(void)arg; |
|
if (conn && conn->instance && conn->instance->topo_groups) { |
|
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups); |
|
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP set callbacks: %s", conn->log_name); etcp_conn_add_up_cbk(conn, topo_group_on_conn_up, group); etcp_conn_add_down_cbk(conn, topo_group_on_conn_down, group); } |
|
} |
|
} |
|
|
|
static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint64_t group_id, uint8_t group_type) { |
|
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return NULL; } |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group_id=%016llx node_id=%016llx", (unsigned long long)group_id, (unsigned long long)instance->node_id); |
|
|
|
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP)); |
|
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return NULL; } |
|
struct TOPO_GROUP* group = (struct TOPO_GROUP*)qe; |
|
memset((uint8_t*)group + sizeof(struct ll_entry), 0, sizeof(*group) - sizeof(struct ll_entry)); |
|
group->group_id = group_id; |
|
group->group_type = group_type; |
|
group->instance = instance; |
|
group->next_ping_req_id = 1; |
|
|
|
sc_derive_ed25519_pubkey(instance->my_keys.private_key, group->ed25519_public_key); |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Ed25519 pubkey derived from X25519 private key"); |
|
|
|
group->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct TOPO_NODEQ, hash_node_id) - sizeof(struct ll_entry), 8, "group_nodes"); |
|
if (!group->nodes) { queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodes queue creation failed"); return NULL; } |
|
|
|
group->senders_list = queue_new(instance->ua, 0, 0, 0, "group_senders"); |
|
if (!group->senders_list) { queue_free(group->nodes); queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "senders_list queue creation failed"); return NULL; } |
|
|
|
group->local_node = NULL; |
|
topo_group_update_my_nodeinfo(instance, group); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "TOPO group created: group_id=%016llx", (unsigned long long)group_id); |
|
return group; |
|
} |
|
|
|
static void topo_group_destroy(struct TOPO_GROUP* group) { |
|
if (!group) return; |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group_id=%016llx", (unsigned long long)group->group_id); |
|
|
|
struct ll_entry* e; |
|
while ((e = queue_data_get(group->senders_list)) != NULL) queue_entry_free(e); |
|
queue_free(group->senders_list); |
|
|
|
if (group->local_node) { topo_node_free_lists(group, group->local_node); u_free(group->local_node); } |
|
|
|
if (group->nodes) queue_free(group->nodes); |
|
} |
|
|
|
struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) { |
|
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return NULL; } |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_id=%016llx", (unsigned long long)instance->node_id); |
|
|
|
struct TOPO_GROUPS* g = u_calloc(1, sizeof(struct TOPO_GROUPS)); |
|
if (!g) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return NULL; } |
|
g->instance = instance; |
|
|
|
g->group_list = queue_new(instance->ua, 16, 0, 8, "topo_groups"); |
|
if (!g->group_list) { u_free(g); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group_list queue creation failed"); return NULL; } |
|
|
|
g->v4_sock_meta_pool = memory_pool_init(sizeof(struct TOPO_SOCKMETA4), "to_v4sock"); |
|
g->v4_addr_pool = memory_pool_init(sizeof(struct TOPO_ADDR4), "to_v4addr"); |
|
g->v6_sock_meta_pool = memory_pool_init(sizeof(struct TOPO_SOCKMETA6), "to_v6sock"); |
|
g->v6_addr_pool = memory_pool_init(sizeof(struct TOPO_ADDR6), "to_v6addr"); |
|
g->v4_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET4), "to_v4sub"); |
|
g->v6_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET6), "to_v6sub"); |
|
|
|
if (instance->config && instance->config->global.db_path[0]) { |
|
int rc = sqlite3_open(instance->config->global.db_path, &g->topo_sqlite_db); |
|
if (rc == SQLITE_OK && g->topo_sqlite_db) { |
|
sqlite3_exec(g->topo_sqlite_db, "PRAGMA journal_mode=WAL", NULL, NULL, NULL); |
|
sqlite3_exec(g->topo_sqlite_db, "PRAGMA foreign_keys=ON", NULL, NULL, NULL); |
|
topo_node_sqlite_init(g->topo_sqlite_db); |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "SQLite opened: %s", instance->config->global.db_path); |
|
} else { |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "sqlite3_open(%s) failed: %s", |
|
instance->config->global.db_path, g->topo_sqlite_db ? sqlite3_errmsg(g->topo_sqlite_db) : "null db"); |
|
if (g->topo_sqlite_db) { sqlite3_close(g->topo_sqlite_db); g->topo_sqlite_db = NULL; } |
|
} |
|
} |
|
|
|
instance->topo_groups = g; |
|
|
|
struct TOPO_GROUP* default_group = topo_group_create(instance, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN); |
|
if (!default_group) { |
|
memory_pool_destroy(g->v4_sock_meta_pool); memory_pool_destroy(g->v4_addr_pool); |
|
memory_pool_destroy(g->v6_sock_meta_pool); memory_pool_destroy(g->v6_addr_pool); |
|
memory_pool_destroy(g->v4_subnet_pool); memory_pool_destroy(g->v6_subnet_pool); |
|
queue_free(g->group_list); u_free(g); |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "failed to create default group"); |
|
return NULL; |
|
} |
|
queue_data_put_with_index(g->group_list, &default_group->ll); |
|
|
|
etcp_bind(instance, ETCP_ID_TOPO_ENTRY, topo_group_receive_cbk); |
|
etcp_add_new_conn_cbk(instance, topo_group_etcp_conn_cbk, NULL); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "TOPO groups initialized (with default group)"); |
|
return g; |
|
} |
|
|
|
void topo_groups_destroy(struct UTUN_INSTANCE* instance) { |
|
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return; } |
|
if (!instance->topo_groups) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_groups is NULL"); return; } |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_id=%016llx", (unsigned long long)instance->node_id); |
|
|
|
etcp_unbind(instance, ETCP_ID_TOPO_ENTRY); |
|
route_connectivity_cancel_all(instance); |
|
|
|
struct TOPO_GROUPS* g = instance->topo_groups; |
|
struct ll_entry* ge; |
|
while ((ge = queue_data_get(g->group_list)) != NULL) { struct TOPO_GROUP* grp = (struct TOPO_GROUP*)ge; topo_group_destroy(grp); queue_entry_free(ge); } |
|
queue_free(g->group_list); |
|
|
|
memory_pool_destroy(g->v4_sock_meta_pool); |
|
memory_pool_destroy(g->v4_addr_pool); |
|
memory_pool_destroy(g->v6_sock_meta_pool); |
|
memory_pool_destroy(g->v6_addr_pool); |
|
memory_pool_destroy(g->v4_subnet_pool); |
|
memory_pool_destroy(g->v6_subnet_pool); |
|
|
|
if (g->topo_sqlite_db) { sqlite3_close(g->topo_sqlite_db); g->topo_sqlite_db = NULL; } |
|
|
|
u_free(g); instance->topo_groups = NULL; |
|
} |
|
|
|
struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g) { |
|
return topo_groups_find(g, TOPO_GROUP_UTUN); |
|
} |
|
|
|
struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id) { |
|
if (!g || !g->group_list) return NULL; |
|
struct ll_entry* e = queue_find_data_by_index(g->group_list, &group_id); |
|
return e ? (struct TOPO_GROUP*)e : NULL; |
|
} |
|
|
|
struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t group_id, uint8_t group_type, const char* channel_id) { |
|
if (!g || !g->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return NULL; } |
|
if (topo_groups_find(g, group_id)) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group_id=%016llx already exists", (unsigned long long)group_id); return NULL; } |
|
struct TOPO_GROUP* group = topo_group_create(g->instance, group_id, group_type); |
|
if (!group) return NULL; |
|
if (channel_id) { strncpy(group->channel_id, channel_id, sizeof(group->channel_id) - 1); group->channel_id[sizeof(group->channel_id) - 1] = '\0'; } |
|
queue_data_put_with_index(g->group_list, &group->ll); |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Group created: group_id=%016llx type=%u ch_id=%s", (unsigned long long)group_id, group_type, group->channel_id); |
|
return group; |
|
} |
|
|
|
void topo_groups_set_sqlite_db(struct TOPO_GROUPS* g, sqlite3* db) { |
|
if (!g) return; |
|
g->topo_sqlite_db = db; |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_sqlite_db set"); |
|
} |
|
|
|
void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow) { if (!group) return; group->allow_nat_check_local = allow ? 1 : 0; } |
|
|
|
void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn) { |
|
if (groups) groups->node_updated_cb = fn; |
|
} |
|
|
|
void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; } |
|
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance is NULL"); return; } |
|
if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance->rt is NULL"); return; } |
|
|
|
struct TOPO_NODEQ* peer_nq = topo_node_find_by_id(group, conn->peer_node_id); |
|
topo_group_add_to_senders(group, conn); |
|
if (peer_nq && peer_nq->alien) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer 0x%016llx is alien, skipping route exchange", (unsigned long long)conn->peer_node_id); return; } |
|
|
|
struct ll_entry* entry = conn->instance->connections->head; |
|
while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_LINK* l = ce->conn->links; while (l) { if (l->initialized && l->conn && l->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, l); l = l->next; } entry = entry->next; } |
|
|
|
topo_group_send_table_request(group, conn); |
|
} |
|
|
|
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; } |
|
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid instance"); return; } |
|
|
|
bool found_in_list = false; |
|
struct ll_entry* e = group->senders_list->head; |
|
while (e) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data; if (item->conn == conn) { found_in_list = true; break; } e = e->next; } |
|
if (!found_in_list) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "conn already removed or not in list, skipping"); return; } |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer=%016llx", (unsigned long long)conn->peer_node_id); |
|
|
|
route_ping_cancel_for_conn(group, conn); |
|
struct ETCP_LINK* lnk = conn->links; while (lnk) { if (lnk->nat_check_status == NAT_CHECK_IN_PROGRESS) lnk->nat_check_status = NAT_CHECK_NONE; lnk = lnk->next; } |
|
|
|
struct ROUTE_TABLE* rt = conn->instance->rt; |
|
int nodes_removed = 0; |
|
struct ll_entry* node_entry = group->nodes ? group->nodes->head : NULL; |
|
while (node_entry) { |
|
struct ll_entry* next = node_entry->next; |
|
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)node_entry; |
|
if (topo_group_remove_path(nq, conn) == 1) { |
|
if (group->group_type != TOPO_GROUP_TYPE_CHAT && rt) route_delete(rt, nq); |
|
if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, nq->hash_node_id); |
|
nq->dirty = 1; |
|
route_connectivity_cancel_node(conn->instance, nq); |
|
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } |
|
uint64_t key = nq->hash_node_id; |
|
topo_node_free_lists(group, nq); |
|
struct ll_entry* entry = node_entry; |
|
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } |
|
nodes_removed++; |
|
etcp_router_conn_close_all_for_node(group->instance, key); |
|
uint64_t wd_src = (key == conn->peer_node_id) ? conn->instance->node_id : conn->peer_node_id; |
|
topo_group_broadcast_withdraw(group, key, wd_src, NULL); |
|
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key); |
|
} |
|
node_entry = next; |
|
} |
|
|
|
e = group->senders_list->head; |
|
while (e) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data; if (item->conn == conn) { queue_remove_data(group->senders_list, e); queue_entry_free(e); break; } e = e->next; } |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d", conn->log_name, nodes_removed); |
|
} |
|
|
|
|
|
// ============================================================================ |
|
// NODEINFO BASED IMPLEMENTATION |
|
// ============================================================================ |
|
|
|
struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id) { |
|
if (!group) return NULL; |
|
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id); |
|
if (!nq || !nq->paths || !nq->paths->head) return NULL; |
|
struct ll_entry* e = nq->paths->head; |
|
struct TOPO_NODEPATH* best_live = NULL; uint8_t best_live_hops = 255; |
|
struct TOPO_NODEPATH* best_any = NULL; uint8_t best_any_hops = 255; |
|
while (e) { |
|
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; |
|
if (path->conn) { if (path->conn->links_up && path->hop_count < best_live_hops) { best_live = path; best_live_hops = path->hop_count; } if (path->hop_count < best_any_hops) { best_any = path; best_any_hops = path->hop_count; } } |
|
e = e->next; |
|
} |
|
if (!best_live && best_any) DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "find_conn_for_node %016llx: no live paths, fallback to best (hops=%d links_up=%d)", (unsigned long long)node_id, best_any_hops, best_any->conn->links_up); |
|
return best_live ? best_live->conn : (best_any ? best_any->conn : NULL); |
|
} |
|
|
|
int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count) { |
|
if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args"); return -1; } |
|
if (!nq->paths) { nq->paths = queue_new(conn->instance->ua, 0, 0, 0, "node_paths"); if (!nq->paths) return -1; } |
|
size_t path_size = sizeof(struct TOPO_NODEPATH) - sizeof(struct ll_entry) + hop_count * 8; |
|
struct ll_entry* pe = queue_entry_new(path_size); if (!pe) return -1; |
|
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)pe; |
|
path->conn = conn; path->hop_count = hop_count; |
|
memcpy((uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH)), hop_list, hop_count * 8); |
|
queue_data_put(nq->paths, pe); |
|
return 0; |
|
} |
|
|
|
static int topo_group_remove_path_by_hop(struct TOPO_NODEQ* nq, uint64_t wd_source) { |
|
if (!nq || !nq->paths) return 0; |
|
int removed = 0; |
|
struct ll_entry* e = nq->paths->head; |
|
while (e) { |
|
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; |
|
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH)); |
|
bool has_wd = false; for (uint8_t i = 0; i < path->hop_count; i++) if (hop[i] == wd_source) { has_wd = true; break; } |
|
if (has_wd) { struct ll_entry* next = e->next; queue_remove_data(nq->paths, e); queue_entry_free(e); removed++; e = next; continue; } |
|
e = e->next; |
|
} |
|
if (removed > 0 && nq->paths && queue_entry_count(nq->paths) == 0) { queue_free(nq->paths); nq->paths = NULL; return 1; } |
|
return removed; |
|
} |
|
|
|
int topo_group_remove_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn) { |
|
if (!nq || !conn || !nq->paths) return -1; |
|
struct ll_entry* e = nq->paths->head; |
|
while (e) { struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; if (path->conn == conn) { queue_remove_data(nq->paths, e); queue_entry_free(e); break; } e = e->next; } |
|
int remaining = nq->paths ? queue_entry_count(nq->paths) : 0; |
|
if (remaining == 0) { if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } return 1; } |
|
return 0; |
|
} |
|
|
|
// NAT check context |
|
static void nat_link_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) { |
|
(void)avg_rtt; (void)count_sent; (void)count_ok; |
|
struct nat_check_arg* na = (struct nat_check_arg*)arg; |
|
if (!na || !na->link) { u_free(na); return; } |
|
struct ETCP_LINK* link = na->link; |
|
if (link->nat_check_status != NAT_CHECK_IN_PROGRESS) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "nat check cb: link status=%d, skip (conn down?)", link->nat_check_status); u_free(na); return; } |
|
link->nat_type = success ? NAT_TYPE_EIM : NAT_TYPE_STRICT; |
|
link->nat_check_status = success ? NAT_CHECK_EIM : NAT_CHECK_STRICT; |
|
if (link->etcp) topo_group_send_nat_info(link->etcp, link->remote_socket_id, na->nat_ip, na->nat_port, link->nat_type); |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT check result: type=%s for conn=%s nat=%s:%u", success ? "EIM" : "STRICT", link->etcp->log_name, ip_to_str(&na->nat_ip, AF_INET).str, na->nat_port); |
|
u_free(na); |
|
} |
|
|
|
static struct ETCP_CONN* topo_group_find_third_node(struct TOPO_GROUP* group, struct ETCP_CONN* exclude) { |
|
if (!group || !group->senders_list) return NULL; |
|
struct ll_entry* e = group->senders_list->head; |
|
while (e) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data; if (item && item->conn && item->conn != exclude && item->conn->links && item->conn->links->remote_only_local==0 && item->conn->links->remote_type != CFG_SERVER_TYPE_PRIVATE && item->conn->links->remote_type != CFG_SERVER_TYPE_LOCAL) return item->conn; e = e->next; } |
|
return NULL; |
|
} |
|
|
|
void topo_group_start_link_nat_check(struct TOPO_GROUP* group, struct ETCP_LINK* link) { |
|
if (!group || !link || !link->conn || !link->etcp) return; |
|
if (link->nat_type == NAT_TYPE_DIRECT) return; |
|
if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return; |
|
if (link->conn->type == CFG_SERVER_TYPE_PRIVATE || link->conn->type == CFG_SERVER_TYPE_LOCAL) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "skip nat check for private/local socket link"); return; } |
|
struct ETCP_CONN* third_conn = topo_group_find_third_node(group, link->etcp); |
|
if (!third_conn) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "no third node found for link"); return; } |
|
|
|
uint32_t target_ip; uint16_t target_port; |
|
if (link->nat_ip != 0 && link->nat_port != 0) { target_ip = link->nat_ip; target_port = link->nat_port; } |
|
else { struct sockaddr_in* sin = (struct sockaddr_in*)&link->remote_addr; target_ip = sin->sin_addr.s_addr; target_port = ntohs(sin->sin_port); } |
|
|
|
if (!group->allow_nat_check_local && is_local_subnet(target_ip)) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "local connection: %s, skip nat check", ip_to_str(&target_ip, AF_INET).str); return; } |
|
|
|
struct nat_check_arg* arg = u_calloc(1, sizeof(struct nat_check_arg)); |
|
if (!arg) return; arg->link = link; arg->nat_ip = target_ip; arg->nat_port = target_port; |
|
const uint8_t* pubkey = link->etcp->crypto_ctx.peer_key_set ? link->etcp->crypto_ctx.peer_public_key : NULL; |
|
if (!pubkey) { u_free(arg); DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "no peer pubkey for link=%p", (void*)link); return; } |
|
int ret = route_ping_send_req_addr(group, third_conn, target_ip, target_port, 3, 500, 1000, 5000, nat_link_check_cb, arg, pubkey); |
|
if (ret == 0) { link->nat_check_status = NAT_CHECK_IN_PROGRESS; DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping remote request via=%s (%s:%d)", third_conn->log_name, ip_to_str(&target_ip, AF_INET).str, target_port); } |
|
else { u_free(arg); DEBUG_WARN(DEBUG_CATEGORY_BGP, "failed to start for link=%s (%s:%d)", third_conn->log_name, ip_to_str(&target_ip, AF_INET).str, target_port); } |
|
} |
|
|
|
// ===== NODEINFO process ===== |
|
|
|
static void topo_group_send_err_group_mismatch(struct TOPO_GROUP* group, struct ETCP_CONN* conn, uint8_t expected_type, uint8_t received_flags) { |
|
if (!group || !conn) return; |
|
struct TOPOMSG_ERR_GROUP_MISMATCH* pkt = u_calloc(1, sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)); |
|
if (!pkt) return; |
|
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_ERR_GROUP_MISMATCH; |
|
pkt->expected_type = expected_type; pkt->received_flags = received_flags; |
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { u_free(pkt); return; } |
|
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH); |
|
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send ERR_GROUP_MISMATCH failed"); u_free(pkt); queue_entry_free(e); } |
|
} |
|
|
|
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len) { |
|
if (!group || !from || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return -1; |
|
|
|
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data; |
|
struct TOPOMSG_NODE* ni = &pkt->node; |
|
|
|
// Проверка соответствия типа группы |
|
uint8_t expected_flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS; |
|
if ((ni->flags & TOPO_FLAG_SEND_SUBNETS) != expected_flags) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Group type mismatch with %s: expected_flags=0x%02x received_flags=0x%02x (local_type=%u)", |
|
from->log_name, expected_flags, ni->flags, group->group_type); |
|
topo_group_send_err_group_mismatch(group, from, group->group_type, ni->flags); |
|
return -1; |
|
} |
|
|
|
int dyn = topo_node_dyn_size(ni); |
|
if ((size_t)dyn + sizeof(struct TOPOMSG_NODEINFO_PKT) != len) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Incorrect packet size (%s)", from->log_name); return -1; } |
|
|
|
uint64_t node_id = ni->node_id; |
|
if (ni->hop_count >= MAX_HOPS) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)", from->log_name, ni->hop_count); return -1; } |
|
if (node_id == group->instance->node_id) return 0; |
|
|
|
struct TOPO_NODEQ* nodeinfo1 = topo_node_find_by_id(group, node_id); |
|
uint8_t new_ver = ni->ver; |
|
|
|
if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver - new_ver) >= 0) { |
|
int new_hops = ni->hop_count + 1; |
|
if (new_hops <= MAX_HOPS) { |
|
uint64_t hop_list[MAX_HOPS]; |
|
const uint8_t* hop_src = (const uint8_t*)ni + sizeof(struct TOPOMSG_NODE) + dyn - ni->hop_count * 8; |
|
memcpy(hop_list, hop_src, ni->hop_count * 8); |
|
hop_list[ni->hop_count] = from->peer_node_id; |
|
topo_group_remove_path(nodeinfo1, from); |
|
topo_group_add_path(nodeinfo1, from, hop_list, new_hops); |
|
} |
|
return 0; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO: node=%016llx ver=%d v4s=%d v4a=%d v6s=%d v6a=%d hops=%d from=%s", |
|
(unsigned long long)node_id, new_ver, ni->local_v4_sockets, ni->local_v4_addrs, |
|
ni->local_v6_sockets, ni->local_v6_addrs, ni->hop_count, from->log_name); |
|
|
|
struct ll_queue* paths = NULL; |
|
int is_new_node = 0; |
|
if (nodeinfo1) { |
|
topo_node_free_lists(group, nodeinfo1); |
|
paths = nodeinfo1->paths; nodeinfo1->paths = NULL; |
|
} else { is_new_node = 1; } |
|
|
|
const uint8_t* ser_data = data + 2; |
|
size_t ser_len = len - 2; |
|
struct TOPO_NODE* new_ni = NULL; |
|
struct TOPO_NODESUBNETS* new_subnets = NULL; |
|
struct TOPOMSG_TRANZIT* new_tranzit = NULL; uint8_t new_tranzit_count = 0; |
|
uint64_t* new_hop_list = NULL; uint8_t new_hop_count = 0; |
|
if (topo_node_deserialize(group, ser_data, ser_len, &new_ni, &new_subnets, &new_tranzit, &new_tranzit_count, &new_hop_list, &new_hop_count) != 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO deserialize failed from %s", from->log_name); |
|
if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } |
|
return -1; |
|
} |
|
|
|
if (nodeinfo1) { |
|
nodeinfo1->node = new_ni; topo_node_ref(new_ni); |
|
nodeinfo1->hash_node_id = new_ni->node_id; |
|
nodeinfo1->subnets = new_subnets; |
|
u_free(nodeinfo1->tranzit_data); nodeinfo1->tranzit_data = new_tranzit; |
|
nodeinfo1->tranzit_count = new_tranzit_count; |
|
u_free(nodeinfo1->hop_list); nodeinfo1->hop_list = new_hop_list; |
|
nodeinfo1->hop_count = new_hop_count; |
|
} else { |
|
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); |
|
if (!qe) { topo_node_unref(new_ni); u_free(new_tranzit); u_free(new_hop_list); u_free(new_subnets); return -1; } |
|
nodeinfo1 = (struct TOPO_NODEQ*)qe; |
|
memset((uint8_t*)nodeinfo1 + sizeof(struct ll_entry), 0, sizeof(*nodeinfo1) - sizeof(struct ll_entry)); |
|
nodeinfo1->node = new_ni; topo_node_ref(new_ni); |
|
nodeinfo1->hash_node_id = new_ni->node_id; |
|
nodeinfo1->subnets = new_subnets; |
|
nodeinfo1->tranzit_data = new_tranzit; nodeinfo1->tranzit_count = new_tranzit_count; |
|
nodeinfo1->hop_list = new_hop_list; nodeinfo1->hop_count = new_hop_count; |
|
nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE; |
|
nodeinfo1->connectivity.interface_status = PROBE_RESULT_UNKNOWN; |
|
nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN; |
|
nodeinfo1->connectivity.real_status = PROBE_RESULT_UNKNOWN; |
|
nodeinfo1->connectivity.ping_req_time = 0; |
|
nodeinfo1->alien = 0; |
|
queue_data_put_with_index(group->nodes, &nodeinfo1->ll); |
|
} |
|
|
|
nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver; |
|
|
|
uint64_t hop_list[MAX_HOPS]; |
|
if (nodeinfo1->hop_count > 0) memcpy(hop_list, nodeinfo1->hop_list, nodeinfo1->hop_count * 8); |
|
hop_list[nodeinfo1->hop_count] = from->peer_node_id; |
|
nodeinfo1->hop_count++; |
|
|
|
u_free(nodeinfo1->hop_list); |
|
nodeinfo1->hop_list = u_malloc(nodeinfo1->hop_count * 8); |
|
memcpy(nodeinfo1->hop_list, hop_list, nodeinfo1->hop_count * 8); |
|
|
|
topo_group_remove_path_by_hop(nodeinfo1, from->peer_node_id); |
|
topo_group_add_path(nodeinfo1, from, hop_list, nodeinfo1->hop_count); |
|
|
|
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) route_insert(group->instance->rt, nodeinfo1); |
|
if (node_id != group->instance->node_id) { |
|
sqlite3* sdb = group->instance->topo_groups->topo_sqlite_db; |
|
if (sdb) { |
|
topo_node_sqlite_node_put(sdb, nodeinfo1); |
|
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) |
|
topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, NULL); |
|
} |
|
} |
|
if (group->instance->control_srv) control_server_notify_node_change(group->instance->control_srv, nodeinfo1); |
|
|
|
if (group->instance->topo_groups->node_updated_cb) |
|
group->instance->topo_groups->node_updated_cb(group->instance, node_id, ni->public_key, ni->ed25519_public_key); |
|
|
|
int hop_count = nodeinfo1->hop_count; |
|
struct ll_entry* se = group->senders_list ? group->senders_list->head : NULL; |
|
while (se) { |
|
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data; |
|
if (item->conn) { |
|
uint64_t id = item->conn->peer_node_id; |
|
if (id != node_id) { |
|
int found = 0; for (int i = 0; i < hop_count; i++) if (hop_list[i] == id) found = 1; |
|
if (found == 0) topo_group_send_nodeinfo(group, nodeinfo1, item->conn); |
|
} |
|
} |
|
se = se->next; |
|
} |
|
|
|
int socks_changed = 1; |
|
if (!is_new_node) socks_changed = 1; |
|
|
|
if (node_id != group->instance->node_id && (is_new_node || socks_changed)) { |
|
int v4a = topo_list_count((struct _topo_head*)nodeinfo1->node->v4_addrs); |
|
int v6a = topo_list_count((struct _topo_head*)nodeinfo1->node->v6_addrs); |
|
if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, nodeinfo1); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* sender, const uint8_t* data, size_t len) { |
|
if (!group || len < sizeof(struct TOPOMSG_WITHDRAW_PKT)) return -1; |
|
struct TOPOMSG_WITHDRAW_PKT* wp = (struct TOPOMSG_WITHDRAW_PKT*)data; |
|
uint64_t node_id = wp->node_id, wd_source = wp->wd_source; |
|
|
|
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id); |
|
if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; } |
|
int ret = topo_group_remove_path_by_hop(nq, wd_source); |
|
if (ret == 1) { |
|
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq); |
|
if (group->instance->control_srv) control_server_notify_node_removed(group->instance->control_srv, node_id); |
|
nq->dirty = 1; |
|
route_connectivity_cancel_node(group->instance, nq); |
|
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } |
|
topo_node_free_lists(group, nq); |
|
struct ll_entry* entry = queue_find_data_by_index(group->nodes, &node_id); |
|
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } |
|
topo_group_broadcast_withdraw(group, node_id, wd_source, sender); |
|
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->instance->topo_groups->topo_sqlite_db) |
|
topo_node_sqlite_member_del(group->instance->topo_groups->topo_sqlite_db, group->channel_id, node_id); |
|
} |
|
return 0; |
|
} |
|
|
|
void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, struct ETCP_CONN* conn) { |
|
if (!group || !node || !conn) return; |
|
|
|
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + 4096; |
|
uint8_t* p = u_malloc(max_sz); |
|
if (!p) return; |
|
|
|
p[0] = ETCP_ID_TOPO_ENTRY; p[1] = TOPO_SUBCMD_NODEINFO; |
|
|
|
int ser_len = topo_node_serialize(group, node, p + 2, max_sz - 2); |
|
if (ser_len < 0) { u_free(p); return; } |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
if (!e) { u_free(p); return; } |
|
e->dgram = p; e->len = (size_t)ser_len + 2; |
|
|
|
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NODEINFO failed"); u_free(p); queue_entry_free(e); } |
|
} |
|
|
|
static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn || !group->senders_list) return; |
|
bool already = false; |
|
struct ll_entry* e = group->senders_list->head; |
|
while (e) { if (((struct TOPO_GROUP_CONN_ITEM*)e->data)->conn == conn) { already = true; break; } e = e->next; } |
|
if (!already) { |
|
struct ll_entry* item_entry = queue_entry_new(sizeof(struct TOPO_GROUP_CONN_ITEM)); |
|
if (item_entry) { ((struct TOPO_GROUP_CONN_ITEM*)item_entry->data)->conn = conn; queue_data_put(group->senders_list, item_entry); } |
|
} |
|
} |
|
|
|
static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id) { |
|
if (!nq || !nq->node || !nq->paths) return false; |
|
if (target_id == nq->node->node_id) return false; |
|
struct ll_entry* e = nq->paths->head; |
|
while (e) { |
|
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; |
|
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH)); |
|
bool has_id = false; for (uint8_t i = 0; i < path->hop_count; i++) if (hop[i] == target_id) { has_id = true; break; } |
|
if (!has_id) return true; |
|
e = e->next; |
|
} |
|
return false; |
|
} |
|
|
|
static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) return; |
|
uint64_t target = conn->peer_node_id; |
|
struct ll_entry* e = group->nodes ? group->nodes->head : NULL; |
|
while (e) { |
|
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; |
|
if (nq->hop_count == 0) { e = e->next; continue; } |
|
if (!nq->paths) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; } |
|
if (topo_group_should_send_to(nq, target)) topo_group_send_nodeinfo(group, nq, conn); |
|
e = e->next; |
|
} |
|
} |
|
|
|
static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
if (!group || !conn) return; |
|
topo_group_send_nodeinfo(group, group->local_node, conn); |
|
topo_group_send_full_table(group, conn); |
|
topo_group_add_to_senders(group, conn); |
|
topo_group_send_table_complete(group, conn); |
|
} |
|
|
|
static void topo_group_handle_nat_info(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { |
|
if (!group || !from_conn || !data || len < sizeof(struct TOPOMSG_NAT_INFO) || !group->local_node || !group->local_node->node) return; |
|
|
|
const struct TOPOMSG_NAT_INFO* info = (const struct TOPOMSG_NAT_INFO*)data; |
|
uint32_t nat_ip = (info->nat_ip[0] << 24) | (info->nat_ip[1] << 16) | (info->nat_ip[2] << 8) | info->nat_ip[3]; |
|
uint16_t nat_port = ntohs(info->nat_port); uint8_t socket_id = info->socket_id; |
|
|
|
uint8_t verified_type; |
|
if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM; |
|
else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT; |
|
else if (info->nat_type == NAT_TYPE_DIRECT) verified_type = NAT_VERIFIED_DIRECT; |
|
else verified_type = NAT_VERIFIED_UNKNOWN; |
|
|
|
int data_changed = 0; |
|
{ struct TOPO_SOCKMETA4* sm = group->local_node->node->v4_sock_meta; while (sm) { if (sm->id == socket_id) { if (sm->nat_type != verified_type) { sm->nat_type = verified_type; data_changed = 1; } break; } sm = sm->next; } } |
|
|
|
struct ETCP_SOCKET* es = group->instance->etcp_sockets; |
|
while (es) { if (es->sock_id == socket_id) { |
|
if (verified_type == NAT_VERIFIED_STRICT) { struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; nat_sin->sin_family = AF_INET; nat_sin->sin_addr.s_addr = nat_ip; nat_sin->sin_port = htons(nat_port); es->nat_type = verified_type; } |
|
else { struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; uint32_t old_nat_ip = nat_sin->sin_addr.s_addr; uint16_t old_nat_port = ntohs(nat_sin->sin_port); nat_sin->sin_family = AF_INET; nat_sin->sin_addr.s_addr = nat_ip; nat_sin->sin_port = htons(nat_port); if (old_nat_ip != nat_ip || old_nat_port != nat_port) data_changed = 1; es->nat_type = verified_type; } break; } |
|
es = es->next; } |
|
|
|
if (data_changed) { |
|
int prev_v4a = topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs); |
|
topo_group_update_my_nodeinfo(group->instance, group); |
|
if (topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs) != prev_v4a) { |
|
group->local_node->dirty = 1; |
|
group->local_node->node->ver = (group->local_node->node->ver % 255) + 1; |
|
group->local_node->last_ver = group->local_node->node->ver; |
|
} |
|
if (group->local_node->dirty && group->senders_list) { |
|
struct ll_entry* se = group->senders_list->head; |
|
while (se) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data; if (item && item->conn) topo_group_send_nodeinfo(group, group->local_node, item->conn); se = se->next; } |
|
} |
|
} |
|
|
|
struct ETCP_LINK* l = from_conn->links; while (l) { l->nat_type = info->nat_type; l = l->next; } |
|
} |
|
|
|
static void topo_group_handle_nat_check_req(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { |
|
if (!group || !from_conn || !data || len < sizeof(struct TOPOMSG_NAT_CHECK_REQ)) return; |
|
const struct TOPOMSG_NAT_CHECK_REQ* req = (const struct TOPOMSG_NAT_CHECK_REQ*)data; |
|
uint8_t socket_id = req->socket_id; |
|
struct ETCP_LINK* target_link = NULL; |
|
struct ETCP_LINK* l = from_conn->links; while (l) { if (l->remote_socket_id == socket_id) { target_link = l; break; } l = l->next; } |
|
if (!target_link) return; |
|
if (target_link->nat_check_status == NAT_CHECK_IN_PROGRESS) return; |
|
topo_group_start_link_nat_check(group, target_link); |
|
} |
|
|
|
void topo_group_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type) { |
|
if (!conn) return; |
|
struct TOPOMSG_NAT_INFO* pkt = u_calloc(1, sizeof(struct TOPOMSG_NAT_INFO)); if (!pkt) return; |
|
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_NAT_INFO; |
|
pkt->socket_id = socket_id; |
|
pkt->nat_ip[0] = (nat_ip >> 24) & 0xFF; pkt->nat_ip[1] = (nat_ip >> 16) & 0xFF; |
|
pkt->nat_ip[2] = (nat_ip >> 8) & 0xFF; pkt->nat_ip[3] = nat_ip & 0xFF; |
|
pkt->nat_port = htons(nat_port); pkt->nat_type = nat_type; |
|
struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return; } |
|
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_NAT_INFO); |
|
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NAT_INFO failed"); u_free(pkt); queue_entry_free(e); } |
|
} |
|
|
|
void topo_group_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) { |
|
if (!conn) return; |
|
struct TOPOMSG_NAT_CHECK_REQ* pkt = u_calloc(1, sizeof(struct TOPOMSG_NAT_CHECK_REQ)); if (!pkt) return; |
|
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_NAT_CHECK_REQ; pkt->socket_id = socket_id; |
|
struct ETCP_LINK* l = conn->links; while (l) { if (l->remote_socket_id == socket_id) { struct sockaddr_in* sin = (struct sockaddr_in*)&l->conn->interface_addr; if (sin->sin_family == AF_INET) { pkt->interface_ip = sin->sin_addr.s_addr; pkt->interface_port = sin->sin_port; } break; } l = l->next; } |
|
struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return; } |
|
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_NAT_CHECK_REQ); |
|
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NAT_CHECK_REQ failed"); u_free(pkt); queue_entry_free(e); } |
|
} |
|
|
|
void topo_group_request_nat_check_all(struct TOPO_GROUP* group) { |
|
if (!group || !group->instance) return; |
|
int count = 0; |
|
struct ll_entry* entry = group->instance->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_LINK* link = ce->conn->links; while (link) { if (link->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, link); count++; link = link->next; } entry = entry->next; } |
|
} |
|
|
|
void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id) { |
|
if (!group) return; |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node=%016llx", (unsigned long long)node_id); |
|
topo_group_broadcast_withdraw(group, node_id, group->instance->node_id, NULL); |
|
}
|
|
|