Repository navigation
Fix SRTP ROC retry after transport reinit being a no-op #5269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -349,6 +349,9 @@ typedef struct transport_srtp | |
|
|
||
| pj_uint32_t tx_ssrc; | ||
|
|
||
| /* ROC retry: prev_rx_roc.roc is the candidate currently applied. */ | ||
| pj_bool_t rx_roc_retry_alt; | ||
|
|
||
| } transport_srtp; | ||
|
|
||
|
|
||
|
|
@@ -917,7 +920,11 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_get_setting( | |
| pjmedia_srtp_setting *opt) | ||
| { | ||
| transport_srtp *srtp = (transport_srtp*) tp; | ||
|
|
||
| pj_lock_acquire(srtp->mutex); | ||
| *opt = srtp->setting; | ||
| pj_lock_release(srtp->mutex); | ||
|
|
||
| return PJ_SUCCESS; | ||
| } | ||
|
|
||
|
|
@@ -929,7 +936,11 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_modify_setting( | |
| const pjmedia_srtp_setting *opt) | ||
| { | ||
| transport_srtp *srtp = (transport_srtp*) tp; | ||
|
|
||
| pj_lock_acquire(srtp->mutex); | ||
| srtp->setting = *opt; | ||
| pj_lock_release(srtp->mutex); | ||
|
|
||
| return PJ_SUCCESS; | ||
| } | ||
|
|
||
|
|
@@ -943,6 +954,7 @@ static pj_status_t create_srtp_ctx(transport_srtp *srtp, | |
| { | ||
| srtp_policy_t tx_; | ||
| srtp_policy_t rx_; | ||
| srtp_policy_t rx_tmpl; | ||
| srtp_err_status_t err; | ||
| int cr_tx_idx = 0; | ||
| int au_tx_idx = 0; | ||
|
|
@@ -1054,8 +1066,10 @@ static pj_status_t create_srtp_ctx(transport_srtp *srtp, | |
| else | ||
| rx_.rtp.sec_serv = sec_serv_none; | ||
| rx_.key = (uint8_t*)ctx->rx_key; | ||
| if (setting->rx_roc.roc != 0 && | ||
| setting->rx_roc.ssrc != 0) | ||
| if (setting->rx_roc.ssrc != 0 && | ||
| (setting->rx_roc.roc != 0 || | ||
| (setting->prev_rx_roc.ssrc == setting->rx_roc.ssrc && | ||
| setting->prev_rx_roc.roc != 0))) | ||
| { | ||
| rx_.ssrc.type = ssrc_specific; | ||
| rx_.ssrc.value = setting->rx_roc.ssrc; | ||
|
|
@@ -1072,6 +1086,16 @@ static pj_status_t create_srtp_ctx(transport_srtp *srtp, | |
| rx_.rtcp = rx_.rtp; | ||
| rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; | ||
| rx_.next = NULL; | ||
| if (rx_.ssrc.type == ssrc_specific) { | ||
| /* Keep a wildcard template alongside the specific stream, so | ||
| * a remote SSRC change after probation is still accepted. | ||
| */ | ||
| rx_tmpl = rx_; | ||
| rx_tmpl.ssrc.type = ssrc_any_inbound; | ||
| rx_tmpl.ssrc.value = 0; | ||
| rx_tmpl.next = NULL; | ||
| rx_.next = &rx_tmpl; | ||
| } | ||
| err = srtp_create(&ctx->srtp_rx_ctx, &rx_); | ||
| if (err != srtp_err_status_ok) { | ||
| srtp_dealloc(ctx->srtp_tx_ctx); | ||
|
|
@@ -1089,6 +1113,8 @@ static pj_status_t create_srtp_ctx(transport_srtp *srtp, | |
| setting->rx_roc.ssrc, setting->rx_roc.roc, | ||
| (err == srtp_err_status_ok)? "succeeded": "failed")); | ||
| } | ||
| if (ctx == &srtp->srtp_ctx) | ||
| srtp->rx_roc_retry_alt = PJ_FALSE; | ||
| ctx->rx_policy = *rx; | ||
| pj_strset(&ctx->rx_policy.key, ctx->rx_key, rx->key.slen); | ||
| ctx->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); | ||
|
|
@@ -1653,6 +1679,13 @@ static void srtp_rtp_cb(pjmedia_tp_cb_param *param) | |
| return; | ||
| } | ||
|
|
||
| /* Runt packets cannot hold an RTP header. */ | ||
| if (size < (pj_ssize_t)sizeof(pjmedia_rtp_hdr)) { | ||
| PJ_LOG(5, (srtp->pool->obj_name, "Runt RTP packet, size=%ld", | ||
| (long)size)); | ||
| return; | ||
| } | ||
|
|
||
| /* Give the packet to keying first by invoking its send_rtp() op. | ||
| * Yes, the usage of send_rtp() is rather hacky, but it is convenient | ||
| * as the signature suits the purpose and it is ready to use | ||
|
|
@@ -1751,6 +1784,7 @@ static void srtp_rtp_cb(pjmedia_tp_cb_param *param) | |
|
|
||
| #if PJMEDIA_SRTP_CHECK_ROC_ON_RESTART | ||
| if (srtp->probation_cnt > 0 && err == srtp_err_status_auth_fail && | ||
| rx_ssrc == srtp->setting.rx_roc.ssrc && | ||
| srtp->setting.prev_rx_roc.ssrc != 0 && | ||
| srtp->setting.prev_rx_roc.ssrc == srtp->setting.rx_roc.ssrc && | ||
| srtp->setting.prev_rx_roc.roc != srtp->setting.rx_roc.roc) | ||
|
|
@@ -1790,28 +1824,40 @@ static void srtp_rtp_cb(pjmedia_tp_cb_param *param) | |
| } | ||
|
|
||
| } else if (need_retry) { | ||
| unsigned roc, new_roc; | ||
| unsigned old_roc, new_roc; | ||
| srtp_err_status_t status; | ||
|
|
||
| srtp_get_stream_roc(srtp->srtp_ctx.srtp_rx_ctx, | ||
| srtp->setting.rx_roc.ssrc, &roc); | ||
| new_roc = (roc == srtp->setting.rx_roc.roc? | ||
| srtp->setting.prev_rx_roc.roc: srtp->setting.rx_roc.roc); | ||
| /* Try the alternative candidate, the setting is only updated | ||
| * after a packet authenticates. | ||
| */ | ||
| old_roc = (srtp->rx_roc_retry_alt? srtp->setting.prev_rx_roc.roc : | ||
| srtp->setting.rx_roc.roc); | ||
| new_roc = (srtp->rx_roc_retry_alt? srtp->setting.rx_roc.roc : | ||
| srtp->setting.prev_rx_roc.roc); | ||
| status = srtp_set_stream_roc(srtp->srtp_ctx.srtp_rx_ctx, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The retry never runs in the remote-change case (
So the two candidates only alternate in the local-change case. At minimum, please log this failure. Ideally, make the remote-change case work too, e.g. by creating the stream so the ROC can be set on it. |
||
| srtp->setting.rx_roc.ssrc, new_roc); | ||
| if (status == srtp_err_status_ok) { | ||
| srtp->rx_roc_retry_alt = !srtp->rx_roc_retry_alt; | ||
| PJ_LOG(4, (srtp->pool->obj_name, | ||
| "Retrying to unprotect SRTP from ROC %d to new ROC %d", | ||
| roc, new_roc)); | ||
| old_roc, new_roc)); | ||
| err = srtp_unprotect(srtp->srtp_ctx.srtp_rx_ctx, (pj_uint8_t*)pkt, | ||
| &len); | ||
| } else { | ||
| /* Cannot set the ROC, stop retrying. */ | ||
| srtp->setting.prev_rx_roc.ssrc = 0; | ||
| PJ_LOG(4, (srtp->pool->obj_name, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
| "Failed to set RX ROC %d for retry, err=%s", | ||
| new_roc, get_libsrtp_errstr(status))); | ||
| } | ||
| } | ||
|
|
||
| if (err != srtp_err_status_ok) { | ||
| PJ_LOG(5,(srtp->pool->obj_name, | ||
| "Failed to unprotect SRTP, pkt size=%ld, SSRC=%u, err=%s", | ||
| size, rx_ssrc, get_libsrtp_errstr(err))); | ||
| "Failed to unprotect SRTP, pkt size=%ld, SSRC=%u, " | ||
| "seq=%u, err=%s", | ||
| size, rx_ssrc, ntohs(((pjmedia_rtp_hdr*)pkt)->seq), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this reads the RTP header without checking the packet length. A runt datagram (for example 1-3 bytes) gets here with |
||
| get_libsrtp_errstr(err))); | ||
| } else { | ||
| cb = srtp->rtp_cb; | ||
| cb2 = srtp->rtp_cb2; | ||
|
|
@@ -1822,6 +1868,18 @@ static void srtp_rtp_cb(pjmedia_tp_cb_param *param) | |
|
|
||
| /* Save SSRC after successful SRTP unprotect */ | ||
| srtp->rx_ssrc = rx_ssrc; | ||
|
|
||
| /* Settle the ROC retry on the SSRC it belongs to: write back | ||
| * the winning ROC and close the retry window. | ||
| */ | ||
| if (srtp->setting.prev_rx_roc.ssrc != 0 && | ||
| rx_ssrc == srtp->setting.rx_roc.ssrc) | ||
| { | ||
| if (srtp->rx_roc_retry_alt) | ||
| srtp->setting.rx_roc.roc = srtp->setting.prev_rx_roc.roc; | ||
| srtp->setting.prev_rx_roc.ssrc = 0; | ||
| srtp->rx_roc_retry_alt = PJ_FALSE; | ||
| } | ||
| } | ||
|
|
||
| pj_lock_release(srtp->mutex); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With
ssrc_specificin the remote-change case there's no longer anssrc_any_inboundtemplate in the RX context. As you traced, a new SSRC from the remote within probation still works via theno_ctxrestart. ButPJMEDIA_SRTP_CHECK_SSRC_ON_RESTARTonly runs whileprobation_cnt > 0, so an SSRC change after probation (e.g. an SBC switching media source with the same SDES keys) now getsno_ctxon every packet, and incoming audio dies. Before this commit, libsrtp would clone a stream from the template.Note that the
rx_roc.roc != 0case already had this exposure before this PR; this commit extends it to the remote-change case. Could you check whether givingsrtp_create()a policy list works here, i.e. thessrc_specificstream plus anssrc_any_inboundtemplate viarx_.next? That would keep the stream available forsrtp_set_stream_roc()and still accept new SSRCs. I haven't verified how libsrtp handles that combination.