diff options
author | Jenkins2 <jenkins2@gerrit.asterisk.org> | 2017-07-13 14:40:11 -0500 |
---|---|---|
committer | Gerrit Code Review <gerrit2@gerrit.digium.api> | 2017-07-13 14:40:11 -0500 |
commit | 0f45c979a3de00b320e05ba93309cf412e9e2702 (patch) | |
tree | 1852402245ee52adb65acc5d47b1ab13857aaea0 /res/res_rtp_asterisk.c | |
parent | e83b9d141a416ab8c0b1fcfcd29d73abf2ca04c9 (diff) | |
parent | 065c3005ad920f5fe2cedcf062e38b8e28eeb015 (diff) |
Merge "res_rtp_asterisk / res_pjsip: Add support for BUNDLE."
Diffstat (limited to 'res/res_rtp_asterisk.c')
-rw-r--r-- | res/res_rtp_asterisk.c | 740 |
1 files changed, 495 insertions, 245 deletions
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 30efb2ca6..9d3969fbe 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -68,6 +68,7 @@ #include "asterisk/module.h" #include "asterisk/rtp_engine.h" #include "asterisk/smoother.h" +#include "asterisk/uuid.h" #include "asterisk/test.h" #define MAX_TIMESTAMP_SKEW 640 @@ -240,6 +241,14 @@ struct ice_wrap { }; #endif +/*! \brief Structure used for mapping an incoming SSRC to an RTP instance */ +struct rtp_ssrc_mapping { + /*! \brief The received SSRC */ + unsigned int ssrc; + /*! \brief The RTP instance this SSRC belongs to*/ + struct ast_rtp_instance *instance; +}; + /*! \brief RTP session description */ struct ast_rtp { int s; @@ -247,6 +256,7 @@ struct ast_rtp { struct ast_frame f; unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ + char cname[AST_UUID_STR_LEN]; /*!< Our local CNAME */ unsigned int themssrc; /*!< Their SSRC */ unsigned int rxssrc; unsigned int lastts; @@ -303,6 +313,11 @@ struct ast_rtp { struct ast_rtcp *rtcp; struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ + struct ast_rtp_instance *bundled; /*!< The RTP instance we are bundled to */ + int stream_num; /*!< Stream num for this RTP instance */ + AST_VECTOR(, struct rtp_ssrc_mapping) ssrc_mapping; /*!< Mappings of SSRC to RTP instances */ + struct ast_sockaddr bind_address; /*!< Requested bind address for the sockets */ + enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ struct ast_sockaddr strict_rtp_address; /*!< Remote address information for strict RTP purposes */ @@ -479,6 +494,9 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance); static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance); +static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc); +static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num); +static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent); #ifdef HAVE_OPENSSL_SRTP static int ast_rtp_activate(struct ast_rtp_instance *instance); @@ -1920,6 +1938,9 @@ static struct ast_rtp_engine asterisk_rtp_engine = { #endif .ssrc_get = ast_rtp_get_ssrc, .cname_get = ast_rtp_get_cname, + .set_remote_ssrc = ast_rtp_set_remote_ssrc, + .set_stream_num = ast_rtp_set_stream_num, + .bundle = ast_rtp_bundle, }; #ifdef HAVE_OPENSSL_SRTP @@ -1956,6 +1977,23 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl } #endif +#ifdef HAVE_OPENSSL_SRTP +static void dtls_perform_setup(struct dtls_details *dtls) +{ + if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) { + return; + } + + SSL_clear(dtls->ssl); + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(dtls->ssl); + } else { + SSL_set_connect_state(dtls->ssl); + } + dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; +} +#endif + #ifdef HAVE_PJPROJECT static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq); @@ -1984,9 +2022,12 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) } #ifdef HAVE_OPENSSL_SRTP + + dtls_perform_setup(&rtp->dtls); dtls_perform_handshake(instance, &rtp->dtls, 0); if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { + dtls_perform_setup(&rtp->rtcp->dtls); dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); } #endif @@ -2254,59 +2295,14 @@ static int dtls_srtp_renegotiate(const void *data) return 0; } -static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp) +static int dtls_srtp_add_local_ssrc(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp, unsigned int ssrc, int set_remote_policy) { unsigned char material[SRTP_MASTER_LEN * 2]; unsigned char *local_key, *local_salt, *remote_key, *remote_salt; struct ast_srtp_policy *local_policy, *remote_policy = NULL; - struct ast_rtp_instance_stats stats = { 0, }; int res = -1; struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; - /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ - if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) { - X509 *certificate; - - if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) { - ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance); - return -1; - } - - /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ - if (rtp->remote_fingerprint[0]) { - const EVP_MD *type; - unsigned char fingerprint[EVP_MAX_MD_SIZE]; - unsigned int size; - - if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA1) { - type = EVP_sha1(); - } else if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA256) { - type = EVP_sha256(); - } else { - ast_log(LOG_WARNING, "Unsupported fingerprint hash type on RTP instance '%p'\n", instance); - return -1; - } - - if (!X509_digest(certificate, type, fingerprint, &size) || - !size || - memcmp(fingerprint, rtp->remote_fingerprint, size)) { - X509_free(certificate); - ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n", - instance); - return -1; - } - } - - X509_free(certificate); - } - - /* Ensure that certificate verification was successful */ - if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(dtls->ssl) != X509_V_OK) { - ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n", - instance); - return -1; - } - /* Produce key information and set up SRTP */ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n", @@ -2341,41 +2337,31 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as goto error; } - if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) { - goto error; - } + res_srtp_policy->set_ssrc(local_policy, ssrc, 0); - res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0); + if (set_remote_policy) { + if (!(remote_policy = res_srtp_policy->alloc())) { + goto error; + } - if (!(remote_policy = res_srtp_policy->alloc())) { - goto error; - } + if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) { + ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp); + goto error; + } - if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) { - ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp); - goto error; - } + if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) { + ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp); + goto error; + } - if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) { - ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp); - goto error; + res_srtp_policy->set_ssrc(remote_policy, 0, 1); } - res_srtp_policy->set_ssrc(remote_policy, 0, 1); - if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) { ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp); goto error; } - if (rtp->rekey) { - ao2_ref(instance, +1); - if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) { - ao2_ref(instance, -1); - goto error; - } - } - res = 0; error: @@ -2388,6 +2374,71 @@ error: return res; } + +static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + int index; + + /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ + if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) { + X509 *certificate; + + if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) { + ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance); + return -1; + } + + /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ + if (rtp->remote_fingerprint[0]) { + const EVP_MD *type; + unsigned char fingerprint[EVP_MAX_MD_SIZE]; + unsigned int size; + + if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA1) { + type = EVP_sha1(); + } else if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA256) { + type = EVP_sha256(); + } else { + ast_log(LOG_WARNING, "Unsupported fingerprint hash type on RTP instance '%p'\n", instance); + return -1; + } + + if (!X509_digest(certificate, type, fingerprint, &size) || + !size || + memcmp(fingerprint, rtp->remote_fingerprint, size)) { + X509_free(certificate); + ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n", + instance); + return -1; + } + } + + X509_free(certificate); + } + + if (dtls_srtp_add_local_ssrc(rtp, srtp, instance, rtcp, ast_rtp_instance_get_ssrc(instance), 1)) { + return -1; + } + + for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) { + struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index); + + if (dtls_srtp_add_local_ssrc(rtp, srtp, instance, rtcp, ast_rtp_instance_get_ssrc(mapping->instance), 0)) { + return -1; + } + } + + if (rtp->rekey) { + ao2_ref(instance, +1); + if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) { + ao2_ref(instance, -1); + return -1; + } + } + + return 0; +} #endif static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet) @@ -2586,7 +2637,9 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz int len = size; void *temp = buf; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp); + struct ast_rtp_instance *transport = rtp->bundled ? rtp->bundled : instance; + struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(transport); + struct ast_srtp *srtp = ast_rtp_instance_get_srtp(transport, rtcp); int res; *via_ice = 0; @@ -2596,20 +2649,24 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz } #ifdef HAVE_PJPROJECT - if (rtp->ice) { + if (transport_rtp->ice) { pj_status_t status; struct ice_wrap *ice; pj_thread_register_check(); /* Release the instance lock to avoid deadlock with PJPROJECT group lock */ - ice = rtp->ice; + ice = transport_rtp->ice; ao2_ref(ice, +1); - ao2_unlock(instance); + if (instance == transport) { + ao2_unlock(instance); + } status = pj_ice_sess_send_data(ice->real_ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len); ao2_ref(ice, -1); - ao2_lock(instance); + if (instance == transport) { + ao2_lock(instance); + } if (status == PJ_SUCCESS) { *via_ice = 1; return len; @@ -2617,7 +2674,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz } #endif - res = ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa); + res = ast_sendto(rtcp ? transport_rtp->rtcp->s : transport_rtp->s, temp, len, flags, sa); if (res > 0) { ast_rtp_instance_set_last_tx(instance, time(NULL)); } @@ -3007,22 +3064,10 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad } #endif -/*! \pre instance is locked */ -static int ast_rtp_new(struct ast_rtp_instance *instance, - struct ast_sched_context *sched, struct ast_sockaddr *addr, - void *data) +static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp) { - struct ast_rtp *rtp = NULL; int x, startplace; - /* Create a new RTP structure to hold all of our data */ - if (!(rtp = ast_calloc(1, sizeof(*rtp)))) { - return -1; - } - - /* Set default parameters on the newly created RTP structure */ - rtp->ssrc = ast_random(); - rtp->seqno = ast_random() & 0x7fff; rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); if (strictrtp) { rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno); @@ -3032,10 +3077,9 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, /* Create a new socket for us to listen on and use */ if ((rtp->s = create_new_socket("RTP", - ast_sockaddr_is_ipv4(addr) ? AF_INET : - ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) { + ast_sockaddr_is_ipv4(&rtp->bind_address) ? AF_INET : + ast_sockaddr_is_ipv6(&rtp->bind_address) ? AF_INET6 : -1)) < 0) { ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance); - ast_free(rtp); return -1; } @@ -3045,11 +3089,11 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, startplace = x; for (;;) { - ast_sockaddr_set_port(addr, x); + ast_sockaddr_set_port(&rtp->bind_address, x); /* Try to bind, this will tell us whether the port is available or not */ - if (!ast_bind(rtp->s, addr)) { + if (!ast_bind(rtp->s, &rtp->bind_address)) { ast_debug(1, "Allocated port %d for RTP instance '%p'\n", x, instance); - ast_rtp_instance_set_local_address(instance, addr); + ast_rtp_instance_set_local_address(instance, &rtp->bind_address); break; } @@ -3062,7 +3106,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, if (x == startplace || (errno != EADDRINUSE && errno != EACCES)) { ast_log(LOG_ERROR, "Oh dear... we couldn't allocate a port for RTP instance '%p'\n", instance); close(rtp->s); - ast_free(rtp); return -1; } } @@ -3073,40 +3116,30 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag)); generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd)); -#endif - ast_rtp_instance_set_data(instance, rtp); -#ifdef HAVE_PJPROJECT + /* Create an ICE session for ICE negotiation */ if (icesupport) { rtp->ice_num_components = 2; - ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance); - if (ice_create(instance, addr, x, 0)) { + ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->bind_address), x, instance); + if (ice_create(instance, &rtp->bind_address, x, 0)) { ast_log(LOG_NOTICE, "Failed to create ICE session\n"); } else { rtp->ice_port = x; - ast_sockaddr_copy(&rtp->ice_original_rtp_addr, addr); + ast_sockaddr_copy(&rtp->ice_original_rtp_addr, &rtp->bind_address); } } #endif - /* Record any information we may need */ - rtp->sched = sched; #ifdef HAVE_OPENSSL_SRTP rtp->rekeyid = -1; rtp->dtls.timeout_timer = -1; #endif - rtp->f.subclass.format = ao2_bump(ast_format_none); - rtp->lastrxformat = ao2_bump(ast_format_none); - rtp->lasttxformat = ao2_bump(ast_format_none); - return 0; } -/*! \pre instance is locked */ -static int ast_rtp_destroy(struct ast_rtp_instance *instance) +static void rtp_deallocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp) { - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); #ifdef HAVE_PJPROJECT struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_STATE_WAIT_TIME, 1000)); struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, }; @@ -3116,35 +3149,16 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) ast_rtp_dtls_stop(instance); #endif - /* Destroy the smoother that was smoothing out audio if present */ - if (rtp->smoother) { - ast_smoother_free(rtp->smoother); - } - /* Close our own socket so we no longer get packets */ if (rtp->s > -1) { close(rtp->s); + rtp->s = -1; } /* Destroy RTCP if it was being used */ - if (rtp->rtcp) { - /* - * It is not possible for there to be an active RTCP scheduler - * entry at this point since it holds a reference to the - * RTP instance while it's active. - */ + if (rtp->rtcp && rtp->rtcp->s > -1) { close(rtp->rtcp->s); - ast_free(rtp->rtcp->local_addr_str); - ast_free(rtp->rtcp); - } - - /* Destroy RED if it was being used */ - if (rtp->red) { - ao2_unlock(instance); - AST_SCHED_DEL(rtp->sched, rtp->red->schedid); - ao2_lock(instance); - ast_free(rtp->red); - rtp->red = NULL; + rtp->rtcp->s = -1; } #ifdef HAVE_PJPROJECT @@ -3165,6 +3179,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) { ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts); } + rtp->turn_rtp = NULL; } /* Destroy the RTCP TURN relay if being used */ @@ -3178,6 +3193,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) { ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts); } + rtp->turn_rtcp = NULL; } /* Destroy any ICE session */ @@ -3186,10 +3202,12 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) /* Destroy any candidates */ if (rtp->ice_local_candidates) { ao2_ref(rtp->ice_local_candidates, -1); + rtp->ice_local_candidates = NULL; } if (rtp->ice_active_remote_candidates) { ao2_ref(rtp->ice_active_remote_candidates, -1); + rtp->ice_active_remote_candidates = NULL; } if (rtp->ioqueue) { @@ -3201,17 +3219,109 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) ao2_unlock(instance); rtp_ioqueue_thread_remove(rtp->ioqueue); ao2_lock(instance); + rtp->ioqueue = NULL; } #endif +} + +/*! \pre instance is locked */ +static int ast_rtp_new(struct ast_rtp_instance *instance, + struct ast_sched_context *sched, struct ast_sockaddr *addr, + void *data) +{ + struct ast_rtp *rtp = NULL; + + /* Create a new RTP structure to hold all of our data */ + if (!(rtp = ast_calloc(1, sizeof(*rtp)))) { + return -1; + } + + /* Set default parameters on the newly created RTP structure */ + rtp->ssrc = ast_random(); + ast_uuid_generate_str(rtp->cname, sizeof(rtp->cname)); + rtp->seqno = ast_random() & 0x7fff; + rtp->sched = sched; + ast_sockaddr_copy(&rtp->bind_address, addr); + + /* Transport creation operations can grab the RTP data from the instance, so set it */ + ast_rtp_instance_set_data(instance, rtp); + + if (rtp_allocate_transport(instance, rtp)) { + ast_free(rtp); + return -1; + } + + rtp->f.subclass.format = ao2_bump(ast_format_none); + rtp->lastrxformat = ao2_bump(ast_format_none); + rtp->lasttxformat = ao2_bump(ast_format_none); + rtp->stream_num = -1; + AST_VECTOR_INIT(&rtp->ssrc_mapping, 1); + + return 0; +} + +/*! + * \brief SSRC mapping comparator for AST_VECTOR_REMOVE_CMP_UNORDERED() + * + * \param elem Element to compare against + * \param value Value to compare with the vector element. + * + * \return 0 if element does not match. + * \return Non-zero if element matches. + */ +#define SSRC_MAPPING_ELEM_CMP(elem, value) ((elem).ssrc == (value)) + +/*! \pre instance is locked */ +static int ast_rtp_destroy(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (rtp->bundled) { + struct ast_rtp *bundled_rtp; + + /* We can't hold our instance lock while removing ourselves from the parent */ + ao2_unlock(instance); + + ao2_lock(rtp->bundled); + bundled_rtp = ast_rtp_instance_get_data(rtp->bundled); + AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, rtp->themssrc, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP); + ao2_unlock(rtp->bundled); + + ao2_lock(instance); + ao2_ref(rtp->bundled, -1); + } + + rtp_deallocate_transport(instance, rtp); + + /* Destroy the smoother that was smoothing out audio if present */ + if (rtp->smoother) { + ast_smoother_free(rtp->smoother); + } + + /* Destroy RTCP if it was being used */ + if (rtp->rtcp) { + /* + * It is not possible for there to be an active RTCP scheduler + * entry at this point since it holds a reference to the + * RTP instance while it's active. + */ + ast_free(rtp->rtcp->local_addr_str); + ast_free(rtp->rtcp); + } + + /* Destroy RED if it was being used */ + if (rtp->red) { + ao2_unlock(instance); + AST_SCHED_DEL(rtp->sched, rtp->red->schedid); + ao2_lock(instance); + ast_free(rtp->red); + rtp->red = NULL; + } ao2_cleanup(rtp->lasttxformat); ao2_cleanup(rtp->lastrxformat); ao2_cleanup(rtp->f.subclass.format); - -#ifdef HAVE_PJPROJECT - /* Destroy synchronization items */ - ast_cond_destroy(&rtp->cond); -#endif + AST_VECTOR_FREE(&rtp->ssrc_mapping); /* Finally destroy ourselves */ ast_free(rtp); @@ -3461,21 +3571,18 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance) struct ast_srtp *rtcp_srtp = ast_rtp_instance_get_srtp(instance, 1); unsigned int ssrc = ast_random(); - if (!rtp->lastts) { - ast_debug(3, "Not changing SSRC since we haven't sent any RTP yet\n"); - return; - } - - /* We simply set this bit so that the next packet sent will have the marker bit turned on */ - ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); + if (rtp->lastts) { + /* We simply set this bit so that the next packet sent will have the marker bit turned on */ + ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); - ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc); + ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc); - if (srtp) { - ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc); - res_srtp->change_source(srtp, rtp->ssrc, ssrc); - if (rtcp_srtp != srtp) { - res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc); + if (srtp) { + ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc); + res_srtp->change_source(srtp, rtp->ssrc, ssrc); + if (rtcp_srtp != srtp) { + res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc); + } } } @@ -3590,14 +3697,13 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr) struct timeval now; unsigned int now_lsw; unsigned int now_msw; - unsigned int *rtcpheader; + unsigned char *rtcpheader; unsigned int lost_packets; int fraction_lost; struct timeval dlsr = { 0, }; - char bdata[512]; + unsigned char bdata[512] = ""; int rate = rtp_get_rate(rtp->f.subclass.format); int ice; - int header_offset = 0; struct ast_sockaddr remote_address = { { 0, } }; struct ast_rtp_rtcp_report_block *report_block = NULL; RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, @@ -3651,38 +3757,42 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr) } } timeval2ntp(rtcp_report->sender_information.ntp_timestamp, &now_msw, &now_lsw); - rtcpheader = (unsigned int *)bdata; - rtcpheader[1] = htonl(rtcp_report->ssrc); /* Our SSRC */ + rtcpheader = bdata; + put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc)); /* Our SSRC */ len += 8; if (sr) { - header_offset = 5; - rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ - rtcpheader[3] = htonl(now_lsw); /* now, LSW */ - rtcpheader[4] = htonl(rtcp_report->sender_information.rtp_timestamp); - rtcpheader[5] = htonl(rtcp_report->sender_information.packet_count); - rtcpheader[6] = htonl(rtcp_report->sender_information.octet_count); + put_unaligned_uint32(rtcpheader + len, htonl(now_msw)); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ + put_unaligned_uint32(rtcpheader + len + 4, htonl(now_lsw)); /* now, LSW */ + put_unaligned_uint32(rtcpheader + len + 8, htonl(rtcp_report->sender_information.rtp_timestamp)); + put_unaligned_uint32(rtcpheader + len + 12, htonl(rtcp_report->sender_information.packet_count)); + put_unaligned_uint32(rtcpheader + len + 16, htonl(rtcp_report->sender_information.octet_count)); len += 20; } if (report_block) { - rtcpheader[2 + header_offset] = htonl(report_block->source_ssrc); /* Their SSRC */ - rtcpheader[3 + header_offset] = htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets); - rtcpheader[4 + header_offset] = htonl(report_block->highest_seq_no); - rtcpheader[5 + header_offset] = htonl(report_block->ia_jitter); - rtcpheader[6 + header_offset] = htonl(report_block->lsr); - rtcpheader[7 + header_offset] = htonl(report_block->dlsr); + put_unaligned_uint32(rtcpheader + len, htonl(report_block->source_ssrc)); /* Their SSRC */ + put_unaligned_uint32(rtcpheader + len + 4, htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets)); + put_unaligned_uint32(rtcpheader + len + 8, htonl(report_block->highest_seq_no)); + put_unaligned_uint32(rtcpheader + len + 12, htonl(report_block->ia_jitter)); + put_unaligned_uint32(rtcpheader + len + 16, htonl(report_block->lsr)); + put_unaligned_uint32(rtcpheader + len + 20, htonl(report_block->dlsr)); len += 24; } - rtcpheader[0] = htonl((2 << 30) | (rtcp_report->reception_report_count << 24) - | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1)); - /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ - /* it can change mid call, and SDES can't) */ - rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); - rtcpheader[(len/4)+1] = htonl(rtcp_report->ssrc); - rtcpheader[(len/4)+2] = htonl(0x01 << 24); - len += 12; + put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (rtcp_report->reception_report_count << 24) + | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1))); + + put_unaligned_uint32(rtcpheader + len, htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | (2 + (AST_UUID_STR_LEN / 4)))); + put_unaligned_uint32(rtcpheader + len + 4, htonl(rtcp_report->ssrc)); + put_unaligned_uint16(rtcpheader + len + 8, htonl(0x01 << 24)); + put_unaligned_uint16(rtcpheader + len + 9, htonl(AST_UUID_STR_LEN << 24)); + memcpy(rtcpheader + len + 10, rtp->cname, AST_UUID_STR_LEN); + len += 12 + AST_UUID_STR_LEN; - ast_sockaddr_copy(&remote_address, &rtp->rtcp->them); + if (rtp->bundled) { + ast_rtp_instance_get_remote_address(instance, &remote_address); + } else { + ast_sockaddr_copy(&remote_address, &rtp->rtcp->them); + } res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice); if (res < 0) { ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n", @@ -3959,7 +4069,6 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr /* VP8: is this a request to send a RTCP FIR? */ if (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) { - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); unsigned int *rtcpheader; char bdata[1024]; int len = 20; @@ -3989,7 +4098,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr rtcpheader[2] = htonl(rtp->themssrc); rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */ rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */ - res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &rtp->rtcp->them, &ice); + res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? &remote_address : &rtp->rtcp->them, &ice); if (res < 0) { ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno)); } @@ -4554,9 +4663,29 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets) rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; } +/*! \pre instance is locked */ +static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance, + struct ast_rtp *rtp, unsigned int ssrc) +{ + int index; + struct ast_rtp_instance *found = instance; + + for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) { + struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index); + + if (mapping->ssrc == ssrc) { + found = mapping->instance; + break; + } + } + + return found; +} + static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr) { - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct ast_rtp_instance *transport = instance; + struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(instance); unsigned int *rtcpheader = (unsigned int *)(rtcpdata); int packetwords, position = 0; int report_counter = 0; @@ -4565,13 +4694,13 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c packetwords = size / 4; - if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) { + if (ast_rtp_instance_get_prop(transport, AST_RTP_PROPERTY_NAT)) { /* Send to whoever sent to us */ - if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) { - ast_sockaddr_copy(&rtp->rtcp->them, addr); + if (ast_sockaddr_cmp(&transport_rtp->rtcp->them, addr)) { + ast_sockaddr_copy(&transport_rtp->rtcp->them, addr); if (rtpdebug) { ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n", - ast_sockaddr_stringify(&rtp->rtcp->them)); + ast_sockaddr_stringify(&transport_rtp->rtcp->them)); } } } @@ -4583,6 +4712,8 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c unsigned int length; struct ast_json *message_blob; RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup); + struct ast_rtp_instance *child; + struct ast_rtp *rtp; i = position; length = ntohl(rtcpheader[i]); @@ -4614,6 +4745,21 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc); } + /* Determine the appropriate instance for this */ + child = rtp_find_instance_by_ssrc(transport, transport_rtp, rtcp_report->ssrc); + if (child != transport) { + /* It is safe to hold the child lock while holding the parent lock, we guarantee that the locking order + * is always parent->child or that the child lock is not held when acquiring the parent lock. + */ + ao2_lock(child); + instance = child; + rtp = ast_rtp_instance_get_data(instance); + } else { + /* The child is the parent! We don't need to unlock it. */ + child = NULL; + rtp = transport_rtp; + } + i += 2; /* Advance past header and ssrc */ switch (pt) { case RTCP_PT_SR: @@ -4649,6 +4795,9 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c /* Don't handle multiple reception reports (rc > 1) yet */ report_block = ast_calloc(1, sizeof(*report_block)); if (!report_block) { + if (child) { + ao2_unlock(child); + } return &ast_null_frame; } rtcp_report->report_block[report_counter] = report_block; @@ -4695,8 +4844,8 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c */ message_blob = ast_json_pack("{s: s, s: s, s: f}", - "from", ast_sockaddr_stringify(&rtp->rtcp->them), - "to", rtp->rtcp->local_addr_str, + "from", ast_sockaddr_stringify(&transport_rtp->rtcp->them), + "to", transport_rtp->rtcp->local_addr_str, "rtt", rtp->rtcp->rtt); ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(), rtcp_report, @@ -4705,26 +4854,26 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report * object as a its data */ - rtp->f.frametype = AST_FRAME_RTCP; - rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET; - memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report)); - rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report); + transport_rtp->f.frametype = AST_FRAME_RTCP; + transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET; + memcpy(transport_rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report)); + transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report); if (rc > 0) { /* There's always a single report block stored, here */ struct ast_rtp_rtcp_report *rtcp_report2; - report_block = rtp->f.data.ptr + rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *); + report_block = transport_rtp->f.data.ptr + transport_rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *); memcpy(report_block, rtcp_report->report_block[report_counter-1], sizeof(struct ast_rtp_rtcp_report_block)); - rtcp_report2 = (struct ast_rtp_rtcp_report *)rtp->f.data.ptr; + rtcp_report2 = (struct ast_rtp_rtcp_report *)transport_rtp->f.data.ptr; rtcp_report2->report_block[report_counter-1] = report_block; - rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block); + transport_rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block); } - rtp->f.offset = AST_FRIENDLY_OFFSET; - rtp->f.samples = 0; - rtp->f.mallocd = 0; - rtp->f.delivery.tv_sec = 0; - rtp->f.delivery.tv_usec = 0; - rtp->f.src = "RTP"; - f = &rtp->f; + transport_rtp->f.offset = AST_FRIENDLY_OFFSET; + transport_rtp->f.samples = 0; + transport_rtp->f.mallocd = 0; + transport_rtp->f.delivery.tv_sec = 0; + transport_rtp->f.delivery.tv_usec = 0; + transport_rtp->f.src = "RTP"; + f = &transport_rtp->f; break; case RTCP_PT_FUR: /* Handle RTCP FIR as FUR */ @@ -4732,34 +4881,38 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c if (rtcp_debug_test_addr(addr)) { ast_verbose("Received an RTCP Fast Update Request\n"); } - rtp->f.frametype = AST_FRAME_CONTROL; - rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE; - rtp->f.datalen = 0; - rtp->f.samples = 0; - rtp->f.mallocd = 0; - rtp->f.src = "RTP"; - f = &rtp->f; + transport_rtp->f.frametype = AST_FRAME_CONTROL; + transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE; + transport_rtp->f.datalen = 0; + transport_rtp->f.samples = 0; + transport_rtp->f.mallocd = 0; + transport_rtp->f.src = "RTP"; + f = &transport_rtp->f; break; case RTCP_PT_SDES: if (rtcp_debug_test_addr(addr)) { ast_verbose("Received an SDES from %s\n", - ast_sockaddr_stringify(&rtp->rtcp->them)); + ast_sockaddr_stringify(&transport_rtp->rtcp->them)); } break; case RTCP_PT_BYE: if (rtcp_debug_test_addr(addr)) { ast_verbose("Received a BYE from %s\n", - ast_sockaddr_stringify(&rtp->rtcp->them)); + ast_sockaddr_stringify(&transport_rtp->rtcp->them)); } break; default: ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n", - pt, ast_sockaddr_stringify(&rtp->rtcp->them)); + pt, ast_sockaddr_stringify(&transport_rtp->rtcp->them)); break; } position += (length + 1); + rtp->rtcp->rtcp_info = 1; + + if (child) { + ao2_unlock(child); + } } - rtp->rtcp->rtcp_info = 1; return f; @@ -4951,11 +5104,19 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, return 0; } +static void rtp_instance_unlock(struct ast_rtp_instance *instance) +{ + if (instance) { + ao2_unlock(instance); + } +} + /*! \pre instance is locked */ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); struct ast_rtp_instance *instance1; + RAII_VAR(struct ast_rtp_instance *, child, NULL, rtp_instance_unlock); struct ast_sockaddr addr; int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno; unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET; @@ -4973,11 +5134,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc return &ast_null_frame; } - /* If we are currently sending DTMF to the remote party send a continuation packet */ - if (rtp->sending_digit) { - ast_rtp_dtmf_continuation(instance); - } - /* Actually read in the data from the socket */ if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0, &addr)) < 0) { @@ -5099,6 +5255,33 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc } } + /* If the version is not what we expected by this point then just drop the packet */ + if (version != 2) { + return &ast_null_frame; + } + + /* We use the SSRC to determine what RTP instance this packet is actually for */ + ssrc = ntohl(rtpheader[2]); + + /* Determine the appropriate instance for this */ + child = rtp_find_instance_by_ssrc(instance, rtp, ssrc); + if (child != instance) { + /* It is safe to hold the child lock while holding the parent lock, we guarantee that the locking order + * is always parent->child or that the child lock is not held when acquiring the parent lock. + */ + ao2_lock(child); + instance = child; + rtp = ast_rtp_instance_get_data(instance); + } else { + /* The child is the parent! We don't need to unlock it. */ + child = NULL; + } + + /* If we are currently sending DTMF to the remote party send a continuation packet */ + if (rtp->sending_digit) { + ast_rtp_dtmf_continuation(instance); + } + /* If we are directly bridged to another instance send the audio directly out */ instance1 = ast_rtp_instance_get_bridged(instance); if (instance1 @@ -5106,11 +5289,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc return &ast_null_frame; } - /* If the version is not what we expected by this point then just drop the packet */ - if (version != 2) { - return &ast_null_frame; - } - /* Pull out the various other fields we will need */ payloadtype = (seqno & 0x7f0000) >> 16; padding = seqno & (1 << 29); @@ -5119,7 +5297,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc cc = (seqno & 0xF000000) >> 24; seqno &= 0xffff; timestamp = ntohl(rtpheader[1]); - ssrc = ntohl(rtpheader[2]); AST_LIST_HEAD_INIT_NOLOCK(&frames); /* Force a marker bit and change SSRC if the SSRC changes */ @@ -5293,6 +5470,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc rtp->f.data.ptr = read_area + hdrlen; rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; rtp->f.seqno = seqno; + rtp->f.stream_num = rtp->stream_num; if ((ast_format_cmp(rtp->f.subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL) && ((int)seqno - (prev_seqno + 1) > 0) @@ -5554,6 +5732,7 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); struct ast_sockaddr local; + int index; ast_rtp_instance_get_local_address(instance, &local); if (!ast_sockaddr_isnull(addr)) { @@ -5582,6 +5761,13 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local)); } + /* Update any bundled RTP instances */ + for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) { + struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index); + + ast_rtp_instance_set_remote_address(mapping->instance, addr); + } + rtp->rxseqno = 0; if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN) { @@ -5865,43 +6051,105 @@ static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance) /*! \pre instance is locked */ static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance) { - /* XXX - * - * Asterisk currently puts a zero-length CNAME value in RTCP SDES items, - * meaning our CNAME will always be an empty string. In future, should - * Asterisk actually start using meaningful CNAMEs, this function will - * need to return that instead of an empty string - */ - return ""; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + return rtp->cname; } -#ifdef HAVE_OPENSSL_SRTP -static void dtls_perform_setup(struct dtls_details *dtls) +static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc) { - if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) { + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (rtp->themssrc) { return; } - SSL_clear(dtls->ssl); - if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(dtls->ssl); - } else { - SSL_set_connect_state(dtls->ssl); - } - dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; + rtp->themssrc = ssrc; } -/*! \pre instance is locked */ -static int ast_rtp_activate(struct ast_rtp_instance *instance) +static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - dtls_perform_setup(&rtp->dtls); + rtp->stream_num = stream_num; +} - if (rtp->rtcp) { - dtls_perform_setup(&rtp->rtcp->dtls); +static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent) +{ + struct ast_rtp *child_rtp = ast_rtp_instance_get_data(child); + struct ast_rtp *parent_rtp = ast_rtp_instance_get_data(parent); + struct rtp_ssrc_mapping mapping; + struct ast_sockaddr them = { { 0, } }; + + if (child_rtp->bundled == parent) { + return 0; } + /* If this instance was already bundled then remove the SSRC mapping */ + if (child_rtp->bundled) { + struct ast_rtp *bundled_rtp; + + ao2_unlock(child); + + /* The child lock can't be held while accessing the parent */ + ao2_lock(child_rtp->bundled); + bundled_rtp = ast_rtp_instance_get_data(child_rtp->bundled); + AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, child_rtp->themssrc, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP); + ao2_unlock(child_rtp->bundled); + + ao2_lock(child); + ao2_ref(child_rtp->bundled, -1); + child_rtp->bundled = NULL; + } + + if (!parent) { + /* We transitioned away from bundle so we need our own transport resources once again */ + rtp_allocate_transport(child, child_rtp); + return 0; + } + + /* We no longer need any transport related resources as we will use our parent RTP instance instead */ + rtp_deallocate_transport(child, child_rtp); + + /* Children maintain a reference to the parent to guarantee that the transport doesn't go away on them */ + child_rtp->bundled = ao2_bump(parent); + + mapping.ssrc = child_rtp->themssrc; + mapping.instance = child; + + ao2_unlock(child); + + ao2_lock(parent); + + AST_VECTOR_APPEND(&parent_rtp->ssrc_mapping, mapping); + +#ifdef HAVE_OPENSSL_SRTP + /* If DTLS-SRTP is already in use then add the local SSRC to it, otherwise it will get added once DTLS + * negotiation has been completed. + */ + if (parent_rtp->dtls.connection == AST_RTP_DTLS_CONNECTION_EXISTING) { + dtls_srtp_add_local_ssrc(parent_rtp, ast_rtp_instance_get_srtp(parent, 0), parent, 0, child_rtp->ssrc, 0); + } +#endif + + /* Bundle requires that RTCP-MUX be in use so only the main remote address needs to match */ + ast_rtp_instance_get_remote_address(parent, &them); + + ao2_unlock(parent); + + ao2_lock(child); + + ast_rtp_instance_set_remote_address(child, &them); + + return 0; +} + +#ifdef HAVE_OPENSSL_SRTP +/*! \pre instance is locked */ +static int ast_rtp_activate(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */ #ifdef HAVE_PJPROJECT if (rtp->ice) { @@ -5909,9 +6157,11 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance) } #endif + dtls_perform_setup(&rtp->dtls); dtls_perform_handshake(instance, &rtp->dtls, 0); if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { + dtls_perform_setup(&rtp->rtcp->dtls); dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); } |