Bound topology dump writes and return the actual length
This commit is contained in:
1 parent
8dcd2fac3b
commit
324b289968
3 files changed
+76
-13
No files matched your search
@@ -1,6 +1,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/debug_config.h"
|
||||
@@ -971,23 +972,36 @@ void topo_node_dump_all(struct TOPO_GROUP* group) {
|
||||
|
||||
/* То же, что dump_all, но в строковый буфер (для control-сервера). */
|
||||
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
|
||||
if (!group || !group->nodes || !buf || buf_size == 0) return 0;
|
||||
int pos = 0;
|
||||
if (buf && buf_size) buf[0] = '\0';
|
||||
if (!group || !group->nodes || !buf || buf_size == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node dump: invalid arguments group=%p buf=%p capacity=%zu", (void*)group, (void*)buf, buf_size);
|
||||
return 0;
|
||||
}
|
||||
if (buf_size > INT_MAX) buf_size = INT_MAX;
|
||||
size_t pos = 0;
|
||||
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
"=== BGP Nodes dump: %d nodes ===\n", queue_entry_count(group->nodes));
|
||||
#define FMT_ADD(...) do { \
|
||||
int added = snprintf(buf + pos, buf_size - pos, __VA_ARGS__); \
|
||||
if (added < 0) { \
|
||||
buf[pos] = '\0'; \
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node dump: formatting failed after %zu bytes", pos); \
|
||||
return -1; \
|
||||
} \
|
||||
if ((size_t)added >= buf_size - pos) { pos = buf_size - 1; goto truncated; } \
|
||||
pos += (size_t)added; \
|
||||
} while (0)
|
||||
|
||||
FMT_ADD("=== BGP Nodes dump: %d nodes ===\n", queue_entry_count(group->nodes));
|
||||
|
||||
int idx = 0;
|
||||
struct ll_entry* e = group->nodes->head;
|
||||
while (e && (size_t)pos < buf_size) {
|
||||
while (e) {
|
||||
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
|
||||
struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id);
|
||||
if (!ni) { e = e->next; continue; }
|
||||
int is_local = (group->local_node == nq);
|
||||
const char* name_s = ni->node_name ? ni->node_name : "";
|
||||
|
||||
#define FMT_ADD(fmt, ...) pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---\n",
|
||||
idx, (unsigned long long)ni->node_id, name_s, ni->ver,
|
||||
(unsigned long long)group->group_id, is_local ? " [LOCAL]" : "");
|
||||
@@ -1052,15 +1066,24 @@ int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
|
||||
FMT_ADD("]\n");
|
||||
}
|
||||
|
||||
#undef FMT_ADD
|
||||
e = e->next; idx++;
|
||||
}
|
||||
#undef FMT_ADD
|
||||
|
||||
if ((size_t)pos >= buf_size) {
|
||||
pos = (int)buf_size - 14; if (pos < 0) pos = 0;
|
||||
pos += snprintf(buf + pos, buf_size - pos, "\n[TRUNCATED]\n");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "node dump: group=%016llx nodes=%d written=%zu capacity=%zu",
|
||||
(unsigned long long)group->group_id, idx, pos, buf_size);
|
||||
return (int)pos;
|
||||
|
||||
truncated: {
|
||||
const char marker[] = "\n[TRUNCATED]\n";
|
||||
size_t marker_len = sizeof(marker) - 1;
|
||||
if (marker_len > pos) marker_len = pos;
|
||||
memcpy(buf + pos - marker_len, marker, marker_len);
|
||||
buf[pos] = '\0';
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "node dump truncated: group=%016llx written=%zu capacity=%zu",
|
||||
(unsigned long long)group->group_id, pos, buf_size);
|
||||
return (int)pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// ===== update my nodeinfo =====
|
||||
|
||||
@@ -278,8 +278,11 @@ int topo_node_init_self(struct UTUN_INSTANCE* instance);
|
||||
int topo_node_update_my_addresses(struct UTUN_INSTANCE* instance); /* 1=changed, 0=unchanged, -1=error */
|
||||
/* Обработчик изменений локального сокета: обновляет адреса узла. */
|
||||
void topo_node_on_socket_changed(struct ETCP_SOCKET* sock, int event, void* arg);
|
||||
/* Вывести диагностику узлов группы в лог / текстовый буфер. */
|
||||
/* Вывести диагностику узлов группы в лог категории BGP на уровне INFO. */
|
||||
void topo_node_dump_all(struct TOPO_GROUP* group);
|
||||
/* Записать дамп в buf: длина без NUL или -1 при ошибке форматирования. Ёмкость ограничена INT_MAX.
|
||||
* При усечении завершает запись сразу, заменяет хвост доступной частью "\n[TRUNCATED]\n" и возвращает реальную длину.
|
||||
* При buf != NULL и buf_size > 0 строка всегда завершена NUL. Некорректные аргументы дают 0 и пустую строку, если есть буфер. */
|
||||
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size);
|
||||
/* 1, если хотя бы в одной группе пора повторить ping этого узла; иначе 0. */
|
||||
int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id);
|
||||
|
||||
@@ -142,11 +142,47 @@ static void test_compact_addresses(struct UTUN_INSTANCE *inst) {
|
||||
sqlite3_close(db); inst->topo_sqlite_db = saved_db;
|
||||
}
|
||||
|
||||
/* Каждый размер буфера: длина, NUL, защитные байты, сохранённый префикс и маркер усечения. */
|
||||
static void test_node_format(struct TOPO_GROUP* group) {
|
||||
char full[8192];
|
||||
int full_len = topo_node_format_all(group, full, sizeof(full));
|
||||
assert(full_len > 0 && (size_t)full_len == strlen(full) && !strstr(full, "[TRUNCATED]"));
|
||||
assert(strstr(full, "=== BGP Nodes dump:"));
|
||||
if (group->nodes->head) assert(strstr(full, " v4addr[0]:") && strstr(full, " path[0]:"));
|
||||
const char marker[] = "\n[TRUNCATED]\n";
|
||||
debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_ERROR);
|
||||
for (size_t capacity = 0; capacity <= (size_t)full_len + 2; capacity++) {
|
||||
uint8_t* storage = u_malloc(capacity + 2);
|
||||
assert(storage); memset(storage, 0xa5, capacity + 2);
|
||||
char* buf = (char*)storage + 1;
|
||||
int length = topo_node_format_all(group, buf, capacity);
|
||||
assert(storage[0] == 0xa5 && storage[capacity + 1] == 0xa5);
|
||||
if (!capacity) { assert(!length); u_free(storage); continue; }
|
||||
size_t expected = (size_t)full_len < capacity ? (size_t)full_len : capacity - 1;
|
||||
assert(length >= 0 && (size_t)length == expected);
|
||||
assert(memchr(buf, '\0', capacity) == buf + length);
|
||||
if (capacity > (size_t)full_len) assert(!memcmp(buf, full, full_len + 1));
|
||||
else {
|
||||
size_t marker_len = expected < sizeof(marker) - 1 ? expected : sizeof(marker) - 1;
|
||||
assert(!memcmp(buf, full, expected - marker_len));
|
||||
assert(!memcmp(buf + expected - marker_len, marker, marker_len));
|
||||
}
|
||||
u_free(storage);
|
||||
}
|
||||
char empty[] = "old";
|
||||
assert(!topo_node_format_all(NULL, empty, sizeof(empty)) && !empty[0]);
|
||||
assert(!topo_node_format_all(group, NULL, sizeof(empty)));
|
||||
debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_DEBUG);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "[PASS] node dump: nodes=%d capacities=0..%d, lengths, guards, NUL and truncation",
|
||||
queue_entry_count(group->nodes), full_len + 2);
|
||||
}
|
||||
|
||||
static void test_bgp(struct UTUN_INSTANCE* signer) {
|
||||
struct UTUN_INSTANCE receiver = { .ua = signer->ua, .topo_groups = signer->topo_groups };
|
||||
struct TOPO_GROUP group = { .instance = &receiver, .group_id = 10, .group_type = TOPO_GROUP_TYPE_CHAT };
|
||||
struct ETCP_CONN from = { .instance = &receiver, .peer_node_id = 7 };
|
||||
group.nodes = queue_new(receiver.ua, 16, 0, 8, "snapshot_bgp");
|
||||
test_node_format(&group);
|
||||
struct TOPO_NODE* ni = record(signer, INT64_MAX - 7, "BGP", 1);
|
||||
from.peer_node_id = ni->node_id; from.links_up = 1;
|
||||
struct TOPO_GROUP_NODE nq = {0};
|
||||
@@ -158,6 +194,7 @@ static void test_bgp(struct UTUN_INSTANCE* signer) {
|
||||
assert(peer && peer->last_timestamp == ni->timestamp && queue_entry_count(peer->paths) == 1);
|
||||
assert(topo_group_process_nodeinfo(&group, &from, packet, (size_t)len + sizeof(struct TOPOMSG_HEADER)) == 0);
|
||||
assert(queue_entry_count(peer->paths) == 1);
|
||||
test_node_format(&group);
|
||||
struct TOPOMSG_NODEINFO_PKT* wire = (struct TOPOMSG_NODEINFO_PKT*)packet;
|
||||
wire->node.local_v4_subnets = 1; /* malformed CHAT не меняет уже принятую запись и маршрут */
|
||||
assert(topo_group_process_nodeinfo(&group, &from, packet, (size_t)len + sizeof(struct TOPOMSG_HEADER)) < 0);
|
||||
|
||||
Reference in new issue
Block a user