diff --git a/.opencode/package-lock.json b/.opencode/package-lock.json
new file mode 100644
index 00000000..abded7b2
--- /dev/null
+++ b/.opencode/package-lock.json
@@ -0,0 +1,115 @@
+{
+ "name": ".opencode",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "@opencode-ai/plugin": "1.4.3"
+ }
+ },
+ "node_modules/@opencode-ai/plugin": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.4.3.tgz",
+ "integrity": "sha512-Ob/3tVSIeuMRJBr2O23RtrnC5djRe01Lglx+TwGEmjrH9yDBJ2tftegYLnNEjRoMuzITgq9LD8168p4pzv+U/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@opencode-ai/sdk": "1.4.3",
+ "zod": "4.1.8"
+ },
+ "peerDependencies": {
+ "@opentui/core": ">=0.1.97",
+ "@opentui/solid": ">=0.1.97"
+ },
+ "peerDependenciesMeta": {
+ "@opentui/core": {
+ "optional": true
+ },
+ "@opentui/solid": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@opencode-ai/sdk": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.4.3.tgz",
+ "integrity": "sha512-X0CAVbwoGAjTY2iecpWkx2B+GAa2jSaQKYpJ+xILopeF/OGKZUN15mjqci+L7cEuwLHV5wk3x2TStUOVCa5p0A==",
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "7.0.6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.1.8",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/1 b/1
new file mode 100644
index 00000000..f3f86bdc
--- /dev/null
+++ b/1
@@ -0,0 +1 @@
+/bin/bash: строка 1: ./test_nat_detection: Нет такого файла или каталога
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..23f10c35
--- /dev/null
+++ b/index.html
@@ -0,0 +1,254 @@
+
+
+
+
+
+ Gitea: Git with a cup of tea
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
A painless, self-hosted Git service
+
+
+
+
+
+
+
+
+ Gitea runs anywhere Go can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!
+
+
+
+
+
+
+
+ Gitea has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ll_queue.c1 b/lib/ll_queue.c1
new file mode 100644
index 00000000..a727d524
--- /dev/null
+++ b/lib/ll_queue.c1
@@ -0,0 +1,549 @@
+// ll_queue.c - Упрощенная архитектура: разделение создания элементов и работы с очередью
+#include
+#include
+#include
+#include
+#include "ll_queue.h"
+#include "u_async.h"
+#include "debug_config.h"
+#include "mem.h"
+
+#ifdef _WIN32
+ #include
+#else
+ #include
+#endif
+
+// ==================== Thread safety check ====================
+#ifdef QUEUE_THREAD_CHECK
+static inline void queue_check_thread(struct ll_queue* q) {
+ if (!q) return;
+
+#ifdef _WIN32
+ DWORD current = GetCurrentThreadId();
+ if (q->owner_thread != current) {
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': thread mismatch! owner=%lu current=%lu",
+ q->name ? q->name : "unknown",
+ (unsigned long)q->owner_thread,
+ (unsigned long)current);
+ printf("ERROR: Queue '%s' accessed from wrong thread!\n", q->name ? q->name : "unknown");
+ abort();
+ }
+#else
+ pthread_t current = pthread_self();
+ if (!pthread_equal(q->owner_thread, current)) {
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': thread mismatch! owner=%lu current=%lu",
+ q->name ? q->name : "unknown",
+ (unsigned long)q->owner_thread,
+ (unsigned long)current);
+ printf("ERROR: Queue '%s' accessed from wrong thread!\n", q->name ? q->name : "unknown");
+ abort();
+ }
+#endif
+}
+#endif
+
+// Предварительные объявления внутренних функций
+static void queue_resume_timeout_cb(void* arg);
+static void check_waiters(struct ll_queue* q);
+static void add_to_hash(struct ll_queue* q, struct ll_entry* entry);
+static void remove_from_hash(struct ll_queue* q, struct ll_entry* entry);
+
+// ==================== Управление очередью ====================
+
+struct ll_queue* queue_new(struct UASYNC* ua, size_t hash_size, char* name) {
+ if (!ua) return NULL;
+
+ struct ll_queue* q = u_calloc(1, sizeof(struct ll_queue));
+ if (!q) return NULL;
+
+ q->name = name;
+ q->ua = ua;
+ q->size_limit = -1; // Без ограничения по умолчанию
+ q->hash_size = hash_size;
+
+#ifdef QUEUE_THREAD_CHECK
+#ifdef _WIN32
+ q->owner_thread = GetCurrentThreadId();
+#else
+ q->owner_thread = pthread_self();
+#endif
+#endif
+
+ // Создать хеш-таблицу если нужно
+ if (hash_size > 0) {
+ q->hash_table = u_calloc(hash_size, sizeof(struct ll_entry*));
+ if (!q->hash_table) {
+ u_free(q);
+ return NULL;
+ }
+ }
+
+ DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_new: created queue %p, hash_size=%zu", q, hash_size);
+
+ return q;
+}
+
+struct ll_entry* ll_alloc_lldgram(uint16_t len) {
+ struct ll_entry* entry = queue_entry_new(0);
+ if (!entry) return NULL;
+
+ entry->len=0;
+ entry->memlen=len;
+ entry->dgram = u_malloc(len);
+// entry->dgram_pool = NULL; - уже null (memset)
+// entry->dgram_free_fn = NULL;
+ if (!entry->dgram) {
+ queue_entry_free(entry);
+ return NULL;
+ }
+
+ return entry;
+}
+
+void queue_free(struct ll_queue* q) {
+ if (!q) return;
+
+ DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_free: freeing queue %p, head=%p, tail=%p, count=%d",
+ q, q->head, q->tail, q->count);
+
+ // ВАЖНО: Не освобождаем элементы в очереди - они должны быть извлечены отдельно
+ // Это упрощает архитектуру и предотвращает double-u_free
+
+ // Освободить хеш-таблицу
+ if (q->hash_table) {
+ u_free(q->hash_table);
+ }
+
+ // Отменить отложенное возобновление
+ if (q->resume_timeout_id) {
+ uasync_call_soon_cancel(q->ua, q->resume_timeout_id);
+ q->resume_timeout_id = NULL;
+ }
+
+ u_free(q);
+}
+
+// ==================== Конфигурация очереди ====================
+
+void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg) {
+ if (!q) return;
+ q->callback = cbk_fn;
+ q->callback_arg = arg;
+}
+
+static void queue_resume_timeout_cb(void* arg) {
+ struct ll_queue* q = (struct ll_queue*)arg;
+ if (!q) return;
+
+ q->resume_timeout_id = NULL;
+
+ // Вызвать коллбэк если есть элементы и коллбэки разрешены
+ if (q->head && !q->callback_suspended && q->callback) {
+ q->callback(q, q->callback_arg);
+ }
+}
+
+void queue_resume_callback(struct ll_queue* q) {
+ if (!q) return;
+
+ q->callback_suspended = 0;
+
+ // Если есть элементы, запланировать вызов коллбэка
+ if (q->head && q->callback && !q->resume_timeout_id) {
+ q->resume_timeout_id = uasync_call_soon(q->ua, q, queue_resume_timeout_cb);
+ }
+}
+
+void queue_set_size_limit(struct ll_queue* q, int lim) {
+ if (!q) return;
+ q->size_limit = lim;
+}
+
+// ==================== Управление элементами ====================
+
+struct ll_entry* queue_entry_new(size_t data_size) {
+ struct ll_entry* entry = u_malloc(sizeof(struct ll_entry) + data_size);
+ if (!entry) return NULL;
+
+ memset(entry, 0, sizeof(struct ll_entry) + data_size);
+ entry->size = data_size;
+ entry->len = 0;
+ entry->pool = NULL; // Выделено через u_malloc
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_new: created entry %p, size=%zu", entry, data_size);
+
+ return entry;
+}
+
+struct ll_entry* queue_entry_new_from_pool(struct memory_pool* pool) {
+ if (!pool) return NULL;
+
+ struct ll_entry* entry = memory_pool_alloc(pool);
+ if (!entry) return NULL;
+
+ memset(entry, 0, pool->object_size);
+ entry->size = pool->object_size - sizeof(struct ll_entry);
+ entry->len = 0;
+ entry->pool = pool; // Выделено из пула
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_new_from_pool: created entry %p from pool %p", entry, pool);
+
+ return entry;
+}
+
+//void ll_u_free_dgram(struct ll_entry* entry) {
+void queue_dgram_free(struct ll_entry* entry) {
+ if (!entry) return;
+
+ if (entry->dgram) {
+ if (entry->dgram_free_fn) {
+ entry->dgram_free_fn(entry->dgram); // arg=NULL, если не задан
+ } else if (entry->dgram_pool) {
+ memory_pool_free(entry->dgram_pool, entry->dgram);
+ } else {
+ u_free(entry->dgram);
+ }
+ entry->dgram = NULL;
+ entry->len = 0; // Опционально сброс len
+ }
+}
+
+void queue_entry_free(struct ll_entry* entry) {
+ if (!entry) return;
+
+ if (entry->pool) {
+ memory_pool_free(entry->pool, entry);
+ } else {
+ u_free(entry);
+ }
+}
+
+// ==================== Операции с очередью ====================
+
+// Внутренняя функция добавления в хеш-таблицу
+static void add_to_hash(struct ll_queue* q, struct ll_entry* entry) {
+ if (!q || q->hash_size == 0 || !entry) return;
+
+ size_t slot = entry->id % q->hash_size;
+ entry->hash_next = q->hash_table[slot];
+ q->hash_table[slot] = entry;
+}
+
+// Внутренняя функция удаления из хеш-таблицы
+static void remove_from_hash(struct ll_queue* q, struct ll_entry* entry) {
+ if (!q || q->hash_size == 0 || !entry) return;
+
+ size_t slot = entry->id % q->hash_size;
+ struct ll_entry** ptr = &q->hash_table[slot];
+ while (*ptr) {
+ if (*ptr == entry) {
+ *ptr = entry->hash_next;
+ entry->hash_next = NULL;
+ return;
+ }
+ ptr = &(*ptr)->hash_next;
+ }
+}
+
+// Проверить и запустить ожидающие коллбэки
+static void check_waiters(struct ll_queue* q) {
+ if (!q) return;
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: checking waiters, count=%d, bytes=%zu", q->count, q->total_bytes);
+
+ struct queue_waiter* waiter = &q->waiter;
+
+ if (waiter->callback) {
+ // Проверить условие: не больше max_packets и не больше max_bytes
+ // max_bytes = 0 означает "не проверять байты"
+ if (q->count <= waiter->max_packets && (waiter->max_bytes == 0 || q->total_bytes <= waiter->max_bytes)) {
+ DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: condition met, calling callback, count=%d<=%d, bytes=%zu<=%zu (max_bytes_check=%s)",
+ q->count, waiter->max_packets, q->total_bytes, waiter->max_bytes,
+ waiter->max_bytes == 0 ? "disabled" : "enabled");
+ waiter->callback(q, waiter->callback_arg);
+ memset(waiter, 0, sizeof(*waiter));
+ }
+ }
+}
+
+int queue_data_put(struct ll_queue* q, struct ll_entry* entry, uint32_t id) {
+ if (!q || !entry) return -1;
+
+#ifdef QUEUE_THREAD_CHECK
+ queue_check_thread(q);
+#endif
+
+#ifdef QUEUE_DEBUG
+// queue_check_consistency(q);// !!!! for debug - BEFORE callback
+#endif
+
+ entry->id = id;
+
+ // Проверить лимит размера
+ if (q->size_limit >= 0 && q->count >= q->size_limit) {
+ queue_dgram_free(entry);
+ queue_entry_free(entry); // Освободить элемент если превышен лимит
+ return -1;
+ }
+
+ // Добавить в конец
+ entry->next = NULL;
+ entry->prev = q->tail;
+ if (q->tail) {
+ q->tail->next = entry;
+ } else {
+ q->head = entry;
+ }
+ q->tail = entry;
+
+ q->count++;
+ entry->int_len=entry->len;
+ q->total_bytes += entry->int_len;
+
+ size_t send_q_bytes = queue_total_bytes(q);
+// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check total bytes: new_q_len=%d element_size:%d", send_q_bytes, entry->size);
+
+ add_to_hash(q, entry);
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put: added entry %p (id=%u), count=%d", entry, id, q->count);
+
+ // ВАЖНО: проверка консистентности ДО коллбэка, так как коллбэк может модифицировать очередь
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug - BEFORE callback
+#endif
+
+ // Если очередь была пуста и коллбэки разрешены - вызвать коллбэк
+ if (q->count == 1 && !q->callback_suspended && q->callback) {
+ q->callback(q, q->callback_arg);
+ }
+
+ // Проверить ожидающие коллбэки (надо только при заборе из очереди)
+// check_waiters(q);
+
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug - AFTER callback
+#endif
+ return 0;
+}
+
+int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry, uint32_t id) {
+ if (!q || !entry) return -1;
+
+#ifdef QUEUE_THREAD_CHECK
+ queue_check_thread(q);
+#endif
+
+ entry->id = id;
+
+ // Проверить лимит размера
+ if (q->size_limit >= 0 && q->count >= q->size_limit) {
+ queue_entry_free(entry); // Освободить элемент если превышен лимит
+ return -1;
+ }
+
+ // Добавить в начало
+ entry->next = q->head;
+ entry->prev = NULL;
+ if (q->head) {
+ q->head->prev = entry;
+ } else {
+ q->tail = entry;
+ }
+ q->head = entry;
+
+ q->count++;
+ entry->int_len=entry->len;
+ q->total_bytes += entry->int_len;
+
+ add_to_hash(q, entry);
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put_first: added entry %p (id=%u), count=%d", entry, id, q->count);
+
+ // ВАЖНО: проверка консистентности ДО коллбэка
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug - BEFORE callback
+#endif
+
+ // Если очередь была пуста и коллбэки разрешены - вызвать коллбэк
+ if (q->count == 1 && !q->callback_suspended && q->callback) {
+ q->callback(q, q->callback_arg);
+ }
+
+ // Проверить ожидающие коллбэки
+// check_waiters(q);
+
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug
+#endif
+ return 0;
+}
+
+struct ll_entry* queue_data_get(struct ll_queue* q) {
+ if (!q || !q->head) return NULL;
+
+#ifdef QUEUE_THREAD_CHECK
+ queue_check_thread(q);
+#endif
+
+ struct ll_entry* entry = q->head;
+
+ q->head = entry->next;
+ if (q->head) q->head->prev = NULL;
+ if (!q->head) q->tail = NULL;
+
+ q->count--;
+ q->total_bytes -= entry->int_len;
+
+ entry->next = NULL;
+ entry->prev = NULL;
+
+ remove_from_hash(q, entry);
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_get: got entry %p (id=%u), count=%d", entry, entry->id, q->count);
+
+ // Приостановить коллбэки для предотвращения рекурсии
+ q->callback_suspended = 1;
+
+ // Проверить ожидающие коллбэки
+ check_waiters(q);
+
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug
+#endif
+ return entry;
+}
+
+int queue_entry_count(struct ll_queue* q) {
+ return q ? q->count : 0;
+}
+
+// Функция проверки консистентности count и total_bytes
+// Возвращает 0 если ok, -1 если есть несоответствия
+int queue_check_consistency(struct ll_queue* q) {
+ if (!q) return -1; // Недопустимая очередь
+
+ // Проверка: если count > 0, то head не должен быть NULL
+ if (q->count > 0 && !q->head) {
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': count=%d but head is NULL!",
+ q->name ? q->name : "unknown", q->count);
+ return -1;
+ }
+
+ int actual_count = 0;
+ size_t actual_bytes = 0;
+ struct ll_entry* current = q->head;
+
+ while (current) {
+ actual_count++;
+ actual_bytes += current->int_len;
+ if (current->next) {
+ if (current->next->prev != current) {
+ // Несоответствие в связях prev/next
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': prev/next error at entry %p != %p entries: %d!=%d bytes: %zu!=%zu",
+ q->name ? q->name : "unknown", (void*)current, (void*)current->next->prev, actual_count, q->count, actual_bytes, q->total_bytes);
+ return -1;
+ }
+ }
+ current = current->next;
+ }
+
+ // Проверить хвост
+ if (q->tail && q->tail->next != NULL) {
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': tail error", q->name ? q->name : "unknown");
+ return -1; // Хвост должен иметь next == NULL
+ }
+
+ // Сравнить с сохранёнными значениями
+ if (actual_count != q->count || actual_bytes != q->total_bytes) {
+ DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "Queue '%s': count error entries: %d!=%d or bytes: %zu!=%zu",
+ q->name ? q->name : "unknown", actual_count, q->count, actual_bytes, q->total_bytes);
+ return -1;
+ }
+
+ return 0;
+}
+
+// ==================== Асинхронное ожидание ====================
+
+struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, size_t max_bytes,
+ queue_threshold_callback_fn callback, void* arg) {
+ if (!q || !callback) return NULL;
+
+ struct queue_waiter* waiter = &q->waiter;
+
+ // Проверить условие немедленно
+ if (q->count <= max_packets && (max_bytes == 0 || q->total_bytes <= max_bytes)) {
+ // Условие уже выполнено - вызвать коллбэк немедленно
+ callback(q, arg);
+ return NULL;
+ }
+
+ // Установить waiter для отложенного вызова
+ waiter->max_packets = max_packets;
+ waiter->max_bytes = max_bytes;
+ waiter->callback = callback;
+ waiter->callback_arg = arg;
+
+ return waiter;
+}
+
+void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter) {
+ if (!q || !waiter || waiter != &q->waiter) return;
+
+ memset(waiter, 0, sizeof(*waiter));
+}
+
+// ==================== Поиск и удаление по ID ====================
+
+struct ll_entry* queue_find_data_by_id(struct ll_queue* q, uint32_t id) {
+ if (!q || q->hash_size == 0 || !q->hash_table) return NULL;
+
+ size_t slot = id % q->hash_size;
+ struct ll_entry* entry = q->hash_table[slot];
+
+ while (entry) {
+ if (entry->id == id) {
+ return entry;
+ }
+ entry = entry->hash_next;
+ }
+
+ return NULL;
+}
+
+int queue_remove_data(struct ll_queue* q, struct ll_entry* entry) {
+ if (!q || !entry) return -1;
+
+#ifdef QUEUE_THREAD_CHECK
+ queue_check_thread(q);
+#endif
+
+ // Удалить из двусвязного списка
+ if (entry->prev) {
+ entry->prev->next = entry->next;
+ } else {
+ q->head = entry->next;
+ }
+
+ if (entry->next) {
+ entry->next->prev = entry->prev;
+ } else {
+ q->tail = entry->prev;
+ }
+
+ q->count--;
+ q->total_bytes -= entry->int_len;
+
+ entry->next = NULL;
+ entry->prev = NULL;
+
+ remove_from_hash(q, entry);
+
+// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_remove_data: removed entry %p (id=%u), count=%d", entry, entry->id, q->count);
+#ifdef QUEUE_DEBUG
+ queue_check_consistency(q);// !!!! for debug
+#endif
+ check_waiters(q);
+ return 0;
+}
diff --git a/lib/ll_queue.h2 b/lib/ll_queue.h2
new file mode 100644
index 00000000..71b93126
--- /dev/null
+++ b/lib/ll_queue.h2
@@ -0,0 +1,130 @@
+#ifndef LL_QUEUE_H
+#define LL_QUEUE_H
+#include
+#include
+#include "memory_pool.h"
+#ifdef _WIN32
+ #include
+ #include
+#else
+ #include
+#endif
+
+#define QUEUE_DEBUG 1
+//#define QUEUE_THREAD_CHECK 1
+
+// Forward declaration
+struct ll_queue;
+
+/**
+ * @struct ll_entry
+ * @brief Элемент очереди (переменного размера).
+ *
+ * Память: [struct ll_entry + data[size]].
+ * Индекс для поиска (произвольной длины) хранится в data[index_offset].
+ * Хеш (uint64_t) вычисляется по последним 8 байтам индекса.
+ */
+struct ll_entry {
+ char* name;
+ struct ll_entry* next;
+ struct ll_entry* prev;
+ uint16_t size; ///< Размер data[]
+ uint16_t len;
+ uint16_t memlen;
+ uint16_t int_len;
+ uint8_t* dgram;
+ void (*dgram_free_fn)(uint8_t* data);
+ struct memory_pool* dgram_pool;
+ struct memory_pool* pool;
+
+ /* === НОВОЕ: поддержка индекса произвольной длины === */
+ uint16_t index_offset; ///< Смещение индекса в data[]
+ uint16_t index_size; ///< Длина индекса (0 = без индекса)
+ uint64_t index_hash; ///< Хеш по последним 8 байтам индекса
+
+ struct ll_entry* hash_next;
+ uint8_t data[0]; ///< Гибкий массив
+};
+
+/* ... (остальные typedef'ы без изменений) ... */
+
+struct ll_queue {
+ char* name;
+ struct ll_entry* head;
+ struct ll_entry* tail;
+ int count;
+ size_t total_bytes;
+ int size_limit;
+ queue_callback_fn callback;
+ void* callback_arg;
+ int callback_suspended;
+ void* resume_timeout_id;
+ struct UASYNC* ua;
+ struct queue_waiter waiter;
+ struct ll_entry** hash_table;
+ size_t hash_size;
+#ifdef QUEUE_THREAD_CHECK
+#ifdef _WIN32
+ DWORD owner_thread;
+#else
+ pthread_t owner_thread;
+#endif
+#endif
+};
+
+/* ==================== Создание / уничтожение ==================== */
+struct ll_queue* queue_new(struct UASYNC* ua, size_t hash_size, char* name);
+void queue_free(struct ll_queue* q);
+
+/* ==================== Конфигурация ==================== */
+void queue_set_size_limit(struct ll_queue* q, int lim);
+
+/* ==================== Автозабор элементов ==================== */
+void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg);
+void queue_resume_callback(struct ll_queue* q);
+
+/* ==================== Пороговое ожидание ==================== */
+struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, size_t max_bytes,
+ queue_threshold_callback_fn callback, void* arg);
+void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter);
+
+/* ==================== Работа с данными ==================== */
+/* Упрощённая версия (без индекса) */
+int queue_data_put(struct ll_queue* q, struct ll_entry* entry);
+int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry);
+
+/* Версия с хешем по индексу произвольной длины */
+int queue_data_put_with_index(struct ll_queue* q, struct ll_entry* entry,
+ uint16_t index_offset, uint16_t index_size);
+int queue_data_put_first_with_index(struct ll_queue* q, struct ll_entry* entry,
+ uint16_t index_offset, uint16_t index_size);
+
+struct ll_entry* queue_data_get(struct ll_queue* q);
+int queue_entry_count(struct ll_queue* q);
+
+/* ==================== Управление памятью ==================== */
+struct ll_entry* ll_alloc_lldgram(uint16_t len);
+struct ll_entry* queue_entry_new(size_t data_size);
+struct ll_entry* queue_entry_new_from_pool(struct memory_pool* pool);
+void queue_entry_free(struct ll_entry* entry);
+void queue_dgram_free(struct ll_entry* entry);
+
+/* ==================== Поиск и удаление ==================== */
+/**
+ * @brief Поиск по индексу произвольной длины.
+ * @note Хеш вычисляется по последним 8 байтам индекса.
+ */
+struct ll_entry* queue_find_data_by_index(struct ll_queue* q,
+ const void* index_key,
+ uint16_t index_size);
+
+int queue_remove_data(struct ll_queue* q, struct ll_entry* entry);
+
+/* ==================== Утилиты ==================== */
+static inline size_t queue_total_bytes(struct ll_queue* q) {
+ return q ? q->total_bytes : 0;
+}
+
+int queue_check_consistency(struct ll_queue* q);
+
+#endif // LL_QUEUE_H
diff --git a/linux-x64-deb b/linux-x64-deb
new file mode 100644
index 00000000..34f3afef
Binary files /dev/null and b/linux-x64-deb differ
diff --git a/src/direct_connect.txt b/src/direct_connect.txt
new file mode 100644
index 00000000..cb784522
--- /dev/null
+++ b/src/direct_connect.txt
@@ -0,0 +1,6 @@
+Есть 2 типа подключений - обычные (роутятся через подключения в конфиге) - по ним распространяется bgp и сигнальные пакеты
+
+А для передачи данных между узлами можно установить прямое подключение (или подключение через лучший промежуточный узел).
+
+Как работает:
+в nodeinfo
diff --git a/src/route_bgp.c b/src/route_bgp.c
index 055c5935..5926f548 100644
--- a/src/route_bgp.c
+++ b/src/route_bgp.c
@@ -308,6 +308,11 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
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");
@@ -549,8 +554,8 @@ struct nat_check_arg {
};
static void nat_link_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
- uint32_t recv_ip, uint16_t recv_port, void* arg) {
- (void)avg_rtt; (void)count_sent; (void)count_ok; (void)recv_ip; (void)recv_port;
+ 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;
@@ -614,7 +619,10 @@ static void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LI
target_port = ntohs(sin->sin_port);
}
- if (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; }
+ 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));
@@ -630,7 +638,7 @@ static void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LI
return;
}
// Send ping via third node - embed peer pubkey for NAT detection
- int ret = route_ping_send_req_addr(bgp, third_conn, link->etcp->peer_node_id, target_ip, target_port,
+ 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) {
@@ -875,8 +883,11 @@ static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* f
const struct BGP_NAT_INFO* info = (const struct BGP_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 = ((info->nat_port >> 8) & 0xFF) | ((info->nat_port & 0xFF) << 8);
+ uint16_t nat_port = ntohs(info->nat_port);
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;
@@ -982,6 +993,9 @@ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t
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) {
diff --git a/src/route_bgp.h b/src/route_bgp.h
index d7278fa9..0f8a2d0d 100644
--- a/src/route_bgp.h
+++ b/src/route_bgp.h
@@ -84,6 +84,7 @@ struct ROUTE_BGP {
struct NODEINFO_Q* local_node;
struct route_ping_pending* ping_pending;
uint64_t next_ping_req_id;
+ uint8_t allow_nat_check_local; // 1 = разрешить NAT check для локальных подсетей (для тестов)
};
/**
@@ -186,4 +187,10 @@ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t
*/
void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id);
+/**
+ * @brief Разрешить/запретить NAT check для локальных подсетей (127.0.0.1, 10.x.x.x и т.д.)
+ * По умолчанию запрещено. Полезно для тестов.
+ */
+void route_bgp_set_nat_check_local(struct ROUTE_BGP* bgp, int allow);
+
#endif // ROUTE_BGP_H
diff --git a/src/route_bgp1.txt b/src/route_bgp1.txt
new file mode 100644
index 00000000..eed82156
--- /dev/null
+++ b/src/route_bgp1.txt
@@ -0,0 +1,71 @@
+подсистема роутинга
+utun - это сеть узлов. у каждого узла есть собственные локальные подсети.
+глобальная задача: создать у каждого узла полную таблицу маршрутизации.
+Узлы преимущественно создают связь напрямую друг с другом. Но если это не получается отправляют трафик транзитом через доступные узлы.
+Иногда бывает что через транзитные узлы метрики лучше чем напрямую. Используем приоритетно узлы с лучшей метриков, при нехватке bandwidth используем разные каналы (агрегируем).
+Динамически обновлем метрики каналов чтобы при отказе быстро переключаться на другие и не фризить обмен из-за отказов.
+
+
+и узлы обмениваются таблицой маршрутов между собой так чтобы у каждого была актуальная таблица подсетей всех узлов.
+маршрутами меняются клиенты, подключения которых которые взяты из конфига. и сервера принявшие подключения если клиент инициировал обмен маршрутами.
+
+инициируется подключение, клиент отправляет свою таблицу. когда сервер принимает таблицу - сервер помечает что по этому маршруту надо обмениваться маршрутами, далее;
+- добавляет узел в список рассылки обновлений маршрутов
+- отправляет свою таблицу
+- добавляет в свою таблицу отсутствующие маршруты
+- если что-то добавил:
+ - рассылает измененные маршруты по списку рассылки
+- список рассылки - это linked-list очередей (также на базе ll_queue - каждый элемент = подписчик). один маршрут = одна отправленная кодограмма
+
+
+при подключении узла или изменении таблицы: узел шлёт свою таблицу
+формат кодограммы: [0x01 - routing module] [subcmd] [data]
+subcmd:
+1 [route] - отправка маршрута
+2, без данных - больше данных нет
+
+если сервер получил кодограмму маршрута - он помечает флаг в etcp что с узлом надо обмениваться маршрутами (etcp_conn->routing_exchange_active=2) и добавляет в очередь рассылки маршрутов
+
+
+
+=========================================
+механизм инкрементальной синхронизации (реализация - потом, пока мысли)
+1. вычисляем хеш каждой записи в роутинг таблице. используем ip+mask+node_uid
+2. потом из этих хешей создаем хеш таблицу (старшие n бит номер ячейки). далее вычисляются хеши каждой ячейки.
+ на первом этапе n=16, на втором - n=16*16 на третьем n=16*16*16.
+
+отправляем хеши удаленному узлу в формате: [n, 1 байт] ([индекс хеша 2 байта - используется n старших бит][хеш - 8 байт])
+
+удаленный узел считает свои хеши и сравнивает. где не совпало смотрит сколько записей.
+ если записей не много - передает эти записи.
+ если записей много - добавляет 4 бита к хеш таблице и строит субтаблицу для
+==========================================
+
+Формат роутинга:
+
+Таблица узлов состоит из записей:
+- uid
+- name
+- links
+- 3 транзитных узла с метриками (RTT)
+- маршруты узла
+
+- текущая загрузка линка (за последние 10 сек) можно частоту адаптировать под размер сети
+- bandidth limit
+- transit bandwidth limit
+
+две группы узлов:
+ - узлы за nat. подключаются через транзитные узлы. измеряют пинги до транзитных и выбирают N (3 default) лучшие линки. 3 лучших используем для распространения маршрутов
+ - транзитные узлы. имеют линки с загрузкой.
+
+добавить кодограмму - отменить распространение маршрутов по линку (+ сделать важным линком)
+
+
+
+карта маршрутизации:
+
+План:
+- сделать передачу роутинга в
+- сделать фоновый probe для узлов (условно 1 нода в секунду). выигравшие по качеству соатновятся основными
+- сделать etcp дизконнект:
+ - отправить disconnect request + дождаться ack дальше master удаляет, slave удаляет по down.
diff --git a/src/route_node.c b/src/route_node.c
index ace059b6..8971ec5d 100644
--- a/src/route_node.c
+++ b/src/route_node.c
@@ -206,6 +206,9 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
sa->port = ntohs(sin->sin_port);
sa->type = e_sock->nat_type;
sa->id = e_sock->sock_id;
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO socket: addr=0x%02x%02x%02x%02x port=%u (from sin_addr=0x%08x sin_port=%u)",
+ sa->addr[0], sa->addr[1], sa->addr[2], sa->addr[3], sa->port,
+ sin->sin_addr.s_addr, ntohs(sin->sin_port));
sa++;
}
e_sock = e_sock->next;
diff --git a/src/route_ping.c b/src/route_ping.c
index 9d2b5f06..7aadac85 100644
--- a/src/route_ping.c
+++ b/src/route_ping.c
@@ -62,69 +62,11 @@ static void route_ping_pending_timeout(void* arg) {
cur = &(*cur)->next;
}
if (p->callback) {
- p->callback(0, 0, 0, 0, 0, 0, p->arg);
+ p->callback(0, 0, 0, 0, p->arg);
}
u_free(p);
}
-// отправить запрос удаленному узлу "пропингуй такой-то узел"
-int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
- uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
- uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg) {
- if (!bgp || !to_conn || count == 0 || timeout_ms == 0) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args");
- return -1;
- }
- struct BGP_PING_REQUEST* req_pkt = u_calloc(1, sizeof(struct BGP_PING_REQUEST));
- if (!req_pkt) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed");
- return -2;
- }
- req_pkt->cmd = ETCP_ID_ROUTE_ENTRY;
- req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ;
- req_pkt->request_id = bgp->next_ping_req_id++;
- req_pkt->node_id = node_id;
- req_pkt->count = count;
- req_pkt->interval_ms = interval_ms;
- req_pkt->timeout_ms = timeout_ms;
-
- struct ll_entry* e = queue_entry_new(0);
- if (!e) {
- u_free(req_pkt);
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "queue_entry_new failed");
- return -3;
- }
- e->dgram = (uint8_t*)req_pkt;
- e->len = offsetof(struct BGP_PING_REQUEST, target_ipv4);
-
- int ret = etcp_send(to_conn, e);
- if (ret != 0) {
- u_free(req_pkt);
- queue_entry_free(e);
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send failed");
- return -4;
- }
-
- struct route_ping_pending* pending = u_calloc(1, sizeof(struct route_ping_pending));
- if (!pending) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "pending alloc failed");
- return -5;
- }
- pending->bgp = bgp;
- pending->request_id = req_pkt->request_id;
- pending->callback = cb;
- pending->arg = arg;
- pending->next = bgp->ping_pending;
- bgp->ping_pending = pending;
-
- pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout, "route_ping");
-
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx node=%016llx count=%u interval=%u timeout=%u wait=%u",
- (unsigned long long)pending->request_id, (unsigned long long)node_id,
- (unsigned)count, (unsigned)interval_ms, (unsigned)timeout_ms, (unsigned)wait_timeout_ms);
- return 0;
-}
-
// прошел ответ "серия пигнов на удаленном узле завершена"
void route_ping_handle_resp(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_PING_RESPONSE)) {
@@ -147,10 +89,7 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
}
if (p->callback) {
int success = (resp->count_ok > 0) ? 1 : 0;
- uint32_t recv_ip = (resp->recv_ipv4[0] << 24) | (resp->recv_ipv4[1] << 16) |
- (resp->recv_ipv4[2] << 8) | resp->recv_ipv4[3];
- uint16_t recv_port = ntohs(resp->recv_port);
- p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, recv_ip, recv_port, p->arg);
+ p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, p->arg);
}
u_free(p);
return;
@@ -163,7 +102,7 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
// ========================================================================
-int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
+int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn,
uint32_t target_ip, uint16_t target_port,
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
@@ -181,7 +120,6 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
req_pkt->cmd = ETCP_ID_ROUTE_ENTRY;
req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ;
req_pkt->request_id = bgp->next_ping_req_id++;
- req_pkt->node_id = node_id;
req_pkt->count = count;
req_pkt->interval_ms = interval_ms;
req_pkt->timeout_ms = timeout_ms;
@@ -190,6 +128,10 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
if (pubkey) {
memcpy(req_pkt->pubkey, pubkey, SC_PUBKEY_SIZE);
}
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "SEND_REQ: target_ip host=0x%08x net=0x%02x%02x%02x%02x port host=%u net=%u",
+ target_ip,
+ req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3],
+ target_port, req_pkt->target_port);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
@@ -222,8 +164,8 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout, "route_ping");
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx node=%016llx ip=%s port=%u pubkey=%s",
- (unsigned long long)pending->request_id, (unsigned long long)node_id,
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx ip=%s port=%u pubkey=%s",
+ (unsigned long long)pending->request_id,
ip_to_str(&target_ip, AF_INET).str, (unsigned)target_port, pubkey ? "yes" : "no");
return 0;
}
@@ -248,9 +190,6 @@ static void route_ping_series_finish(struct route_ping_series_ctx* ctx) {
resp->count_sent = ctx->count_sent;
resp->count_ok = ctx->count_ok;
resp->avg_rtt = avg_rtt;
- /* recv_ipv4/port = 0 (не используется) */
- memset(resp->recv_ipv4, 0, 4);
- resp->recv_port = 0;
struct ll_entry* e = queue_entry_new(0);
if (e) {
@@ -318,7 +257,7 @@ void route_ping_handle_req(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_PING_REQUEST)) {
+ if (!bgp || !from_conn || !data || len < offsetof(struct BGP_PING_REQUEST, pubkey)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "ping req: bad args len=%zu", len);
return;
}
@@ -336,13 +275,76 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
ctx->request_id = req_pkt->request_id;
ctx->count_total = req_pkt->count;
ctx->timeout_ms = req_pkt->timeout_ms;
- memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
+ if (len >= sizeof(struct BGP_PING_REQUEST)) {
+ memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
+ }
/* Целевой адрес */
struct sockaddr_in* sin = (struct sockaddr_in*)&ctx->target_addr;
sin->sin_family = AF_INET;
memcpy(&sin->sin_addr.s_addr, req_pkt->target_ipv4, 4);
sin->sin_port = req_pkt->target_port;
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "RECV_REQ: target_ip net_bytes=0x%02x%02x%02x%02x sin_addr=0x%08x port_net=%u port_host=%u",
+ req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3],
+ sin->sin_addr.s_addr, req_pkt->target_port, ntohs(req_pkt->target_port));
+
+ /* Если target не указан — можно разрешить из nodeinfo */
+ /*
+ if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) {
+ struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id);
+ if (nq) {
+ const struct NODEINFO_IPV4_SOCKET* sockets;
+ int count = get_node_v4_sockets(nq, &sockets);
+ if (count > 0) {
+ memcpy(&sin->sin_addr.s_addr, sockets[0].addr, 4);
+ sin->sin_port = sockets[0].port;
+ DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "resolved target from nodeinfo: %s:%u",
+ ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
+ }
+ }
+ }
+ */
+
+ /* Если pubkey не указан — ищем по target_ip:port в nodeinfo */
+ int pubkey_empty = 1;
+ for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
+ if (ctx->pubkey[i] != 0) { pubkey_empty = 0; break; }
+ }
+ if (pubkey_empty) {
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "looking for pubkey for target %s:%u, nodes_count=%d",
+ ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port),
+ bgp->nodes ? bgp->nodes->count : -1);
+ struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL;
+ while (e) {
+ struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
+ const struct NODEINFO_IPV4_SOCKET* sockets;
+ int sc = get_node_v4_sockets(nq, &sockets);
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "checking node %016llx with %d sockets",
+ (unsigned long long)nq->node.node_id, sc);
+ for (int i = 0; i < sc; i++) {
+ uint32_t socket_ip, target_ip;
+ memcpy(&socket_ip, sockets[i].addr, 4);
+ memcpy(&target_ip, req_pkt->target_ipv4, 4);
+ if (socket_ip == target_ip || socket_ip == htonl(target_ip) ||
+ htonl(socket_ip) == target_ip || htonl(socket_ip) == htonl(target_ip)) {
+ memcpy(ctx->pubkey, nq->node.public_key, SC_PUBKEY_SIZE);
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "resolved pubkey from nodeinfo for %s:%u",
+ ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
+ break;
+ }
+ }
+ pubkey_empty = 1;
+ for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
+ if (ctx->pubkey[i] != 0) { pubkey_empty = 0; break; }
+ }
+ if (!pubkey_empty) break;
+ e = e->next;
+ }
+ if (pubkey_empty) {
+ DEBUG_WARN(DEBUG_CATEGORY_BGP, "pubkey not found for target %s:%u",
+ ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
+ }
+ }
/* Ищем первый IPv4-сокет (как в старом коде) */
struct ETCP_SOCKET* ls = bgp->instance->etcp_sockets;
diff --git a/src/route_ping.h b/src/route_ping.h
index 408328bd..380e9ccb 100644
--- a/src/route_ping.h
+++ b/src/route_ping.h
@@ -12,7 +12,6 @@ struct BGP_PING_REQUEST {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
uint8_t subcmd; // ROUTE_SUBCMD_PING_REQ
uint64_t request_id; // для корреляции
- uint64_t node_id; // целевой узел
uint8_t count; // число пингов
uint8_t socket_id; // id сокета пингуемого узла
uint16_t interval_ms; // интервал между пингами
@@ -29,22 +28,14 @@ struct BGP_PING_RESPONSE {
uint8_t count_sent;
uint8_t count_ok;
uint16_t avg_rtt; // средний RTT в 0.1ms
- uint8_t recv_ipv4[4]; // IP:port с которого получен PING_REQ (STUN-like)
- uint16_t recv_port; // network byte order
} __attribute__((packed));
-typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
- uint32_t recv_ip, uint16_t recv_port, void* arg);
-
-// Отправить запрос пинга через BGP, ожидать ответа с таймаутом
-int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
- uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
- uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg);
+typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg);
// Отправить запрос пинга по произвольному IP:port (используется для NAT-детекции)
// Если target_ip == 0, используется node_id из списка известных узлов
// Если pubkey != NULL, он передается в пакете (для пинга без локального nodeinfo)
-int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
+int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn,
uint32_t target_ip, uint16_t target_port,
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
diff --git a/test_serialize b/test_serialize
new file mode 100755
index 00000000..2716eb8f
Binary files /dev/null and b/test_serialize differ
diff --git a/tests/FIX_SUMMARY.md b/tests/FIX_SUMMARY.md
new file mode 100644
index 00000000..22a72f89
--- /dev/null
+++ b/tests/FIX_SUMMARY.md
@@ -0,0 +1,4 @@
+Minimal fix applied to test_etcp_two_instances.c (removed duplicate init_connections calls before utun_instance_init).
+Test should no longer hit 'Address already in use' on second bind.
+Full test run requires fixing Makefile.am dependencies or rebuilding all src objects.
+Run 'make check' after fixing build to verify.
diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c
index 2c39b6ff..8a551777 100644
--- a/tests/test_nat_detection.c
+++ b/tests/test_nat_detection.c
@@ -52,8 +52,6 @@ static struct {
uint16_t avg_rtt;
uint8_t count_sent;
uint8_t count_ok;
- uint32_t recv_ip;
- uint16_t recv_port;
} nat_ping_result;
static int write_config(const char* path, const char* content) {
@@ -177,19 +175,15 @@ static void test_timeout_cb(void* arg) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test_nat_detection: overall test timeout");
}
-static void nat_ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
- uint32_t recv_ip, uint16_t recv_port, void* arg) {
+static void nat_ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) {
(void)arg;
nat_ping_result.done = 1;
nat_ping_result.success = success;
nat_ping_result.avg_rtt = avg_rtt;
nat_ping_result.count_sent = count_sent;
nat_ping_result.count_ok = count_ok;
- nat_ping_result.recv_ip = recv_ip;
- nat_ping_result.recv_port = recv_port;
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "nat_ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u recv_ip=%08x recv_port=%u",
- success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok,
- (unsigned)recv_ip, (unsigned)recv_port);
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "nat_ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u",
+ success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok);
}
int main(void) {
@@ -218,6 +212,9 @@ int main(void) {
goto cleanup;
}
+ // Разрешаем NAT check для localhost (для теста)
+ if (inst_s->bgp) route_bgp_set_nat_check_local(inst_s->bgp, 1);
+
if (init_connections(inst_s) != 0 || init_connections(inst_c1) != 0 || init_connections(inst_c2) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to init connections");
goto cleanup;
@@ -421,7 +418,9 @@ int main(void) {
}
memset(&nat_ping_result, 0, sizeof(nat_ping_result));
- int ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2, NODE_ID_C1,
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "TEST: sending ping with pubkey, nat_ip=0x%08x nat_port=%u",
+ link_sc1->nat_ip, link_sc1->nat_port);
+ int ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2,
link_sc1->nat_ip, link_sc1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL,
node_c1->node.public_key);
@@ -444,19 +443,7 @@ int main(void) {
nat_ping_result.success, (unsigned)nat_ping_result.count_ok);
goto cleanup;
}
- if (nat_ping_result.recv_ip == 0 || nat_ping_result.recv_port == 0) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: STUN recv_ip/port zero (ip=%08x port=%u)",
- (unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port);
- goto cleanup;
- }
- // recv_ip should match C2's socket address (127.0.0.1)
- if (nat_ping_result.recv_ip != 0x7F000001) {
- DEBUG_WARN(DEBUG_CATEGORY_BGP, "STUN recv_ip=%08x (expected 127.0.0.1), tolerating",
- (unsigned)nat_ping_result.recv_ip);
- }
-
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "STUN check PASSED: recv_ip=%08x recv_port=%u",
- (unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port);
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "STUN check PASSED");
// 7. Test route_ping_send_req_addr WITHOUT embedded pubkey (C2 should resolve pubkey from node_id)
// First wait for C2 to learn C1's nodeinfo via BGP exchange from S
@@ -477,7 +464,9 @@ int main(void) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Testing route ping without embedded pubkey...");
memset(&nat_ping_result, 0, sizeof(nat_ping_result));
- ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2, NODE_ID_C1,
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "TEST: sending ping NO pubkey, nat_ip=0x%08x nat_port=%u",
+ link_sc1->nat_ip, link_sc1->nat_port);
+ ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2,
link_sc1->nat_ip, link_sc1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL,
NULL);
@@ -500,10 +489,6 @@ int main(void) {
nat_ping_result.success, (unsigned)nat_ping_result.count_ok);
goto cleanup;
}
- if (nat_ping_result.recv_ip == 0 || nat_ping_result.recv_port == 0) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no-pubkey ping STUN fields zero");
- goto cleanup;
- }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "No-pubkey ping check PASSED");
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection test PASSED");
diff --git a/tests/test_route_ping.c b/tests/test_route_ping.c
index e726ba97..256273e7 100644
--- a/tests/test_route_ping.c
+++ b/tests/test_route_ping.c
@@ -177,17 +177,15 @@ static void test_timeout_cb(void* arg) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test_route_ping: overall test timeout");
}
-static void ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
- uint32_t recv_ip, uint16_t recv_port, void* arg) {
- (void)arg; (void)recv_ip; (void)recv_port;
+static void ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) {
+ (void)arg;
ping_result.done = 1;
ping_result.success = success;
ping_result.avg_rtt = avg_rtt;
ping_result.count_sent = count_sent;
ping_result.count_ok = count_ok;
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u recv_ip=%08x recv_port=%u",
- success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok,
- (unsigned)recv_ip, (unsigned)recv_port);
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u",
+ success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok);
}
int main(void) {
@@ -253,22 +251,42 @@ int main(void) {
goto cleanup;
}
- // 3. Send route ping request from A to B for node C
+ // 3. Get target info for C and send route ping request from A to B
struct ETCP_CONN* conn_ab = find_conn_to_peer(inst_a, NODE_ID_B);
if (!conn_ab) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "A has no connection to B");
goto cleanup;
}
- DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending route ping request A->B for C");
+
+ /* Получаем target IP:port из nodeinfo узла C */
+ uint32_t target_ip = 0;
+ uint16_t target_port = 0;
+ struct NODEINFO_Q* nq = inst_b->bgp ? route_bgp_get_node(inst_b->bgp, NODE_ID_C) : NULL;
+ if (nq) {
+ const struct NODEINFO_IPV4_SOCKET* sockets;
+ int sc = get_node_v4_sockets(nq, &sockets);
+ if (sc > 0) {
+ memcpy(&target_ip, sockets[0].addr, 4);
+ target_port = sockets[0].port;
+ }
+ }
+ if (target_ip == 0 || target_port == 0) {
+ DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot resolve target for node C");
+ goto cleanup;
+ }
+
+ DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending route ping request A->B for C (%s:%u)",
+ ip_to_str(&target_ip, AF_INET).str, (unsigned)target_port);
memset(&ping_result, 0, sizeof(ping_result));
- int ret = route_ping_send_req(inst_a->bgp, conn_ab, NODE_ID_C,
- 3, // count
- 10, // interval_ms
- 200, // timeout_ms
- 5000,// wait_timeout_ms
- ping_resp_cb, NULL);
+ int ret = route_ping_send_req_addr(inst_a->bgp, conn_ab, target_ip, target_port,
+ 3, // count
+ 10, // interval_ms
+ 200, // timeout_ms
+ 5000,// wait_timeout_ms
+ ping_resp_cb, NULL,
+ nq ? nq->node.public_key : NULL);
if (ret != 0) {
- DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req failed: %d", ret);
+ DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret);
goto cleanup;
}
diff --git a/tools/bping/bping b/tools/bping/bping
new file mode 100755
index 00000000..931e5744
Binary files /dev/null and b/tools/bping/bping differ
diff --git a/tools/proxy/proxy.sh b/tools/proxy/proxy.sh
new file mode 100755
index 00000000..62d49852
--- /dev/null
+++ b/tools/proxy/proxy.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+./udp_proxy --listen 192.168.29.117:1266 --target 192.168.29.117:1333
\ No newline at end of file
diff --git a/tools/proxy/udp_proxy b/tools/proxy/udp_proxy
new file mode 100755
index 00000000..075df5c8
Binary files /dev/null and b/tools/proxy/udp_proxy differ
diff --git a/utun.log1 b/utun.log1
new file mode 100644
index 00000000..3431e825
--- /dev/null
+++ b/utun.log1
@@ -0,0 +1,106 @@
+[22:59:46-599.960] [INFO] [BGP] (route_bgp.c:390) route_bgp_init() route_bgp_init: node_id=5f75c7445af88e1f
+[22:59:46-600.017] [INFO] [BGP] (route_bgp.c:418) route_bgp_init() BGP module initialized (with hop_list support)
+[22:59:46-600.022] [INFO] [BGP] (utun_instance.c:117) instance_init_common() BGP module initialized
+[22:59:46-600.042] [INFO] [GENERAL] (etcp_connections.c:506) etcp_socket_add() Listen socket initialized: name=lan1 fd=8 addr=192.168.29.117:1333
+[22:59:46-600.046] [INFO] [BGP] (etcp_connections.c:514) etcp_socket_add() Add Socket type=1
+[22:59:46-600.061] [INFO] [GENERAL] (etcp_connections.c:506) etcp_socket_add() Listen socket initialized: name=lo0_test fd=9 addr=127.0.0.1:1330
+[22:59:46-600.064] [INFO] [BGP] (etcp_connections.c:514) etcp_socket_add() Add Socket type=2
+[22:59:46-600.832] [ERROR] [ETCP] (utun.c:391) main() Run mainloop
+[23:00:02-217.693] [TRACE] [BGP] (route_bgp.c:378) route_bgp_etcp_conn_cbk() Set ETCP ready callback for connection 8E1F->???? []
+[23:00:02-217.724] [INFO] [GENERAL] (etcp_connections.c:948) etcp_connections_read_callback_socket() New connection received on socket lan1: log_name=8E1F->0C2D [] peer_id=7043239820550081581
+[23:00:02-217.784] [INFO] [GENERAL] (etcp_connections.c:1079) etcp_connections_read_callback_socket() Connection established: log_name=8E1F->0C2D [] socket=lan1 link_id=1 status=UP
+[23:00:02-217.792] [TRACE] [BGP] (route_bgp.c:359) route_bgp_on_conn_up() Connection 8E1F->0C2D [] on up
+[23:00:02-217.797] [INFO] [BGP] (route_bgp.c:493) route_bgp_new_conn() New connection added to senders_list
+[23:00:02-217.806] [INFO] [BGP] (route_bgp.c:111) route_bgp_send_table_request() Sent table request to 8E1F->0C2D [] (version=0)
+[23:00:04-223.725] [TRACE] [BGP] (route_bgp.c:368) route_bgp_on_conn_down() Connection 8E1F->0C2D [] on down
+[23:00:04-223.747] [INFO] [BGP] (route_bgp.c:528) route_bgp_remove_conn() route_bgp_remove_conn: peer=61be9d4cd3c60c2d
+[23:00:04-223.763] [INFO] [BGP] (route_bgp.c:549) route_bgp_remove_conn() Connection removed, routes updated via route_remove_conn
+[23:00:04-223.768] [INFO] [GENERAL] (etcp_connections.c:262) keepalive_timer_cb() Link down: log_name=8E1F->0C2D [] socket=lan1 link_id=1 status=DOWN
+[23:44:58-804.874] [INFO] [GENERAL] (etcp_connections.c:1079) etcp_connections_read_callback_socket() Connection established: log_name=8E1F->0C2D [] socket=lan1 link_id=1 status=UP
+[23:44:58-804.949] [TRACE] [BGP] (route_bgp.c:359) route_bgp_on_conn_up() Connection 8E1F->0C2D [] on up
+[23:44:58-804.956] [INFO] [BGP] (route_bgp.c:493) route_bgp_new_conn() New connection added to senders_list
+[23:44:58-804.963] [INFO] [BGP] (route_bgp.c:111) route_bgp_send_table_request() Sent table request to 8E1F->0C2D [] (version=0)
+[23:44:58-804.973] [TRACE] [BGP] (route_bgp.c:368) route_bgp_on_conn_down() Connection 8E1F->0C2D [] on down
+[23:44:58-804.978] [INFO] [BGP] (route_bgp.c:528) route_bgp_remove_conn() route_bgp_remove_conn: peer=61be9d4cd3c60c2d
+[23:44:58-804.984] [INFO] [BGP] (route_bgp.c:549) route_bgp_remove_conn() Connection removed, routes updated via route_remove_conn
+[23:44:58-804.988] [INFO] [GENERAL] (etcp_connections.c:262) keepalive_timer_cb() Link down: log_name=8E1F->0C2D [] socket=lan1 link_id=1 status=DOWN
+[23:44:58-809.776] [TRACE] [BGP] (route_bgp.c:359) route_bgp_on_conn_up() Connection 8E1F->0C2D [] on up
+[23:44:58-809.832] [INFO] [BGP] (route_bgp.c:493) route_bgp_new_conn() New connection added to senders_list
+[23:44:58-809.846] [INFO] [BGP] (route_bgp.c:111) route_bgp_send_table_request() Sent table request to 8E1F->0C2D [] (version=0)
+[23:44:58-810.212] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=6
+[23:44:58-810.260] [INFO] [BGP] (route_bgp.c:340) route_bgp_receive_cbk() Received table request from 8E1F->0C2D [] (version=0), sending full table
+[23:44:58-810.287] [INFO] [BGP] (route_bgp.c:78) route_bgp_send_route() Sending route 10.23.1.0/24, peer 8E1F->0C2D [] (ENTRY)
+[23:44:58-810.294] [INFO] [BGP] (route_bgp.c:126) route_bgp_send_full_table() Sent full routing table to 8E1F->0C2D [] (version=1)
+[23:44:58-814.981] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[23:44:58-815.036] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.5.0/24 from 61be9d4cd3c60c2d (hops=1)
+[23:44:58-815.071] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.5.0/24 exclude=0x56275e8e5d38 subcmd=1
+[23:44:58-815.076] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[23:44:58-815.080] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[23:44:58-815.085] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[23:44:58-815.089] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.6.0/24 from 61be9d4cd3c60c2d (hops=1)
+[23:44:58-815.093] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.6.0/24 exclude=0x56275e8e5d38 subcmd=1
+[23:44:58-815.097] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[23:44:58-815.100] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[23:44:58-815.213] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[23:44:58-815.220] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.5.0/24 from 61be9d4cd3c60c2d (hops=1)
+[23:44:58-815.224] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.5.0/24 exclude=0x56275e8e5d38 subcmd=2
+[23:44:58-815.228] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[23:44:58-815.231] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[23:44:58-815.235] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[23:44:58-815.239] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.6.0/24 from 61be9d4cd3c60c2d (hops=1)
+[23:44:58-815.242] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.6.0/24 exclude=0x56275e8e5d38 subcmd=2
+[23:44:58-815.246] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[23:44:58-815.249] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[00:40:35-599.488] [TRACE] [BGP] (route_bgp.c:368) route_bgp_on_conn_down() Connection 8E1F->0C2D [] on down
+[00:40:35-599.548] [INFO] [BGP] (route_bgp.c:528) route_bgp_remove_conn() route_bgp_remove_conn: peer=61be9d4cd3c60c2d
+[00:40:35-599.769] [INFO] [BGP] (route_bgp.c:166) route_bgp_broadcast_withdraw() route_bgp_broadcast_withdraw: node_id=61be9d4cd3c60c2d exclude=(nil)
+[00:40:35-599.795] [INFO] [BGP] (route_bgp.c:166) route_bgp_broadcast_withdraw() route_bgp_broadcast_withdraw: node_id=61be9d4cd3c60c2d exclude=(nil)
+[00:40:35-599.882] [INFO] [BGP] (route_bgp.c:549) route_bgp_remove_conn() Connection removed, routes updated via route_remove_conn
+[00:40:35-599.888] [INFO] [GENERAL] (etcp_connections.c:262) keepalive_timer_cb() Link down: log_name=8E1F->0C2D [] socket=lan1 link_id=1 status=DOWN
+[01:14:00-195.013] [TRACE] [BGP] (route_bgp.c:359) route_bgp_on_conn_up() Connection 8E1F->0C2D [] on up
+[01:14:00-195.072] [INFO] [BGP] (route_bgp.c:493) route_bgp_new_conn() New connection added to senders_list
+[01:14:00-195.084] [INFO] [BGP] (route_bgp.c:111) route_bgp_send_table_request() Sent table request to 8E1F->0C2D [] (version=0)
+[01:14:00-201.819] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=10
+[01:14:00-201.866] [INFO] [BGP] (route_bgp.c:329) route_bgp_receive_cbk() Received WITHDRAW for node 5f75c7445af88e1f
+[01:14:00-201.888] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=6
+[01:14:00-201.909] [INFO] [BGP] (route_bgp.c:340) route_bgp_receive_cbk() Received table request from 8E1F->0C2D [] (version=0), sending full table
+[01:14:00-201.920] [INFO] [BGP] (route_bgp.c:78) route_bgp_send_route() Sending route 10.23.1.0/24, peer 8E1F->0C2D [] (ENTRY)
+[01:14:00-201.924] [INFO] [BGP] (route_bgp.c:126) route_bgp_send_full_table() Sent full routing table to 8E1F->0C2D [] (version=1)
+[01:14:00-207.872] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[01:14:00-207.908] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.5.0/24 from 61be9d4cd3c60c2d (hops=1)
+[01:14:00-207.916] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.5.0/24 exclude=0x56275e8e5d38 subcmd=1
+[01:14:00-207.920] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[01:14:00-207.924] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[01:14:00-207.928] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=18
+[01:14:00-207.932] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.6.0/24 from 61be9d4cd3c60c2d (hops=1)
+[01:14:00-207.936] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.6.0/24 exclude=0x56275e8e5d38 subcmd=1
+[01:14:00-207.939] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 0 peers
+[01:14:00-207.943] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[02:29:31-196.879] [TRACE] [BGP] (route_bgp.c:378) route_bgp_etcp_conn_cbk() Set ETCP ready callback for connection 8E1F->???? []
+[02:29:31-196.922] [INFO] [GENERAL] (etcp_connections.c:948) etcp_connections_read_callback_socket() New connection received on socket lan1: log_name=8E1F->D894 [] peer_id=1206313594063214740
+[02:29:31-196.953] [INFO] [GENERAL] (etcp_connections.c:1079) etcp_connections_read_callback_socket() Connection established: log_name=8E1F->D894 [] socket=lan1 link_id=1 status=UP
+[02:29:31-196.959] [TRACE] [BGP] (route_bgp.c:359) route_bgp_on_conn_up() Connection 8E1F->D894 [] on up
+[02:29:31-196.964] [INFO] [BGP] (route_bgp.c:493) route_bgp_new_conn() New connection added to senders_list
+[02:29:31-196.971] [INFO] [BGP] (route_bgp.c:111) route_bgp_send_table_request() Sent table request to 8E1F->D894 [] (version=0)
+[02:29:31-235.679] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=10bdafd28ddcd894 len=18
+[02:29:31-235.721] [INFO] [BGP] (route_bgp.c:302) route_bgp_receive_cbk() Received route 10.23.0.0/24 from 10bdafd28ddcd894 (hops=1)
+[02:29:31-235.729] [INFO] [BGP] (route_bgp.c:144) route_bgp_broadcast_route() route_bgp_broadcast_route: network=10.23.0.0/24 exclude=0x562763e892e8 subcmd=1
+[02:29:31-235.738] [INFO] [BGP] (route_bgp.c:78) route_bgp_send_route() Sending route 10.23.0.0/24, peer 8E1F->0C2D [] (ENTRY)
+[02:29:31-235.743] [TRACE] [BGP] (route_bgp.c:157) route_bgp_broadcast_route() route_bgp_broadcast_route: sent to 1 peers
+[02:29:31-235.747] [TRACE] [BGP] (route_bgp.c:318) route_bgp_receive_cbk() route_bgp_receive_cbk: INSERTED
+[02:31:13-366.794] [TRACE] [BGP] (route_bgp.c:368) route_bgp_on_conn_down() Connection 8E1F->D894 [] on down
+[02:31:13-366.869] [INFO] [BGP] (route_bgp.c:528) route_bgp_remove_conn() route_bgp_remove_conn: peer=10bdafd28ddcd894
+[02:31:13-366.956] [INFO] [BGP] (route_bgp.c:166) route_bgp_broadcast_withdraw() route_bgp_broadcast_withdraw: node_id=10bdafd28ddcd894 exclude=(nil)
+[02:31:13-366.977] [INFO] [BGP] (route_bgp.c:549) route_bgp_remove_conn() Connection removed, routes updated via route_remove_conn
+[02:31:13-366.982] [INFO] [GENERAL] (etcp_connections.c:262) keepalive_timer_cb() Link down: log_name=8E1F->D894 [] socket=lan1 link_id=1 status=DOWN
+[02:31:13-374.249] [INFO] [BGP] (route_bgp.c:256) route_bgp_receive_cbk() route_bgp_receive_cbk: from=61be9d4cd3c60c2d len=10
+[02:31:13-374.306] [INFO] [BGP] (route_bgp.c:329) route_bgp_receive_cbk() Received WITHDRAW for node 0000000000000000
+[02:31:17-925.784] [ERROR] [CONNECTION] (etcp_connections.c:635) etcp_link_new() Can not insert link to socket
+[02:31:17-925.819] [ERROR] [CONNECTION] (etcp_connections.c:1002) etcp_connections_read_callback_socket() etcp_connections_read_callback: failed to create link for connection
+[02:31:17-925.824] [ERROR] [ETCP] (etcp_connections.c:1221) etcp_connections_read_callback_socket() etcp_connections_read_callback: error 66
+[02:31:17-968.183] [ERROR] [CONNECTION] (etcp_connections.c:635) etcp_link_new() Can not insert link to socket
+[02:31:17-968.247] [ERROR] [CONNECTION] (etcp_connections.c:1002) etcp_connections_read_callback_socket() etcp_connections_read_callback: failed to create link for connection
+[02:31:17-968.253] [ERROR] [ETCP] (etcp_connections.c:1221) etcp_connections_read_callback_socket() etcp_connections_read_callback: error 66
+[02:31:18-018.408] [ERROR] [CONNECTION] (etcp_connections.c:635) etcp_link_new() Can not insert link to socket
+[02:31:18-018.452] [ERROR] [CONNECTION] (etcp_connections.c:1002) etcp_connections_read_callback_socket() etcp_connections_read_callback: failed to create link for connection
+[02:31:18-018.457] [ERROR] [ETCP] (etcp_connections.c:1221) etcp_connections_read_callback_socket() etcp_connections_read_callback: error 66