Validate terminal call packets and document end acknowledgements

This commit is contained in:
evgeny committed 2026-10-15 12:26:40 +02:00
1 parent e6954b4152
commit 92f62a0121
2 files changed
+24 -8

No files matched your search

+18 -8
View File
@@ -17,7 +17,7 @@ DM-группа, маршрутизация через `etcp_router` идент
| Файл | Язык | Сборки | Назначение |
|---|---|---|---|
| `src/call/call_proto.h` | C | везде | wire-формат, subcmd, svc_id `ETCP_RT_ID_CALL 0x34` |
| `src/call/call_proto.h` | C | везде | wire-формат, subcmd, svc_id `ETCP_RT_ID_CALL 0x36` |
| `src/call/call.h` / `call.c` | C | везде (autotools + GUI) | сигналинг, реестр сессий, таймеры, watchdog (20с по приёму), релей медиа с backpressure, счётчики |
| `src/call/call_audio.h/c` | C | только GUI-сборки | Opus encode/decode, фрейминг, pull-обвязка с тонами; регистрация через `call_audio_ops` |
| `src/call/call_tones.h/c` | C | только GUI-сборки | генератор тонов (glitch/ended), синус |
@@ -28,19 +28,20 @@ Autotools-сборка демона остаётся C-only (`.cpp` не ком
Диагностика: категория `call` (`DEBUG_CATEGORY_CALL`); транспортная доставка — `etcp_route`.
## 3. Протокол (svc_id 0x34, первый байт payload = subcmd)
## 3. Протокол (сигналинг svc_id 0x36, первый байт payload = subcmd)
Формат приёма (как media_delivery, одноуровневый): `entry->dgram[0]=svc_id`,
`entry->dgram[1]=subcmd`, `entry->dgram[2..]=payload`.
Формат приёма: `entry->dgram[0]=svc_id`, служебный заголовок роутера до
`ROUTER_SVC_PAYLOAD_OFF=26`, затем subcmd и тело команды.
```
CALL_INVITE 0x01 {call_id:8, group_id:8, tracks:[{type:AUDIO, codec:OPUS, sr:4, ch:1, bitrate:4}]}
CALL_RINGING 0x02 {call_id:8}
CALL_ACCEPT 0x03 {call_id:8, agreed_tracks}
CALL_ACCEPT 0x03 {call_id:8}
CALL_DECLINE 0x04 {call_id:8, reason:1}
CALL_BUSY 0x05 {call_id:8}
CALL_CANCEL 0x06 {call_id:8}
CALL_HANGUP 0x07 {call_id:8, reason:1}
CALL_END_ACK 0x08 {call_id:8}
CALL_MEDIA 0x10 {call_id:8, track_id:1, seq:2, ts_ms:4, capture_pts_us:8, capture_epoch:8, codec_delay_samples:4, payload}
```
@@ -75,9 +76,18 @@ NO_TRAFFIC / RING_TIMEOUT / CONNECT_FAIL`.
- `CONN_EVENT_DOWN/TIMEOUT` CM не завершают звонок: роутер использует доступные пути;
- **NO_TRAFFIC 20с** — нет входящих медиа-кадров → авто HANGUP.
Завершающие кодограммы (HANGUP/CANCEL/DECLINE) шлются без ретрансмиссий — потеря
компенсируется watchdog'ом 2с у вызываемого. Запоздалые кодограммы вызова при
идущем разговоре (`ACTIVE`) игнорируются.
Завершающие кодограммы HANGUP/CANCEL/DECLINE повторяются каждые 300мс до END_ACK,
не дольше 20с. Это необходимо при смене эпохи роутера: старые очереди удаляются,
даже если отправитель ранее получил успешный результат постановки команды в очередь.
Медиа и их локальные очереди освобождаются сразу, ENDED публикуется один раз.
В реестре до ACK/таймаута остаётся только завершённая сессия с CM handle и таймером.
Получатель подтверждает также повторные команды после удаления сессии, без повторного ENDED.
Команда и ACK проверяются по подписи, шифрованию, размеру, call_id, пиру и группе существующей сессии.
ACK не подтверждается. Новый звонок имеет другой call_id и не затрагивается поздними командами старого.
При одновременном завершении стороны независимо подтверждают команды друг друга.
После ACK сессия удаляется на ближайшем tick завершения; при удалённом завершении CM удерживается
ещё 500мс для отправки ACK. Остановка ядра отменяет таймер без повторных отправок.
Запоздалые кодограммы вызова при идущем разговоре (`ACTIVE`) игнорируются.
Онлайн-проверка: `topo_node_find_by_id(group, peer) == NULL` → «не в сети» (event).
+6
View File
@@ -724,6 +724,12 @@ static void call_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
int terminal = subcmd == CALL_SUBCMD_HANGUP || subcmd == CALL_SUBCMD_CANCEL || subcmd == CALL_SUBCMD_DECLINE;
if (terminal || subcmd == CALL_SUBCMD_END_ACK) {
uint8_t required = ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED;
if ((d[ROUTER_SVC_FLAGS_OFF] & required) != required) {
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call end unauthenticated command=%s peer=%016llx flags=%02x",
call_subcmd_name(subcmd), (unsigned long long)from, d[ROUTER_SVC_FLAGS_OFF]);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
size_t expected = subcmd == CALL_SUBCMD_CANCEL || subcmd == CALL_SUBCMD_END_ACK ? 9 : 10;
if (blen != expected) {
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call end invalid packet command=%s bytes=%zu expected=%zu",