Files
utun2/src/utun_instance.h
T
evgeny f521bb37f4 fix(call): align audio frontend ownership with core lifecycle
Keep the borrowed audio provider on UTUN_INSTANCE across chat service restarts.
Attach and detach Android voice from the core worker and reject DSP startup
after detach. Preserve TX owner epochs, pending slots and foreign-instance RX
isolation. Remove the unused cross-thread online API and keep its check local
to call.c.

Add eight lifecycle regressions with real cores and the Android host bridge.
Audit public call, DSP, clock, headless and desktop audio contracts and record
the findings and validation scope.

Validation: voice/desktop 38/38, four native call suites, native and both Android
ABI builds, full check 204/204 plus proxy/burst/load. JVM results remain Gradle
UP-TO-DATE (155 app and 15 router). No hardware runtime validation.
2026-10-26 20:22:46 +01:00

387 lines
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* utun_instance — владелец ядра и сервисов одного узла.
* Ядро содержит транспорт, маршрутизатор, идентичность и общую БД.
* create → core_start; UTUN и чат запускаются отдельно через service_start/stop.
* Все операции — в потоке переданного UASYNC; stop/reload/destroy вне callbacks останавливаемых модулей.
* Цикл uasync_poll запускает вызывающий; один UASYNC можно разделять между несколькими instance.
* destroy останавливает сервисы и освобождает ядро, но не UASYNC. Подробности: utun_instance_doc.md. */
#ifndef UTUN_INSTANCE_H
#define UTUN_INSTANCE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "../lib/memory_pool.h"
#include "../lib/sqlite3.h"
#include "secure_channel.h"
#include "etcp_padding.h"
#include "reality_io.h"
#include "etcp_api.h"
#include "transport_layer/etcp_tx_stats.h"
#include "config_parser.h"
#include "stcp_link.h"
#include "firewall.h"
#include "eim_nat.h"
#include "nat_transport.h"
#include "media_delivery/media_delivery.h"
#include "media_async/media_async.h"
#include "proxy/tcp_proxy_client.h"
#include "etcp_router.h"
#include "proxy/tcp_proxy_server.h"
#include "ntp_time.h"
#include "ntp_node_time.h"
#include "topo_group.h"
#include "chat/chat_setting.h"
#include "chat/chat_event.h"
// Forward declarations
struct utun_config;
struct chat_core_ctx;
struct chat_sync;
struct join_key_entry; /* chat_join.c */
struct tgi_pending; /* routing_layer/topo_group_invite.c */
struct dm_state;
struct dm_mb_state;
struct dm_media_state;
struct file_transfer;
struct call_ctx; /* call/call.c */
struct call_audio_ops; /* Заимствованный frontend аудиозвонка; привязка живёт до detach ядра. */
struct radio_instance; /* radio/radio.c */
struct radio_call_context; /* radio/radio_call.c */
struct radio_headless; /* radio/radio_headless.c */
struct stream_service; /* stream/stream.c — подписки и публикация данных */
struct multicast_ctx; /* routing_layer/multicast.c — общие FIFO и отчёты потерь */
struct uasync_s;
struct ROUTE_TABLE;
struct ETCP_CONN;
typedef void (*etcp_new_conn_fn)(struct ETCP_CONN* conn, void* arg);
struct ETCP_SOCKET;
struct NODE_CONN_DIRECT;
struct tun_if;
struct ETCP_BINDINGS;
struct ETCP_ROUTER_BINDINGS;
struct TOPO_GROUP;
struct TOPO_GROUPS;
struct control_server;
struct PING_CONTEXT;
struct CONN_MGR;
struct DB_SYNC;
struct NAT_DETECTION;
struct STUN_NAT;
struct merkle_sync;
struct NETWORK_ENTRY {
uint64_t id; // 56-битный ID сети; первые 7 байт — ключ индекса
uint8_t pubkey[SC_PUBKEY_SIZE]; // бинарный публичный ключ сети
uint8_t signing_key[SC_PRIVKEY_SIZE]; // бинарный ключ подписи сети
char name[64]; // имя [network:name], ASCIIZ
};
// Queue entry for instance->connections (indexed by peer_node_id)
struct conn_queue_entry {
uint64_t peer_node_id; // ключ индекса, 0 до определения пира
struct ETCP_CONN* conn; // живой транспорт; удаляется при close
};
/* Постоянный транспорт ядра к пиру [client]; UTUN приобретает отдельный групповой запрос.
* config/links принадлежат записи; привязка локального сокета хранится именем, без ссылок на старый конфиг. */
struct CONFIG_CONN_HANDLE {
uint64_t node_id;
struct UTUN_INSTANCE* instance; // заимствован до закрытия записи
struct CFG_CLIENT config; // собственная копия параметров и списка links
struct NODE_CONN_DIRECT* transport; // принадлежит ядру, сохраняется при остановке UTUN
struct TOPO_PEER_REQUEST* request; // принадлежит UTUN; NULL, когда сервис выключен
void* retry_timer; // повтор после окончательного CLOSED; отменяется перед освобождением записи
struct CONFIG_CONN_HANDLE* next;
};
// Queue entry for instance->tcp_connections (indexed by node_id)
struct tcp_conn_entry {
uint64_t node_id; // index key
struct stcp_link* link; // active TCP link
struct ETCP_CONN* etcp_conn; // = stcp_link_get_etcp_conn(link) — for etcp_send() compat
};
// Подписка на смену active mode (client_activity). Событие рассылается
// подписчикам (topo_group, keepalive и т.д.) при каждом изменении активности.
typedef void (*utun_activity_cbk_fn)(struct UTUN_INSTANCE* instance, int active, void* arg);
struct utun_activity_cbk_entry {
utun_activity_cbk_fn fn; // синхронный обработчик эффективной активности
void* arg; // заимствованный контекст обработчика
struct utun_activity_cbk_entry* next; // следующая подписка
uint8_t removed; // tombstone до выхода из внешней рассылки
};
// Подписка на смену спячки пира (keepalive-протокол). Сервер (или любой узел)
// получает от мобильного пира sleep-анонс и уведомляет подписчиков:
// sleeping=1 — пир ушёл в спячку (в фон), 0 — проснулся (дедлайн истёк / обычный keepalive).
typedef void (*peer_sleep_cbk_fn)(struct UTUN_INSTANCE* instance, uint64_t peer_node_id, int sleeping, void* arg);
struct peer_sleep_cbk_entry {
peer_sleep_cbk_fn fn; // синхронный обработчик sleep/wake пира
void* arg; // заимствованный контекст обработчика
struct peer_sleep_cbk_entry* next; // следующая подписка
uint8_t removed; // снятая подписка не вызывается, память удерживает текущая рассылка
};
// Состояние одного узла. Вложенные модули освобождаются через их lifecycle API.
struct UTUN_INSTANCE {
struct etcp_tx_history tx_history; // секундное окно ACK всех транспортов ядра
struct reality_owner reality_owner; // relay и replay-кеш REALITY данного ядра
uint8_t core_started, utun_started, chat_started; // успешный запуск ядра / UTUN / чата
uint8_t destroying; // запрещает повторный запуск ядра/повторное destroy во время очистки
// Identification
char name[MAX_CONN_NAME_LEN]; // Instance name from config
// Конфигурация принадлежит экземпляру после успешного create.
struct utun_config *config;
// TUN interface
struct tun_if* tun;
// Принадлежащие UTUN системные маршруты; независимы от изменений config.
struct tun_route* tun_routes; // только успешно установленные системные маршруты UTUN
struct ROUTE_TABLE* rt; // таблица маршрутов подсетей
struct TOPO_GROUPS* topo_groups; // Groups module for topology exchange
sqlite3* topo_sqlite_db; // собственная общая SQLite; открывается при create с топологией
struct NAT_DETECTION* nat_det; // NAT detection module
struct STUN_NAT* stun_nat; // независимая локальная STUN-диагностика UDP
struct stun_ports* stun_port_history; // ранее использованные STUN-порты; переживает reload config
struct merkle_sync* msync; // Merkle tree sync module (per-instance)
// Identification
uint64_t node_id;
struct SC_MYKEYS my_keys;
uint8_t my_ed25519_pubkey[SC_PUBKEY_SIZE]; // свой Ed25519 pubkey (для подписи метрик)
uint8_t my_ed25519_privkey[SC_PRIVKEY_SIZE]; // свой Ed25519 privkey (derived from X25519)
uint8_t next_socket_id; // начало поиска свободного ID 0..255; поколение различает повторное использование
// Заимствованный цикл событий: создаёт и уничтожает вызывающий.
struct UASYNC* ua;
// State
int running; // продолжать цикл приложения; готовность задают отдельные *_started
// Connections (очередь всех подключений для instance)
struct ll_queue* connections; // indexed by peer_node_id (0=pending), data=conn_queue_entry
struct ll_queue* tcp_connections; // старый TCP-реестр tcp_conn_entry, fallback instance_find_conn; обычный STCP живёт в links
// Callback chain for new ETCP connections
struct etcp_inst_cbk_entry* new_conn_cbks;
struct etcp_link_status_cbk_entry* link_status_cbks;
struct etcp_status_cbk_entry* conn_status_cbks; // instance-level: NEW/UP/DOWN/DELETE
struct etcp_socket_cbk_entry* socket_cbks; // instance-level: socket ADDR/STATUS changes
unsigned etcp_callbacks_depth; // Вложенность ETCP-рассылок: снятые подписки освобождаются при выходе из внешней
struct nodeinfo_cbk_entry* nodeinfo_cbks; // глобальная подписка на изменения nodeinfo
void* test_user_ptr; // Generic user pointer (used by tests)
struct memory_pool* data_pool;// для входных-выходных данных пакета
struct memory_pool* pkt_pool; // ETCP_DGRAM и буферы сетевых пакетов
struct memory_pool* ack_pool; // ACK_PACKET
// Active sockets (UDP + TCP, is_tcp=1 flag)
struct ETCP_SOCKET* etcp_sockets;// linked-list (UDP + TCP)
struct stcp_server *stcp_servers; // TCP servers linked list (via stcp_link.c)
void* socket_monitor; // SOCKET_MONITOR* (opaque, transport_layer/socket_monitor.c)
void* auto_socket_state; // AUTO_SOCKET* (opaque, transport_layer/auto_socket.c)
uint8_t auto_socket_enabled; // 1 = auto_sockets enabled from config
// Pending one-shot pings (for callback on PONG or timeout)
struct PING_CONTEXT* pending_pings;
struct tgi_pending* group_invites; // запросы описаний каналов; отменяются при stop чата/ядра
struct ETCP_PADDING padding; // диапазон и таймер PING/PONG без ETCP_CONN
// Routing statistics
uint64_t routed_packets;
uint64_t dropped_packets;
// ETCP API bindings (per-instance)
struct ETCP_BINDINGS api_bindings;
// Control server for monitoring
struct control_server* control_srv;
// Firewall
struct firewall_ctx fw;
// EIM NAT
struct eim_nat_ctx nat;
struct nat_transport_ctx nat_tr;
// Media delivery
struct media_delivery_ctx md;
// Media async engine (thread-per-task for crypto/file ops)
struct media_async* media_async;
// Последний результат init/reload endpoint: 0=успех, -1=ошибка хотя бы одного обязательного endpoint.
int socket_init_status;
// TCP proxy client (optional, NULL if not enabled)
struct tcp_proxy_client* tcp_proxy_client;
// etcp_router bindings и seq-connections (per-instance service routing)
struct ETCP_ROUTER_BINDINGS router_bindings;
struct ll_queue* router_conns;
struct multicast_ctx* multicast; // принадлежит ядру, общий для всех topo-групп
uint64_t router_loop_drops; // пакеты, вернувшиеся в уже посещённый узел
uint64_t router_path_errors; // некорректный или переполненный список пройденных узлов
struct CONN_MGR* conn_mgr; // заимствован у UTUN-группы; NULL при остановленном UTUN
struct DB_SYNC* db_sync; // Distributed DB sync (может быть NULL)
const struct call_audio_ops* call_audio_ops; // Регистрация frontend, только uasync; сохраняется при stop/start чата.
// Контексты чат-сервиса; освобождаются при chat_service_stop.
struct chat_core_ctx* chat_core; // каналы, сообщения и доступ к общей БД
struct local_transport* local_transport; // отдельный открытый LAN UDP, принадлежит чат-сервису
struct chat_sync* chat_sync; // join и синхронизация каналов
struct join_key_entry* join_keys; // зарегистрированные ключи приглашений
struct chat_join_registration* join_registrations; // ожидания KEY_REGISTER_ACK
int join_stopping; // запрещает новые регистрации при teardown
struct chat_invite_build_req* invite_gui_request; // текущее построение ссылки для GUI
struct dm_state* dm; // личные сообщения
struct dm_mb_state* dm_mailbox; // хранилище личных сообщений для получателей
struct dm_media_state* dm_media; // собственные файлы и промежуточное ciphertext-хранилище DM
struct file_transfer* file_transfer; // общий движок передачи файлов с CM-владением
struct member_files* member_files; // файловый сервис участников CHAT-групп
struct call_ctx* call; // call/call.c — P2P audio call
struct radio_call_context* radio_call; // адресные приглашения в рацию, принадлежит чат-сервису
void* call_audio; // call/call_headless.c — audio stream socket
struct radio_instance* radio; // radio/radio.c — PTT walkie-talkie (frame cb + counters)
struct radio_headless* radio_headless; // radio/radio_headless.c — audio stream socket
struct stream_service* stream; // живёт в чат-сервисе, независимо от UTUN
struct chat_setting_state chat_settings; // текущие настройки чата
chat_event_handler_fn chat_event_handler; // получатель событий чата
void* headless; // контекст TCP-управления headless-чатом
// E2E encryption cache — per-peer sc_context_t with derived session key
#define E2E_CTX_CACHE_SIZE 8
struct { uint64_t peer_node_id; struct secure_channel ctx; uint8_t valid; } e2e_ctx_cache[E2E_CTX_CACHE_SIZE];
struct broadcast_ctx* broadcast; // Broadcast protocol instance handler (ETCP_ID_BROADCAST dispatcher)
uint8_t client_type; // CLIENT_TYPE_SERVER/DESKTOP/MOBILE (from config)
uint16_t keepalive_interval; // желаемый keepalive (ms), из конфига. для handshake
uint8_t client_activity; // CLIENT_ACTIVITY_STANDBY/ACTIVE
uint8_t client_activity_requested; // режим frontend; stream owners могут временно удерживать эффективный ACTIVE
uint8_t phone_state; // 0=неизвестно, 1=свободен, 2=занят; живое состояние ОС, не сохраняется
uint8_t incoming_call_policy; // CALL_POLICY_*: независимые настройки публикации и допуска входящих
struct utun_activity_cbk_entry* activity_cbks; // подписки на смену client_activity
struct peer_sleep_cbk_entry* peer_sleep_cbks; // подписки на смену спячки пира (keepalive)
unsigned lifecycle_callbacks_depth; // общая вложенность рассылок activity/peer_sleep
uint64_t activity_version; // смена эффективной активности отменяет остаток более старой рассылки
// TCP proxy server (exit node)
struct tcp_proxy_server tcp_proxy_server;
struct udp_proxy_ctx* udp_proxy;
struct icmp_proxy_ctx* icmp_proxy;
// Networks (queue of NETWORK_ENTRY, indexed by 56-bit id)
struct ll_queue* networks;
uint32_t etcp_connect_timeout_tb; // Initial timeout in 0.1ms units, default 20000 (2s)
// NTP time synchronization
struct NTP_TIME ntp;
struct NTP_NODE_TIME ntp_node;
// Постоянные транспорты [client] ядра и независимые запросы участия в UTUN.
struct CONFIG_CONN_HANDLE* config_conn_handles;
// Per-instance NCD state (replaces global statics in node_conn_direct.c)
void* ncd_registry; // struct ncd_entry* linked list
uint8_t ncd_control_bound; // 1 = etcp_bind(ETCP_ID_NCD_CONTROL) сделан
};
/* Создать ядро: ua заимствован, config_file — обязательный путь. Файл может создаваться/нормализоваться updater.
* Уже открывает listeners, SQLite/топологию, router/proxy и мониторинг; core/UTUN/chat ещё не запущены.
* Возвращает instance либо NULL с освобождением частично созданных ресурсов. */
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file);
/* Как create, но без updater и файлового parse: config должен содержать корректные ключи.
* Успех передаёт владение config ядру; при NULL config остаётся caller (node_id мог быть исправлен).
* debug меняет общий logger процесса даже при отказе; log_file выбирается только до открытия файлового logger. */
struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config);
/* Как create из ASCIIZ config_text; строка заимствована лишь на вызов. Использует mkstemp в /tmp,
* updater и файловый parse, затем unlink. Нужна доступная ФС; ошибки дают NULL. */
struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text);
/* Остановить сервисы/транспорт и освободить instance/config; NULL допустим. UASYNC остаётся caller.
* Выполняет drain отложенных callbacks и join media workers; вызывать вне callbacks и без параллельных обращений.
* Заимствованные SQLite statements/backup должны быть закрыты caller; BUSY логируется и переводится в close_v2. */
void utun_instance_destroy(struct UTUN_INSTANCE* instance);
/* Запустить репликацию, NCD/keepalive, [client], control и время; без вступления в группы. Первый успех: running=core_started=1.
* При core_started возвращает 0 без изменения running. Ошибка — -1; подготовленные ресурсы остаются для retry/destroy.
* Отказ NTP/STUN не фатален. NULL/разрушаемый instance отклоняется. */
int utun_core_start(struct UTUN_INSTANCE* instance);
/* Запустить UTUN-группу, TUN/NAT по конфигу и отдельные запросы к [client]. Нужны core_start и топология.
* 0 — успех/уже запущен; -1 — откат ресурсов UTUN, ядро и чат сохраняются. */
int utun_service_start(struct UTUN_INSTANCE* instance);
/* Остановить UTUN, сохранив ядро и чат. Повтор допустим. */
void utun_service_stop(struct UTUN_INSTANCE* instance);
/* Последовательно core_start → UTUN (при наличии топологии) → чат (chatserver_enabled).
* 0/-1; ошибка позднего сервиса сохраняет ранее запущенные сервисы. */
int utun_instance_init(struct UTUN_INSTANCE *instance);
/* Перечитать config_file через updater; ua должен совпадать с instance->ua. Всегда использовать результат.
* Ошибка подготовки сохраняет прежний указатель. Смена негорячих global/подсетей либо automatic servers
* пересоздаёт ядро с прежними core/UTUN/chat/running; повторные server-имена также требуют restart.
* Ошибка после destroy возвращает NULL.
* Ручные endpoints обновляются по имени и всем параметрам, неизменённые сохраняются (включая port=0).
* Частичный reload не транзакционный: отказ endpoint логируется, новый config сохраняется для следующего retry. */
struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struct UASYNC *ua, const char *config_file);
/* Снять running и разбудить цикл; ресурсы освобождает destroy(), а не stop(). */
void utun_instance_stop(struct UTUN_INSTANCE *instance);
/* Общий переключатель TUN (по умолчанию 0): влияет на последующий UTUN start и proxy test mode при create. */
void utun_instance_set_tun_init_enabled(int enabled);
/* Общий переключатель топологии (по умолчанию 1): влияет на последующие create, включая открытие SQLite. */
void utun_instance_set_topo_group_enabled(int enabled);
/* Сохраняет желаемый режим frontend; stream owners удерживают ACTIVE до последнего close.
* Применяет эффективную активность и мобильный standby до уведомления подписчиков; выполнять в uasync-потоке.
* Повторная команда согласует фактический режим без повторного BGP-события. SERVER — успешный no-op.
* 0 — успех, -1 — NULL/ошибка standby с откатом режима. phone_state не меняется. */
int utun_set_client_activity(struct UTUN_INSTANCE* instance, int active);
/* Подписки уникальны по fn+arg, новые идут в голову; arg заимствован. NULL instance/fn — no-op,
* OOM логируется без добавления. Remove из callback безопасен; память удерживается до конца внешней рассылки.
* Новая подписка не получает уже начатое событие, но участвует во вложенном. Destroy внутри callback запрещён. */
void utun_add_activity_cbk(struct UTUN_INSTANCE* instance, utun_activity_cbk_fn fn, void* arg);
/* Удалить подписку fn+arg; отсутствие — no-op. Вложенная новая активность отменяет остаток старого события. */
void utun_remove_activity_cbk(struct UTUN_INSTANCE* instance, utun_activity_cbk_fn fn, void* arg);
/* Аналогичная подписка на peer_node_id/sleeping; nesting исполняется синхронно до продолжения внешней рассылки. */
void utun_add_peer_sleep_cbk(struct UTUN_INSTANCE* instance, peer_sleep_cbk_fn fn, void* arg);
/* Удалить peer-sleep подписку fn+arg; отсутствие/NULL — no-op. */
void utun_remove_peer_sleep_cbk(struct UTUN_INSTANCE* instance, peer_sleep_cbk_fn fn, void* arg);
/* Синхронно передать подписчикам node_id и sleeping (1 сон, 0 пробуждение); NULL instance — no-op. */
void utun_fire_peer_sleep_cbk(struct UTUN_INSTANCE* instance, uint64_t peer_node_id, int sleeping);
/* Вывести sys/info счётчики ресурсов и простые sanity-проверки списков с меткой phase.
* Живые ресурсы сами по себе не означают утечку; функция ничего не освобождает. */
void utun_instance_diagnose_leaks(struct UTUN_INSTANCE* instance, const char* phase);
/* Найти транспорт по node_id в реестрах ETCP/TCP. Указатель заимствован.
* NULL — не найден; ненулевой результат не гарантирует UP или готовность группы. */
static inline struct ETCP_CONN* instance_find_conn(struct UTUN_INSTANCE* inst, uint64_t node_id) {
if (!inst) return NULL;
if (inst->connections) {
struct ll_entry* e = queue_find_data_by_index(inst->connections, (const uint8_t*)&node_id);
if (e) return ((struct conn_queue_entry*)e->data)->conn;
}
if (inst->tcp_connections) {
struct ll_entry* e = queue_find_data_by_index(inst->tcp_connections, (const uint8_t*)&node_id);
if (e) return ((struct tcp_conn_entry*)e->data)->etcp_conn;
}
return NULL;
}
#ifdef __cplusplus
}
#endif
#endif // UTUN_INSTANCE_H