call: ретрансмит INVITE каждые 2с + fail-fast при отсутствии маршрута (6с)
This commit is contained in:
1 parent
93becd36e6
commit
0e625da109
8 files changed
+128
-20
No files matched your search
+54
-13
@@ -57,6 +57,8 @@ struct call_session {
|
||||
struct CONN_MGR_HANDLE* cm_handle;
|
||||
struct ETCP_CONN* data_conn; /* прямой data-коннект звонка (NULL для INDIRECT) */
|
||||
void* ring_timer; /* caller: 45с ожидания ответа */
|
||||
void* invite_timer; /* caller: периодический ретрансмит INVITE (до RINGING) */
|
||||
uint8_t no_route_strikes; /* caller: подряд ретрансмитов без маршрута (fail-fast) */
|
||||
void* traffic_timer; /* ACTIVE: 20с без медиа (запускается по первому медиа) */
|
||||
void* teardown_timer; /* отложенный разрыв conn_mgr (500мс после завершения) */
|
||||
void* stats_timer; /* ACTIVE: раз в 1с — CALL_STATS (rtt/buffer/tempo) */
|
||||
@@ -136,6 +138,22 @@ static int call_send(struct call_ctx* ctx, uint64_t group_id, uint64_t dst,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Собрать и отправить INVITE для сессии вызывающего. */
|
||||
static int call_send_invite(struct call_ctx* ctx, struct call_session* s) {
|
||||
uint8_t buf[sizeof(struct call_pkt_invite)];
|
||||
struct call_pkt_invite* inv = (struct call_pkt_invite*)buf;
|
||||
inv->subcmd = CALL_SUBCMD_INVITE;
|
||||
inv->call_id = s->call_id;
|
||||
inv->group_id = s->group_id;
|
||||
inv->num_tracks = 1;
|
||||
inv->tracks[0].type = CALL_TRACK_AUDIO;
|
||||
inv->tracks[0].codec = CALL_CODEC_OPUS;
|
||||
inv->tracks[0].sample_rate = CALL_OPUS_SAMPLE_RATE;
|
||||
inv->tracks[0].channels = 1;
|
||||
inv->tracks[0].bitrate = 32000;
|
||||
return call_send(ctx, s->group_id, s->peer_node_id, buf, sizeof(buf), ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT);
|
||||
}
|
||||
|
||||
/* ─────────────────── реестр сессий ─────────────────── */
|
||||
|
||||
static struct call_session* call_session_find(struct call_ctx* ctx, uint64_t call_id) {
|
||||
@@ -289,6 +307,7 @@ static void call_end(struct call_ctx* ctx, struct call_session* s, uint8_t reaso
|
||||
ctx->c_calls_ended++;
|
||||
|
||||
if (s->ring_timer) { uasync_cancel_timeout(ctx->inst->ua, s->ring_timer); s->ring_timer = NULL; }
|
||||
if (s->invite_timer) { uasync_cancel_timeout(ctx->inst->ua, s->invite_timer); s->invite_timer = NULL; }
|
||||
if (s->traffic_timer) { uasync_cancel_timeout(ctx->inst->ua, s->traffic_timer); s->traffic_timer = NULL; }
|
||||
if (s->stats_timer) { uasync_cancel_timeout(ctx->inst->ua, s->stats_timer); s->stats_timer = NULL; }
|
||||
|
||||
@@ -363,6 +382,37 @@ static void call_ring_timeout_cb(void* arg) {
|
||||
call_end(ctx, s, CALL_REASON_RING_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Периодический ретрансмит INVITE (caller, до RINGING). Покрывает потерю INVITE/RINGING
|
||||
* и появление маршрута позже. При стойком отсутствии маршрута — fail-fast CONNECT_FAIL. */
|
||||
static void call_invite_retry_cb(void* arg) {
|
||||
struct call_session* s = (struct call_session*)arg;
|
||||
if (!s || !s->ctx) return;
|
||||
struct call_ctx* ctx = s->ctx;
|
||||
s->invite_timer = NULL;
|
||||
if (s->state == CALL_ST_ENDED) return;
|
||||
if (s->state != CALL_ST_OUTGOING) return; /* RINGING — INVITE доставлен, ретрансмит не нужен */
|
||||
|
||||
if (etcp_router_has_route(ctx->inst, s->group_id, s->peer_node_id)) {
|
||||
s->no_route_strikes = 0;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "%s: INVITE retry id=%016llx (route ok)", CALL_ID, (unsigned long long)s->call_id);
|
||||
call_send_invite(ctx, s);
|
||||
} else if (++s->no_route_strikes >= CALL_NO_ROUTE_MAX_STRIKES) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "%s: no route after %d retries id=%016llx -> CONNECT_FAIL",
|
||||
CALL_ID, s->no_route_strikes, (unsigned long long)s->call_id);
|
||||
uint8_t buf[9];
|
||||
memcpy(buf, &s->call_id, 8);
|
||||
buf[8] = 2; /* err: peer unreachable (no route) */
|
||||
chat_event_post(ctx->inst, CHAT_EVT_CALL_ERROR, buf, sizeof(buf));
|
||||
call_end(ctx, s, CALL_REASON_CONNECT_FAIL);
|
||||
return;
|
||||
} else {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "%s: INVITE retry id=%016llx no route (strike %d/%d)",
|
||||
CALL_ID, (unsigned long long)s->call_id, s->no_route_strikes, CALL_NO_ROUTE_MAX_STRIKES);
|
||||
}
|
||||
|
||||
s->invite_timer = uasync_set_timeout(ctx->inst->ua, CALL_INVITE_RETRY_TB, s, call_invite_retry_cb, "call_invite_retry");
|
||||
}
|
||||
|
||||
static void call_traffic_timeout_cb(void* arg) {
|
||||
struct call_session* s = (struct call_session*)arg;
|
||||
if (!s || !s->ctx) return;
|
||||
@@ -519,6 +569,7 @@ static void call_handle_ringing(struct call_ctx* ctx, uint64_t from, const uint8
|
||||
if (!s || s->role != CALL_ROLE_CALLER || s->state == CALL_ST_ENDED) return;
|
||||
if (s->state == CALL_ST_OUTGOING) {
|
||||
s->state = CALL_ST_RINGING;
|
||||
if (s->invite_timer) { uasync_cancel_timeout(ctx->inst->ua, s->invite_timer); s->invite_timer = NULL; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: RINGING id=%016llx", CALL_ID, (unsigned long long)call_id);
|
||||
call_event_id(ctx, CHAT_EVT_CALL_RINGING, call_id, 0);
|
||||
}
|
||||
@@ -692,6 +743,7 @@ void call_destroy(struct UTUN_INSTANCE* inst) {
|
||||
struct call_session* s = (struct call_session*)e->data;
|
||||
s->state = CALL_ST_ENDED;
|
||||
if (s->ring_timer) { uasync_cancel_timeout(inst->ua, s->ring_timer); s->ring_timer = NULL; }
|
||||
if (s->invite_timer) { uasync_cancel_timeout(inst->ua, s->invite_timer); s->invite_timer = NULL; }
|
||||
if (s->traffic_timer) { uasync_cancel_timeout(inst->ua, s->traffic_timer); s->traffic_timer = NULL; }
|
||||
if (s->stats_timer) { uasync_cancel_timeout(inst->ua, s->stats_timer); s->stats_timer = NULL; }
|
||||
if (s->teardown_timer) { uasync_cancel_timeout(inst->ua, s->teardown_timer); s->teardown_timer = NULL; }
|
||||
@@ -827,27 +879,16 @@ void call_start(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t peer_nod
|
||||
s->state = CALL_ST_OUTGOING;
|
||||
ctx->c_calls_started++;
|
||||
|
||||
uint8_t buf[sizeof(struct call_pkt_invite)];
|
||||
struct call_pkt_invite* inv = (struct call_pkt_invite*)buf;
|
||||
inv->subcmd = CALL_SUBCMD_INVITE;
|
||||
inv->call_id = out_call_id;
|
||||
inv->group_id = group_id;
|
||||
inv->num_tracks = 1;
|
||||
inv->tracks[0].type = CALL_TRACK_AUDIO;
|
||||
inv->tracks[0].codec = CALL_CODEC_OPUS;
|
||||
inv->tracks[0].sample_rate = CALL_OPUS_SAMPLE_RATE;
|
||||
inv->tracks[0].channels = 1;
|
||||
inv->tracks[0].bitrate = 32000;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: call_start id=%016llx peer=0x%016llx group=0x%016llx",
|
||||
CALL_ID, (unsigned long long)out_call_id, (unsigned long long)peer_node_id, (unsigned long long)group_id);
|
||||
|
||||
if (call_send(ctx, group_id, peer_node_id, buf, sizeof(buf), ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT) != 0) {
|
||||
if (call_send_invite(ctx, s) != 0) {
|
||||
call_end(ctx, s, CALL_REASON_CONNECT_FAIL);
|
||||
return;
|
||||
}
|
||||
call_post_bgp_path(ctx, s);
|
||||
s->ring_timer = uasync_set_timeout(inst->ua, CALL_RING_TIMEOUT_TB, s, call_ring_timeout_cb, "call_ring");
|
||||
s->invite_timer = uasync_set_timeout(inst->ua, CALL_INVITE_RETRY_TB, s, call_invite_retry_cb, "call_invite_retry");
|
||||
}
|
||||
|
||||
void call_accept(struct UTUN_INSTANCE* inst, uint64_t call_id) {
|
||||
|
||||
@@ -85,6 +85,8 @@ struct call_pkt_media {
|
||||
|
||||
/* ── таймауты и ёмкости (timebase 0.1ms) ── */
|
||||
#define CALL_RING_TIMEOUT_TB 450000 /* 45с — нет ответа → CANCEL */
|
||||
#define CALL_INVITE_RETRY_TB 20000 /* 2с — период ретрансмита INVITE (до RINGING) */
|
||||
#define CALL_NO_ROUTE_MAX_STRIKES 3 /* 3 × 2с ≈ 6с без маршрута → fail-fast CONNECT_FAIL */
|
||||
#define CALL_NO_TRAFFIC_TB 200000 /* 20с — нет входящих медиа → HANGUP */
|
||||
#define CALL_TEARDOWN_DELAY_TB 5000 /* 500мс — задержка разрыва conn_mgr после завершения */
|
||||
#define CALL_MAX_TX_FRAMES 50 /* ~1с буфер отправки (50 кадров × 20мс) */
|
||||
|
||||
@@ -891,6 +891,11 @@ int merkle_sync_start(struct UTUN_INSTANCE* inst, uint64_t peer,
|
||||
}
|
||||
}
|
||||
|
||||
/* я в SLEEP-фазе — не инициирую синхронизацию (client-side throttling) */
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
if (standby_get_sleep_tb() > 0) return 0;
|
||||
#endif
|
||||
|
||||
struct ms_session* s = _session_find(ms, peer, ns);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_MEMBER_SYNC, "%s: start peer=%016llx ns=%s new=%d", MS_ID, (unsigned long long)peer, ns, s ? 0 : 1);
|
||||
if (!s) {
|
||||
|
||||
@@ -194,21 +194,30 @@ static struct ETCP_ROUTER_CONN* router_conn_find(struct UTUN_INSTANCE* inst,
|
||||
// ====================================================================
|
||||
|
||||
// Найти ETCP_CONN для отправки (учитывая indirect-посредников). NULL = нет маршрута.
|
||||
static struct ETCP_CONN* router_send_conn(struct ETCP_ROUTER_CONN* rconn) {
|
||||
struct UTUN_INSTANCE* inst = rconn->inst;
|
||||
struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id);
|
||||
static struct ETCP_CONN* router_route_conn(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t remote_node_id) {
|
||||
struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, group_id);
|
||||
if (group) {
|
||||
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, rconn->remote_node_id);
|
||||
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, remote_node_id);
|
||||
if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) {
|
||||
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) {
|
||||
struct ETCP_CONN* c = topo_group_find_conn_for_node(group, nq->conn_mgr_intermediaries[i]);
|
||||
if (c) return c;
|
||||
}
|
||||
}
|
||||
return topo_group_find_conn_for_node(group, rconn->remote_node_id);
|
||||
return topo_group_find_conn_for_node(group, remote_node_id);
|
||||
}
|
||||
/* group_id=0 (или группа не найдена) — глобальная маршрутизация: прямое соединение по node_id */
|
||||
return instance_find_conn(inst, rconn->remote_node_id);
|
||||
return instance_find_conn(inst, remote_node_id);
|
||||
}
|
||||
|
||||
static struct ETCP_CONN* router_send_conn(struct ETCP_ROUTER_CONN* rconn) {
|
||||
return router_route_conn(rconn->inst, rconn->group_id, rconn->remote_node_id);
|
||||
}
|
||||
|
||||
int etcp_router_has_route(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id) {
|
||||
if (!inst) return 0;
|
||||
if (dst_node_id == inst->node_id) return 1;
|
||||
return router_route_conn(inst, group_id, dst_node_id) != NULL;
|
||||
}
|
||||
|
||||
// Собрать и закодировать (подпись/шифрование) пакет в начале отправки.
|
||||
|
||||
@@ -207,6 +207,10 @@ int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst
|
||||
struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst,
|
||||
uint64_t group_id, uint64_t remote_node_id, uint8_t svc_id);
|
||||
|
||||
// Есть ли сейчас физический маршрут до узла (прямой, indirect-посредник или глобальный).
|
||||
// 1 = есть ETCP-соединение для отправки, 0 = нет (передача встанет в no_route).
|
||||
int etcp_router_has_route(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id);
|
||||
|
||||
// Отправить данные с авто-seq и контролем inflight
|
||||
// data: payload без svc_id, mode: ROUTE_CRYPTO_SIGN / ROUTE_CRYPTO_ENCRYPT (0 = обычный)
|
||||
int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn,
|
||||
|
||||
Reference in new issue
Block a user