/** * @file route_bgp.c * @brief BGP-like обмен маршрутами — исправленная версия под новую route_lib */ #include #include #include #include "../lib/platform_compat.h" #include "../lib/debug_config.h" #include "../lib/mem.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 "route_node.h" #include "route_lib.h" #include "route_bgp.h" #include "route_ping.h" // ============================================================================ // Вспомогательные функции // ============================================================================ static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name); struct BGP_ROUTE_REQUEST* req = u_calloc(1, sizeof(struct BGP_ROUTE_REQUEST)); if (!req) return; req->cmd = ETCP_ID_ROUTE_ENTRY; req->subcmd = ROUTE_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 BGP_ROUTE_REQUEST); etcp_send(conn, e); } static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id); static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); static char* nodeinfo_format(const uint8_t* data, size_t len) { if (!data || len < sizeof(struct BGP_NODEINFO_PACKET)) return NULL; struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data; struct NODEINFO* ni = &pkt->node; uint64_t node_id = ni->node_id; const uint8_t* dyn = data + sizeof(struct BGP_NODEINFO_PACKET); 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 BGP_NODEINFO_PACKET)) { memcpy(name_buf, dyn + off, nl); off += nl; } off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET); off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET); char subs_buf[512] = {0}; if (ni->local_v4_subnets > 0) { const struct NODEINFO_IPV4_SUBNET* subs = (const struct NODEINFO_IPV4_SUBNET*)(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 v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(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 v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count); return buf; } /* Old full table sending removed - now using NODEINFO broadcast now */ // ============================================================================ // Broadcast / Withdraw: node_id - удаляемый узел, wd_source - от кого получена команда удалить // ============================================================================ static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude) { if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bgp is NULL"); return; } struct BGP_WITHDRAW_PACKET* pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET)); if (!pkt) return; pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->subcmd = ROUTE_SUBCMD_WITHDRAW; pkt->node_id = node_id; pkt->wd_source = wd_source; struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_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); } // ============================================================================ // Приём пакетов // ============================================================================ void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* link); static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len); static void route_bgp_handle_nat_check_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len); static void route_bgp_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->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/bgp"); queue_dgram_free(entry); queue_entry_free(entry); return; } struct ROUTE_BGP* bgp = instance->bgp; uint8_t* data = entry->dgram; uint8_t cmd = data[0]; uint8_t subcmd = data[1]; if (cmd != ETCP_ID_ROUTE_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet from %s, subcmd=%d, len=%zu", from_conn->log_name, subcmd, entry->len); if (subcmd == ROUTE_SUBCMD_NODEINFO) { char* s = nodeinfo_format(data, entry->len); if (s) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s from %s", s, from_conn->log_name); u_free(s); } route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_WITHDRAW) { route_bgp_process_withdraw(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) { route_bgp_handle_request_table(bgp, from_conn); } else if (subcmd == ROUTE_SUBCMD_PING_REQ) { route_ping_handle_req(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_PING_RESP) { route_ping_handle_resp(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_NAT_INFO) { route_bgp_handle_nat_info(bgp, from_conn, data, entry->len); } else if (subcmd == ROUTE_SUBCMD_NAT_CHECK_REQ) { route_bgp_handle_nat_check_req(bgp, from_conn, data, entry->len); } queue_dgram_free(entry); queue_entry_free(entry); } // ============================================================================ // Init / Destroy / New / Remove conn // ============================================================================ // Callback для готовности ETCP соединения - отправляет маршруты static void route_bgp_on_conn_up(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON UP %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Connection %s on up ", conn->log_name); route_bgp_new_conn(conn); } } static void route_bgp_on_conn_down(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON DOWN %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Connection %s on down ", conn->log_name); route_bgp_remove_conn(conn); } } // Callback для новых ETCP соединений - устанавливает ready callback static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) { (void)arg; if (conn && conn->instance && conn->instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP SET CBKS %s", conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Set ETCP ready callback for connection %s", conn->log_name); etcp_conn_set_up_cbk(conn, route_bgp_on_conn_up, conn->instance->bgp); etcp_conn_set_down_cbk(conn, route_bgp_on_conn_down, conn->instance->bgp); } } struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) { if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: instance is NULL"); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx", (unsigned long long)instance->node_id); struct ROUTE_BGP* bgp = u_calloc(1, sizeof(struct ROUTE_BGP)); if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: alloc failed"); return NULL; } bgp->instance = instance; bgp->ping_pending = NULL; bgp->next_ping_req_id = 1; // Create nodes queue with hash support for fast lookup by node_id bgp->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, "bgp_nodes"); if (!bgp->nodes) { u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: nodes queue creation failed"); return NULL; } bgp->senders_list = queue_new(instance->ua, 0, "BGP_senders"); if (!bgp->senders_list) { queue_free(bgp->nodes); u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: senders_list queue creation failed"); return NULL; } int vc = 0; struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; while (s) { if (s->ip.family == AF_INET) vc++; s = s->next; } size_t d = vc * sizeof(struct NODEINFO_IPV4_SUBNET); bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + d); if (!bgp->local_node) { queue_free(bgp->nodes); queue_free(bgp->senders_list); u_free(bgp); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: local_node alloc failed"); return NULL; } bgp->local_node->node.node_id = instance->node_id; bgp->local_node->node.hop_count = 0; bgp->local_node->node.ver = 1; bgp->local_node->dirty = 0; route_bgp_update_my_nodeinfo(instance, bgp); etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk); // Устанавливаем callback для новых ETCP соединений etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized (NODEINFO based routing)"); return bgp; } void route_bgp_destroy(struct UTUN_INSTANCE* instance) { if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_destroy: instance is NULL"); return; } if (!instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_destroy: bgp is NULL"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx", (unsigned long long)instance->node_id); etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); // route_ping_destroy_pending(instance->bgp); struct ll_entry* e; while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) { queue_entry_free(e); } queue_free(instance->bgp->senders_list); if (instance->bgp->local_node) { if (instance->bgp->local_node->paths) { queue_free(instance->bgp->local_node->paths); } u_free(instance->bgp->local_node); } if (instance->bgp->nodes) { queue_free(instance->bgp->nodes); } u_free(instance->bgp); instance->bgp = NULL; } void route_bgp_set_nat_check_local(struct ROUTE_BGP* bgp, int allow) { if (!bgp) return; bgp->allow_nat_check_local = allow ? 1 : 0; } void route_bgp_new_conn(struct ETCP_CONN* conn) { if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn is NULL"); return; } if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance is NULL"); return; } if (!conn->instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance->bgp is NULL"); return; } if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn->instance->rt is NULL"); return; } struct ROUTE_BGP* bgp = conn->instance->bgp; route_bgp_add_to_senders(bgp, conn); // Scan ALL connections and ALL links in instance - start NAT check if not started struct ETCP_CONN* c = conn->instance->connections; while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->conn && l->nat_check_status < NAT_CHECK_IN_PROGRESS) { route_bgp_start_link_nat_check(bgp, l); } l = l->next; } c = c->next; } route_bgp_send_table_request(bgp, conn); } void route_bgp_remove_conn(struct ETCP_CONN* conn) { if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: conn is NULL"); return; } if (!conn->instance || !conn->instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: invalid instance/bgp"); return; } struct ROUTE_BGP* bgp = conn->instance->bgp; // SAFETY: проверяем что conn ещё есть в senders_list bool found_in_list = false; struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_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, "route_bgp_remove_conn: conn already removed or not in list, skipping"); return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx", (unsigned long long)conn->peer_node_id); // Cancel pending NAT checks for this connection's links route_ping_cancel_for_conn(bgp, 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; // Remove this connection from all nodes' path lists // and send WITHDRAW if a node becomes unreachable bool need_withdraw = false; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; while (node_entry) { struct ll_entry* next = node_entry->next; struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry; if (route_bgp_remove_path(nq, conn) == 1) { need_withdraw = true; if (rt) { route_delete(rt, nq); } nq->dirty = 1; if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = nq->node.node_id; struct ll_entry* entry = node_entry; if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key); } node_entry = next; } // Удаляем из списка рассылки e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn == conn) { queue_remove_data(bgp->senders_list, e); queue_entry_free(e); break; } e = e->next; } if (need_withdraw) { route_bgp_broadcast_withdraw(bgp, conn->peer_node_id, conn->instance->node_id, NULL); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, paths updated"); } /* ================================================ * NEW NODEINFO BASED IMPLEMENTATION * ================================================ */ struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) { if (!bgp || !bgp->nodes) { return NULL; } uint64_t key = node_id; struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8); return e ? (struct NODEINFO_Q*)e : NULL; } int route_bgp_add_path(struct NODEINFO_Q* 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, "node_paths"); if (!nq->paths) return -1; } size_t hop_size = hop_count * 8; size_t path_size = sizeof(struct NODEINFO_PATH) - sizeof(struct ll_entry) + hop_size; struct ll_entry* pe = queue_entry_new(path_size); if (!pe) return -1; struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe; path->conn = conn; path->hop_count = hop_count; uint64_t* stored = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); memcpy(stored, hop_list, hop_size); queue_data_put(nq->paths, pe); DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s with %d hops", (unsigned long long)nq->node.node_id, conn->log_name, hop_count); return 0; } int route_bgp_remove_path_by_hop(struct NODEINFO_Q* nq, uint64_t wd_source) { if (!nq || !nq->paths) return 0; int removed = 0; struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e; // parse hoplist in dyn after hop_count (after fixed PATH) uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); 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; // unreachable } return removed; } int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) { if (!nq || !conn || !nq->paths) { return -1; } // Remove conn from paths queue struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)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; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via %s, remaining paths: %d", (unsigned long long)nq->node.node_id, conn->log_name, remaining); // If no paths left - node is unreachable if (remaining == 0) { if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } return 1; // signal that node became unreachable } return 0; } int nodeinfo_dyn_size(struct NODEINFO* node) { return node->node_name_len + node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + node->local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) + node->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET) + node->tranzit_nodes * 8 + node->hop_count * 8; } // 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; // Защита: если conn упал, статус уже сброшен в NONE 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; } uint8_t nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED; link->nat_type = nat_type; link->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED; // Send NAT_INFO to peer (via their connection) if (link->etcp) { uint8_t socket_id = link->remote_socket_id; route_bgp_send_nat_info(link->etcp, socket_id, na->nat_ip, na->nat_port, nat_type); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "nat check result: type=%s for node %s", success ? "OPEN" : "RESTRICTED", link->etcp->log_name); u_free(na); } static struct ETCP_CONN* route_bgp_find_third_node(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude) { if (!bgp || !bgp->senders_list) return NULL; struct ll_entry* e = bgp->senders_list->head; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item && item->conn && item->conn != exclude && item->conn->links && item->conn->links->remote_only_local==0) return item->conn; e = e->next; } return NULL; } static void route_bgp_extract_nat_addr(struct ETCP_CONN* conn, uint32_t* nat_ip, uint16_t* nat_port) { *nat_ip = 0; *nat_port = 0; if (!conn) return; struct ETCP_LINK* l = conn->links; while (l) { if (l->nat_ip != 0) { *nat_ip = l->nat_ip; *nat_port = l->nat_port; return; } l = l->next; } } void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* link) {// проверяем тип нат для линка link if (!bgp || !link || !link->conn || !link->etcp) return; if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return; // Find third node to send ping through struct ETCP_CONN* third_conn = route_bgp_find_third_node(bgp, link->etcp); if (!third_conn) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "no third node found for link"); return; } // Get target address - use nat_ip/nat_port if set, otherwise use remote_addr /* link->nat_ip и sin_addr.s_addr в network byte order */ 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); /* port в host order для API */ } if (!bgp->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; } // Allocate callback arg 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; // Use peer public key from the connection's crypto context 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; } // Send ping via third node - embed peer pubkey for NAT detection int ret = route_ping_send_req_addr(bgp, 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); } } int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len) { if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1; struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data; struct NODEINFO* ni = &pkt->node; int dyn_size=nodeinfo_dyn_size(ni); if (len!=dyn_size + sizeof(struct BGP_NODEINFO_PACKET)) { 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; } struct NODEINFO_Q* nodeinfo1 = route_bgp_get_node(bgp, node_id); uint8_t new_ver = ni->ver; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO from %s node=%016llx new_ver=%d local_ver=%d local_node=%p nodeinfo1=%p", from->log_name, (unsigned long long)node_id, new_ver, nodeinfo1 ? nodeinfo1->last_ver : 0, (void*)bgp->local_node, (void*)nodeinfo1); if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) { DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO from %s ignored (old ver %d <= %d)", from->log_name, new_ver, nodeinfo1->last_ver); return 0; } int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8; struct ll_queue* paths=NULL; int need_alloc=0; if (nodeinfo1) {// remove old node paths=nodeinfo1->paths; if (nodeinfo1->ll.size < new_data_size) { need_alloc=1; } } else need_alloc=1; if (need_alloc) { if (nodeinfo1) { queue_remove_data(bgp->nodes, &nodeinfo1->ll); queue_entry_free(&nodeinfo1->ll); } nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size); paths = queue_new(bgp->instance->ua, 0, "node_paths"); memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); queue_data_put_with_index(bgp->nodes, &nodeinfo1->ll, offsetof(struct NODEINFO_Q, node.node_id)-sizeof(struct ll_entry), 8); } else memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size); /* Если это наш local_node (по node_id), обновляем его данные */ DEBUG_INFO(DEBUG_CATEGORY_BGP, "Checking local_node update: node_id=%016llx my_id=%016llx local_node=%p", (unsigned long long)node_id, (unsigned long long)bgp->instance->node_id, (void*)bgp->local_node); if (node_id == bgp->instance->node_id && bgp->local_node) { /* Копируем динамическую часть (сокеты, подсети) из nodeinfo1 в local_node */ uint8_t* dyn_src = (uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO); uint8_t* dyn_dst = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); int dyn_sz = nodeinfo1->node.node_name_len + nodeinfo1->node.local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) + nodeinfo1->node.local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) + nodeinfo1->node.local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) + nodeinfo1->node.local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET); memcpy(dyn_dst, dyn_src, dyn_sz); bgp->local_node->node.ver = nodeinfo1->node.ver; bgp->local_node->last_ver = new_ver; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Updated local_node from NODEINFO for node %016llx", (unsigned long long)node_id); } nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver; // add last hop uint64_t* hop_list = (uint64_t*)((uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO) + dyn_size); uint64_t sender = from->peer_node_id; memcpy(hop_list, &sender, 8); nodeinfo1->node.hop_count++; // update node path route_bgp_remove_path_by_hop(nodeinfo1, from->peer_node_id); route_bgp_add_path(nodeinfo1, from, hop_list, nodeinfo1->node.hop_count); if (bgp->instance->rt) { route_insert(bgp->instance->rt, nodeinfo1); } int hop_count=nodeinfo1->node.hop_count; // рассылаем по узлам которых нет в hop_list этой ноды (loop prevention) struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL; while (e) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (item->conn) { uint64_t id=item->conn->peer_node_id; int found=0; for (int i=0; iconn); } else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id); } e = e->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d)", from->log_name, (unsigned long long)node_id, new_ver, nodeinfo1->paths ? queue_entry_count(nodeinfo1->paths) : 0); return 0; } int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len) { if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) return -1; struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data; uint64_t node_id = wp->node_id; uint64_t wd_source = wp->wd_source; struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id); if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; } int ret=route_bgp_remove_path_by_hop(nq, wd_source); if (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) { if (bgp->instance && bgp->instance->rt) { route_delete(bgp->instance->rt, nq); } nq->dirty = 1; if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } uint64_t key = node_id; struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8); if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after WITHDRAW", (unsigned long long)node_id); route_bgp_broadcast_withdraw(bgp, node_id, wd_source, sender); } return 0; } void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn) { if (!node || !conn) { return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name); int dyn = nodeinfo_dyn_size(&node->node); size_t ps = sizeof(struct BGP_NODEINFO_PACKET) + dyn; uint8_t* p = u_malloc(ps); if (!p) { return; } p[0] = ETCP_ID_ROUTE_ENTRY; p[1] = ROUTE_SUBCMD_NODEINFO; memcpy(p + 2, &node->node, sizeof(struct NODEINFO)); uint8_t* ds = (uint8_t*)&node->node + sizeof(struct NODEINFO); memcpy(p + sizeof(struct BGP_NODEINFO_PACKET), ds, dyn); struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(p); return; } e->dgram = p; e->len = ps; int ret = etcp_send(conn, e); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NODEINFO failed: %d", ret); } } static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn || !bgp->senders_list) return; bool already = false; struct ll_entry* e = bgp->senders_list->head; while (e) { if (((struct ROUTE_BGP_CONN_ITEM*)e->data)->conn == conn) { already = true; break; } e = e->next; } if (!already) { struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM)); if (item_entry) { ((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn; queue_data_put(bgp->senders_list, item_entry); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added to senders_list"); } } } static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id) { if (!nq || !nq->paths) return false; struct ll_entry* e = nq->paths->head; while (e) { struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e; uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH)); 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 route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; uint64_t target = conn->peer_node_id; struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL; while (e) { struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e; if (nq->node.hop_count == 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "local node in learned list"); e = e->next; continue; } if (!nq->paths) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; } if (route_bgp_should_send_to(nq, target)) { route_bgp_send_nodeinfo(nq, conn); } e = e->next; } } static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", conn->log_name); route_bgp_send_nodeinfo(bgp->local_node, conn); route_bgp_send_full_table(bgp, conn); route_bgp_add_to_senders(bgp, conn); } static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { if (!bgp || !from_conn || !data || len < sizeof(struct BGP_NAT_INFO) || !bgp->local_node) return; const struct BGP_NAT_INFO* info = (const struct BGP_NAT_INFO*)data; /* IP в пакете в network byte order (big-endian bytes), конвертируем в host order */ 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); /* порт из network byte order */ uint8_t socket_id = info->socket_id; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO recv: ip_bytes=0x%02x%02x%02x%02x ip_host=0x%08x port_net=%u port_host=%u", info->nat_ip[0], info->nat_ip[1], info->nat_ip[2], info->nat_ip[3], nat_ip, info->nat_port, nat_port); uint8_t* dynamic = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO); dynamic += bgp->local_node->node.node_name_len; struct NODEINFO_IPV4_SOCKET* sockets = (struct NODEINFO_IPV4_SOCKET*)dynamic; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO updating local_node: sockets_count=%d looking for socket_id=%u", bgp->local_node->node.local_v4_sockets, socket_id); for (int i = 0; i < bgp->local_node->node.local_v4_sockets; i++) { DEBUG_INFO(DEBUG_CATEGORY_BGP, " checking socket %d: id=%u", i, sockets[i].id); if (sockets[i].id == socket_id) { uint8_t old_type = sockets[i].type; /* addr[] хранится в network byte order (big-endian bytes) */ uint32_t old_ip = (sockets[i].addr[0] << 24) | (sockets[i].addr[1] << 16) | (sockets[i].addr[2] << 8) | sockets[i].addr[3]; uint16_t old_port = sockets[i].port; memcpy(sockets[i].addr, info->nat_ip, 4); sockets[i].port = nat_port; uint8_t verified_type; if (info->nat_type == NAT_TYPE_OPEN) { verified_type = NAT_VERIFIED_OPEN; } else if (info->nat_type == NAT_TYPE_RESTRICTED) { verified_type = NAT_VERIFIED_RESTRICTED; } else { verified_type = NAT_VERIFIED_UNKNOWN; } sockets[i].type = verified_type; struct ETCP_SOCKET* es = bgp->instance->etcp_sockets; while (es) { if (es->sock_id == socket_id) { es->nat_type = verified_type; break; } es = es->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO update: socket_id=%u old_ip=0x%08x nat_ip=0x%08x old_port=%u nat_port=%u old_type=%u verified_type=%u changed=%d", socket_id, old_ip, nat_ip, old_port, nat_port, old_type, verified_type, (old_ip != nat_ip || old_port != nat_port || old_type != verified_type) ? 1 : 0); if (old_ip != nat_ip || old_port != nat_port || old_type != verified_type) { bgp->local_node->dirty = 1; bgp->local_node->node.ver = (bgp->local_node->node.ver + 1) % 255 + 1; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO triggered NODEINFO send, ver=%d senders=%d", bgp->local_node->node.ver, bgp->senders_list ? queue_entry_count(bgp->senders_list) : 0); if (bgp->senders_list) { struct ll_entry* se = bgp->senders_list->head; while (se) { struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)se->data; if (item && item->conn) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO sending NODEINFO to %s", item->conn->log_name); route_bgp_send_nodeinfo(bgp->local_node, item->conn); } se = se->next; } } } break; } } struct ETCP_LINK* l = from_conn->links; while (l) { l->nat_type = info->nat_type; l = l->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO from %s: socket_id=%u type=%s ip=%u.%u.%u.%u %s port=%u", from_conn->log_name, socket_id, info->nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED", (info->nat_ip[0]), (info->nat_ip[1]), (info->nat_ip[2]), (info->nat_ip[3]), ip_to_str(&info->nat_ip, AF_INET).str, (unsigned)nat_port); } static void route_bgp_handle_nat_check_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {// поиск нужного линка и запуск nat_check if (!bgp || !from_conn || !data || len < sizeof(struct BGP_NAT_CHECK_REQ)) return; const struct BGP_NAT_CHECK_REQ* req = (const struct BGP_NAT_CHECK_REQ*)data; uint8_t socket_id = req->socket_id; struct sockaddr_storage dummy; memset(&dummy, 0, sizeof(dummy)); if (req->interface_ip != 0) { struct sockaddr_in* sin = (struct sockaddr_in*)&dummy; sin->sin_family = AF_INET; sin->sin_addr.s_addr = req->interface_ip; sin->sin_port = req->interface_port; } 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) { struct sockaddr_in* sin = (struct sockaddr_in*)&dummy; DEBUG_WARN(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: no link with remote_socket_id=%u interface=%s:%u", from_conn->log_name, socket_id, sin->sin_addr.s_addr ? ip_to_str(&sin->sin_addr, AF_INET).str : "none", (unsigned)ntohs(sin->sin_port)); } else if (target_link->nat_check_status == NAT_CHECK_IN_PROGRESS) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: check already in progress for socket_id=%u", from_conn->log_name, socket_id); } else { DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: restarting NAT check for socket_id=%u", from_conn->log_name, socket_id); route_bgp_start_link_nat_check(bgp, target_link); } } void route_bgp_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 BGP_NAT_INFO* pkt = u_calloc(1, sizeof(struct BGP_NAT_INFO)); if (!pkt) return; pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->subcmd = ROUTE_SUBCMD_NAT_INFO; pkt->socket_id = socket_id; /* IP уже в network byte order */ 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; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO send: ip_host=0x%08x ip_bytes=0x%02x%02x%02x%02x port_host=%u port_net=%u", nat_ip, pkt->nat_ip[0], pkt->nat_ip[1], pkt->nat_ip[2], pkt->nat_ip[3], nat_port, pkt->nat_port); struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return; } e->dgram = (uint8_t*)pkt; e->len = sizeof(struct BGP_NAT_INFO); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_info to %s: socket_id=%u type=%s ip=%s port=%u", conn->log_name, socket_id, nat_type == NAT_TYPE_OPEN ? "OPEN" : "RESTRICTED", ip_to_str(pkt->nat_ip, AF_INET).str, (unsigned)nat_port); etcp_send(conn, e); } void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) { if (!conn) return; struct BGP_NAT_CHECK_REQ* pkt = u_calloc(1, sizeof(struct BGP_NAT_CHECK_REQ)); if (!pkt) return; pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->subcmd = ROUTE_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 BGP_NAT_CHECK_REQ); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_check_req to %s: socket_id=%u interface=%s:%u", conn->log_name, socket_id, ip_to_str(&pkt->interface_ip, AF_INET).str, (unsigned)ntohs(pkt->interface_port)); etcp_send(conn, e); } void route_bgp_request_nat_check_all(struct ROUTE_BGP* bgp) { if (!bgp || !bgp->instance) return; int count = 0; struct ETCP_CONN* conn = bgp->instance->connections; while (conn) { struct ETCP_LINK* link = conn->links; while (link) { route_bgp_start_link_nat_check(bgp, link); count++; link = link->next; } conn = conn->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT check requested for all links, count=%d", count); }