diff --git a/daemon/call.c b/daemon/call.c index b66a174d1..fdef6c20c 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -161,6 +161,7 @@ static int call_avpf2avp_rtcp(str *s, struct packet_stream *); static int call_savpf2avp_rtcp(str *s, struct packet_stream *); //static int call_savpf2savp_rtcp(str *s, struct packet_stream *); +static void unkernelize(struct packet_stream *); /* ********** */ @@ -435,6 +436,7 @@ void kernelize(struct packet_stream *stream) { reti.src_addr.family = reti.dst_addr.family; reti.src_addr.port = sink->sfd->fd.localport; + reti.ssrc = sink->crypto.ssrc; ifa = g_atomic_pointer_get(&sink->media->local_address); if (reti.src_addr.family == AF_INET) @@ -860,6 +862,12 @@ kernel_check: if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) goto forward; + if (sink && sink->crypto.ssrc_mismatch && !stun_ret) { + ilog(LOG_INFO, "SSRC changed, unkernelizing media stream"); + __unkernelize(stream); + goto forward; + } + if (PS_ISSET(stream, CONFIRMED) && sink && PS_ARESET2(sink, CONFIRMED, FILLED)) kernelize(stream); @@ -1378,6 +1386,7 @@ static void callmaster_timer(void *ptr) { struct stream_fd *sfd; struct rtp_stats *rs; unsigned int pt; + struct rtp_ssrc_entry *cur_ssrc; ZERO(hlp); @@ -1444,6 +1453,11 @@ static void callmaster_timer(void *ptr) { mutex_lock(&sink->out_lock); if (sink->crypto.params.crypto_suite && ke->target.encrypt.last_index - sink->crypto.last_index > 0x4000) { + // Keep the SSRC list in sync too. + cur_ssrc = find_ssrc(ke->target.ssrc, sink->crypto.ssrc_list); + if (cur_ssrc) + cur_ssrc->index = ke->target.encrypt.last_index; + sink->crypto.last_index = ke->target.encrypt.last_index; update = 1; } @@ -2931,6 +2945,7 @@ void call_destroy(struct call *c) { dtls_shutdown(ps); ps->sfd = NULL; crypto_cleanup(&ps->crypto); + free_ssrc_list(ps->crypto.ssrc_list); ps->rtp_sink = NULL; ps->rtcp_sink = NULL; diff --git a/daemon/crypto.c b/daemon/crypto.c index 4caaf0c50..b069c112b 100644 --- a/daemon/crypto.c +++ b/daemon/crypto.c @@ -572,3 +572,36 @@ void crypto_dump_keys(struct crypto_context *in, struct crypto_context *out) { ilog(LOG_DEBUG, "SRTP keys, outgoing:"); dump_key(out); } + +struct rtp_ssrc_entry *find_ssrc(u_int32_t ssrc, struct rtp_ssrc_entry *list) { + for (; list; list = list->next) { + if (list->ssrc == ssrc) + return list; + } + return 0; +} + +void add_ssrc_entry(struct rtp_ssrc_entry *ent, struct rtp_ssrc_entry *list) { + struct rtp_ssrc_entry *cur; + for (cur = list; list; list = list->next) + cur = list; + cur->next = ent; +} + +struct rtp_ssrc_entry *create_ssrc_entry(u_int32_t ssrc, u_int64_t index) { + struct rtp_ssrc_entry *ent; + ent = malloc(sizeof(struct rtp_ssrc_entry)); + ent->ssrc = ssrc; + ent->index = index; + ent->next = 0; + return ent; +} + +void free_ssrc_list(struct rtp_ssrc_entry *list) { + struct rtp_ssrc_entry *tmp; + for (tmp = list; tmp; ) { + tmp = list->next; + free(list); + list = tmp; + } +} diff --git a/daemon/crypto.h b/daemon/crypto.h index 54aa37c62..04b28b66d 100644 --- a/daemon/crypto.h +++ b/daemon/crypto.h @@ -88,10 +88,16 @@ struct crypto_context { void *session_key_ctx[2]; int have_session_key:1; -}; - + struct rtp_ssrc_entry *ssrc_list; + int ssrc_mismatch; +}; +struct rtp_ssrc_entry { + u_int32_t ssrc; + u_int64_t index; + struct rtp_ssrc_entry *next; +}; extern const struct crypto_suite crypto_suites[]; extern const int num_crypto_suites; @@ -102,7 +108,10 @@ const struct crypto_suite *crypto_find_suite(const str *); int crypto_gen_session_key(struct crypto_context *, str *, unsigned char, int); void crypto_dump_keys(struct crypto_context *in, struct crypto_context *out); - +struct rtp_ssrc_entry *find_ssrc(u_int32_t, struct rtp_ssrc_entry *); +void add_ssrc_entry(struct rtp_ssrc_entry *, struct rtp_ssrc_entry *); +struct rtp_ssrc_entry *create_ssrc_entry(u_int32_t, u_int64_t); +void free_ssrc_list(struct rtp_ssrc_entry *); INLINE int crypto_encrypt_rtp(struct crypto_context *c, struct rtp_header *rtp, diff --git a/daemon/rtp.c b/daemon/rtp.c index a70544bee..289d6f508 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -182,26 +182,41 @@ void rtp_append_mki(str *s, struct crypto_context *c) { int rtp_avp2savp(str *s, struct crypto_context *c) { struct rtp_header *rtp; str payload, to_auth; - u_int64_t index; + struct rtp_ssrc_entry *cur_ssrc; if (rtp_payload(&rtp, &payload, s)) return -1; if (check_session_keys(c)) return -1; - /* SSRC is part of the crypto context and ROC must be reset when it changes */ + if (G_UNLIKELY(!c->ssrc_list)) + c->ssrc_list = create_ssrc_entry(rtp->ssrc, ntohs(rtp->seq_num)); + + // Find the entry for the current SSRC. + cur_ssrc = find_ssrc(rtp->ssrc, c->ssrc_list); + // If it doesn't exist, create a new entry. + if (G_UNLIKELY(!cur_ssrc)) { + cur_ssrc = create_ssrc_entry(rtp->ssrc, ntohs(rtp->seq_num)); + add_ssrc_entry(cur_ssrc, c->ssrc_list); + } + + // New SSRC, set the crypto context. if (G_UNLIKELY(!c->ssrc)) c->ssrc = rtp->ssrc; + // SSRC has changed. else if (G_UNLIKELY(c->ssrc != rtp->ssrc)) { - c->last_index = 0; + // Signal stream_packet() to unkernelize. + c->ssrc_mismatch = 1; c->ssrc = rtp->ssrc; + c->last_index = cur_ssrc->index; + } else { + c->ssrc_mismatch = 0; } - index = packet_index(c, rtp); + cur_ssrc->index = packet_index(c, rtp); /* rfc 3711 section 3.1 */ - - if (!c->params.session_params.unencrypted_srtp && crypto_encrypt_rtp(c, rtp, &payload, index)) + if (!c->params.session_params.unencrypted_srtp && crypto_encrypt_rtp(c, rtp, &payload, cur_ssrc->index)) return -1; to_auth = *s; @@ -209,7 +224,7 @@ int rtp_avp2savp(str *s, struct crypto_context *c) { rtp_append_mki(s, c); if (!c->params.session_params.unauthenticated_srtp && c->params.crypto_suite->srtp_auth_tag) { - c->params.crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, index); + c->params.crypto_suite->hash_rtp(c, s->s + s->len, &to_auth, cur_ssrc->index); s->len += c->params.crypto_suite->srtp_auth_tag; } diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index c203eb838..f7db1826a 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -141,6 +141,7 @@ struct re_crypto_context { struct crypto_shash *shash; const struct re_cipher *cipher; const struct re_hmac *hmac; + u_int32_t ssrc; }; struct rtpengine_stats_a { @@ -1891,6 +1892,12 @@ static u_int64_t packet_index(struct re_crypto_context *c, long long int diff; unsigned long flags; + // Keep the crypt context SSRC in sync. + if (unlikely(!c->ssrc)) + c->ssrc = rtp->ssrc; + else if (unlikely(c->ssrc != rtp->ssrc)) + c->ssrc = rtp->ssrc; + seq = ntohs(rtp->seq_num); spin_lock_irqsave(&c->lock, flags); @@ -2271,6 +2278,9 @@ src_check_ok: if (pkt_idx != pkt_idx_u) update_packet_index(&g->decrypt, &g->target.decrypt, pkt_idx); } else { + // Pass to userspace if SSRC has changed. + if ((g->encrypt.ssrc) && (g->encrypt.ssrc != rtp.header->ssrc)) + goto skip_error; pkt_idx_u = pkt_idx = packet_index(&g->encrypt, &g->target.encrypt, rtp.header); if (pkt_idx != pkt_idx_u) diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h index 361a56fb0..0dd2bd8e1 100644 --- a/kernel-module/xt_RTPENGINE.h +++ b/kernel-module/xt_RTPENGINE.h @@ -82,6 +82,7 @@ struct rtpengine_target_info { struct rtpengine_srtp decrypt; struct rtpengine_srtp encrypt; + u_int32_t ssrc; // Expose the SSRC to userspace when we resync. unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */ unsigned int num_payload_types;