diff --git a/src/topo_group.c b/src/topo_group.c index 8211933b..8f9f0230 100644 --- a/src/topo_group.c +++ b/src/topo_group.c @@ -112,6 +112,7 @@ static const char* group_subcmd_name(uint8_t subcmd) { 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 "?"; } } @@ -166,6 +167,13 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* 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); } @@ -197,7 +205,7 @@ static void topo_group_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) { } } -static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint64_t group_id) { +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); @@ -206,6 +214,7 @@ static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint 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; @@ -262,7 +271,7 @@ struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) { instance->topo_groups = g; - struct TOPO_GROUP* default_group = topo_group_create(instance, TOPO_GROUP_UTUN); + 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); @@ -313,6 +322,16 @@ struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t 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) { + 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; + queue_data_put_with_index(g->group_list, &group->ll); + DEBUG_INFO(DEBUG_CATEGORY_BGP, "Group created: group_id=%016llx type=%u", (unsigned long long)group_id, group_type); + return group; +} + 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_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { @@ -351,7 +370,7 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { 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 (rt) route_delete(rt, nq); + 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); @@ -478,11 +497,33 @@ void topo_group_start_link_nat_check(struct TOPO_GROUP* group, struct ETCP_LINK* // ===== 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; } @@ -570,7 +611,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from topo_group_remove_path_by_hop(nodeinfo1, from->peer_node_id); topo_group_add_path(nodeinfo1, from, hop_list, nodeinfo1->hop_count); - if (group->instance->rt) route_insert(group->instance->rt, nodeinfo1); + if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) route_insert(group->instance->rt, nodeinfo1); if (node_id != group->instance->node_id) topo_node_lmdb_put(group, nodeinfo1); if (group->instance->control_srv) control_server_notify_node_change(group->instance->control_srv, nodeinfo1); @@ -609,7 +650,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send 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->instance && group->instance->rt) route_delete(group->instance->rt, nq); + 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); diff --git a/src/topo_group.h b/src/topo_group.h index 573dce3e..2bad6fcd 100644 --- a/src/topo_group.h +++ b/src/topo_group.h @@ -25,7 +25,8 @@ extern "C" { #define TOPO_SUBCMD_WITHDRAW 0x06 // узел стал недоступен #define TOPO_SUBCMD_NAT_INFO 0x09 // информация о типе NAT клиента #define TOPO_SUBCMD_NAT_CHECK_REQ 0x0A // запрос от клиента на проверку NAT для сокета -#define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы +#define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы +#define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы #define MAX_HOPS 16 #define BGP_NODES_HASH_SIZE 256 @@ -57,6 +58,16 @@ struct TOPOMSG_TABLE_REQ { uint8_t subcmd; } __attribute__((packed)); +/** + * @brief Пакет ошибки несоответствия типа группы + */ +struct TOPOMSG_ERR_GROUP_MISMATCH { + uint8_t cmd; + uint8_t subcmd; + uint8_t expected_type; + uint8_t received_flags; +} __attribute__((packed)); + struct TOPO_GROUP_CONN_ITEM { struct ll_entry ll; struct ETCP_CONN* conn; @@ -94,6 +105,7 @@ struct ETCP_LINK; struct TOPO_GROUP { struct ll_entry ll; uint64_t group_id; // уникальный идентификатор группы + uint8_t group_type; // TOPO_GROUP_TYPE_* struct UTUN_INSTANCE* instance; struct ll_queue* senders_list; struct ll_queue* nodes; @@ -145,6 +157,16 @@ struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g); */ struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id); +/** + * @brief Создаёт новую группу с указанным типом. + * + * @param g контейнер групп + * @param group_id уникальный идентификатор + * @param group_type TOPO_GROUP_TYPE_UTUN или TOPO_GROUP_TYPE_CHAT + * @return TOPO_GROUP или NULL при ошибке + */ +struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t group_id, uint8_t group_type); + /** * @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo). * diff --git a/src/topo_node.c b/src/topo_node.c index 50307882..cef8433b 100644 --- a/src/topo_node.c +++ b/src/topo_node.c @@ -482,6 +482,9 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets; while (s) { if (s->ip.family == AF_INET) vc++; else if (s->ip.family == AF_INET6) vc6++; s = s->next; } + // Для chat групп подсети не передаём + if (group->group_type == TOPO_GROUP_TYPE_CHAT) { vc = 0; vc6 = 0; } + int sock_count = 0, addr_count = 0, sock6_count = 0, addr6_count = 0; struct ETCP_SOCKET* e_sock = instance->etcp_sockets; while (e_sock) { @@ -531,8 +534,8 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { u_free(lq); group->local_node = NULL; return -1; } ni->ref_count = 1; - ni->flags = TOPO_FLAG_SEND_SUBNETS; - ni->group_id = TOPO_GROUP_UTUN; + ni->flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS; + ni->group_id = group->group_id; ni->node_id = instance->node_id; ni->ver = (old_ver % 255) + 1; memcpy(ni->public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE); @@ -600,7 +603,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR } srv = srv->next; } } - if (vc || vc6) { + if (group->group_type != TOPO_GROUP_TYPE_CHAT && (vc || vc6)) { struct TOPO_NODESUBNETS* r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); s = instance->config->my_subnets; while (s) { @@ -620,7 +623,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: v4s=%d v4a=%d v6s=%d v6a=%d v4sub=%d v6sub=%d ver=%d", sock_count, addr_count, sock6_count, addr6_count, vc, vc6, ni->ver); - if (instance->rt && !route_insert(instance->rt, lq)) + if (group->group_type != TOPO_GROUP_TYPE_CHAT && instance->rt && !route_insert(instance->rt, lq)) DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "failed to insert local routes"); } else { group->local_node->last_ver = group->local_node->node->ver; diff --git a/src/topo_node.h b/src/topo_node.h index 14037093..0264db36 100644 --- a/src/topo_node.h +++ b/src/topo_node.h @@ -53,6 +53,10 @@ struct UTUN_INSTANCE; // ---- group_id по умолчанию ---- #define TOPO_GROUP_UTUN 0x8000000000000000ULL +// ---- типы групп ---- +#define TOPO_GROUP_TYPE_UTUN 1 +#define TOPO_GROUP_TYPE_CHAT 2 + // ---- состояние связности с удалённым узлом (локальное, не передаётся по BGP) ---- struct TOPO_CONNECTIVITY {