Browse Source

TOPO_GROUP: add group_type support (UTUN/CHAT), mismatch detection, topo_groups_create_group()

- CHAT groups don't send subnets and skip route insert/delete
- Group type mismatch sends ERR_GROUP_MISMATCH to peer
- New topo_groups_create_group() public API for creating typed groups
chatgui
Evgeny 3 months ago
parent
commit
a9a6b6bb10
  1. 51
      src/topo_group.c
  2. 24
      src/topo_group.h
  3. 11
      src/topo_node.c
  4. 4
      src/topo_node.h

51
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);

24
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).
*

11
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;

4
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 {

Loading…
Cancel
Save