SRTP index (ROC) is kept across a DTLS restart: after the sequence number has wrapped, a new DTLS association gets packets it cannot authenticate
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 40/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- c
- Ambito
- networking, security
Direzione di ricerca
Start with daemon/call.c at __dtls_restart() and call_stream_crypto_reset(), then trace how daemon/rtp.c derives the SRTP packet index from ext_seq and how daemon/dtls.c installs new keys. The report proposes resetting the media's inbound and outbound SSRC indices on DTLS restart; check existing tests for DTLS restart and SRTP rollover behavior. Done means a restarted association with a wrapped sequence number can authenticate downlink packets, while existing SRTP behavior still passes.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
rtpengine version the issue has been seen with
mr26.3.0.0. Reproduced with userspace forwarding (table = -1). We also see field calls of the same shape through an SBC with kernel forwarding (table = 0), but haven't proven those separately. The same code is on master as of 2026-11-06 (89589b7)
Used distribution and its version
Debian GNU/Linux 11
Linux kernel version used
5.10.0-35-amd64
CPU architecture issue was seen on (see uname -m)
None
Expected behaviour you didn't see
Setup
WebRTC client (Chrome / Electron, DTLS-SRTP, AEAD_AES_256_GCM) <-> rtpengine <-> Asterisk (plain RTP).
dtls-passive = true. rtpengine forwards Asterisk's SSRC to the client unchanged; no transcoding.
What happens
A call runs long enough for the downlink (Asterisk -> rtpengine -> client) RTP sequence number to wrap past 65535. Asterisk starts its sequence number below 0x8000, so this happens 11 to 22 minutes into a call.
The client replaces its peer connection (a new RTCPeerConnection after a network failure), and sends a re-INVITE with a new a=fingerprint. rtpengine logs DTLS fingerprint changed, restarting DTLS, and the new DTLS handshake completes.
rtpengine keeps sending the downlink with the same SSRC and continuing sequence numbers. Each packet is protected with the old rollover counter (ROC >= 1) under the new keys.
The client's new SRTP context starts with ROC 0 (RFC 3711 3.3.1; RFC 5764 4.2: the DTLS association yields new keys, hence new crypto contexts). Every downlink packet fails authentication. In the browser: the transport's packetsReceived / bytesReceived grow normally, but no inbound-rtp stats object is ever created, and no frame is decoded. The call is one-way silent until it ends.
The uplink is not affected: the new peer connection sends new SSRCs, which get fresh entries.
Reproduction (ours)
One long call; force a new client peer connection by blocking the client's media port for 32 s.
Before the downlink sequence number wraps: the new peer connection plays, 3 of 3.
After the wrap: 0 of 2 play. Transport counters are the same as on the working attempts. SSRC and sequence numbers are the same as before the restart.
Production: 8 calls over 16 days on calls at least 15 minutes old with a wrapped downlink, 0 of 8 recovered. Two calls where the far end's SSRC happened to change did recover.
Not the same as #2220 or #2238: no transcoding and no fixed-egress-SSRC here (plain pass-through, DTLS=passive toward the browser). The failing direction is rtpengine → browser, and the trigger is __dtls_restart() after the downlink sequence has wrapped. I checked master at 89589b7 (2026-11-06): __dtls_restart() still doesn’t reset ext_seq.
Where in the code
daemon/rtp.c packet_index() takes the SRTP index from ssrc_entry_call->stats->ext_seq, and derives the ROC from its upper bits.
daemon/call.c __dtls_restart() (via __fingerprint_changed()) shuts DTLS down and re-runs __init_stream(), but never resets ext_seq. The new keys are installed by crypto_init() in dtls.c, which doesn't touch it either.
call_stream_crypto_reset() already zeroes ext_seq for every SSRC of the media, but only on an endpoint change of a non-DTLS stream.
Proposed fix (patch attached / PR): zero ext_seq for the media's ssrc_hash_in and ssrc_hash_out in __dtls_restart(), the same way call_stream_crypto_reset() does. After the reset, the next packet re-seeds the index from its own sequence number with ROC 0, which matches the peer's new context.
static void __ssrc_hash_reset_index(struct ssrc_hash *h) {
mutex_lock(&h->lock);
for (GList *l = h->nq.head; l; l = l->next) {
struct ssrc_entry_call *se = l->data;
atomic_set_na(&se->stats->ext_seq, 0);
}
mutex_unlock(&h->lock);
}
static void __dtls_restart(struct call_media *m) {
IQUEUE_FOREACH(&m->streams, ps) {
PS_CLEAR(ps, FINGERPRINT_VERIFIED);
dtls_shutdown(ps);
__init_stream(ps);
}
__ssrc_hash_reset_index(&m->ssrc_hash_in);
__ssrc_hash_reset_index(&m->ssrc_hash_out);
}
Unexpected behaviour you saw
No response
Steps to reproduce the problem
No response
Additional program output to the terminal or logs illustrating the issue
Anything else?
No response
- Lingua principale
- Perl
- Stelle
- 992
- Fork
- 459
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di sipwise/rtpengine
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 55/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
sipwise/rtpengine#2231 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 42/100
sipwise/rtpengine#2219 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di sipwise/rtpengine
Issue simili
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
virtualmin/virtualmin-gpl#1325 ·
-
check_keywords.pl dies before reporting an unknown keyword categoryForse già presa @zhaohai666 l’ha presa 1 giorno fa. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 87/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
libwww-perl/HTTP-Message#236 ·
-
1.severity: security
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno