diff options
author | Joshua Colp <jcolp@digium.com> | 2017-03-16 07:39:51 -0500 |
---|---|---|
committer | Gerrit Code Review <gerrit2@gerrit.digium.api> | 2017-03-16 07:39:51 -0500 |
commit | 57be9cf8f98c5e93b60cafb1cc8cbbed154f408a (patch) | |
tree | 9df3b719b89719675d40fa36a19c7815d752b7ba /res | |
parent | 701b753a0bc4e136ab447a855d6b5db5a6b59ab3 (diff) | |
parent | 7bc69753bc79fa47508c5d62e9bc27f41698bb41 (diff) |
Merge "Add rtcp-mux support" into 13
Diffstat (limited to 'res')
-rw-r--r-- | res/res_pjsip.c | 10 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 1 | ||||
-rw-r--r-- | res/res_pjsip_sdp_rtp.c | 68 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 367 |
4 files changed, 323 insertions, 123 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 347658f9c..8d2b8f763 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -944,6 +944,16 @@ to the receiving one. </para></description> </configOption> + <configOption name="rtcp_mux" default="no"> + <synopsis>Enable RFC 5761 RTCP multiplexing on the RTP port</synopsis> + <description><para> + With this option enabled, Asterisk will attempt to negotiate the use of the "rtcp-mux" + attribute on all media streams. This will result in RTP and RTCP being sent and received + on the same port. This shifts the demultiplexing logic to the application rather than + the transport layer. This option is useful when interoperating with WebRTC endpoints + since they mandate this option's use. + </para></description> + </configOption> </configObject> <configObject name="auth"> <synopsis>Authentication type</synopsis> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index bfaf750d4..eb8e19712 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1938,6 +1938,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtcp_mux)); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index b27050ed8..c8ad051d3 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -169,6 +169,23 @@ static int rtp_check_timeout(const void *data) return 0; } +/*! + * \brief Enable RTCP on an RTP session. + */ +static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_media *remote_media) +{ + enum ast_rtp_instance_rtcp rtcp_type; + + if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux) { + rtcp_type = AST_RTP_INSTANCE_RTCP_MUX; + } else { + rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD; + } + + ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type); +} + /*! \brief Internal function which creates an RTP instance */ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media) { @@ -186,7 +203,6 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me return -1; } - ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1); ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric); if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) { @@ -555,6 +571,13 @@ static void process_ice_attributes(struct ast_sip_session *session, struct ast_s continue; } + if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux && candidate.id > 1) { + /* Remote side may have offered RTP and RTCP candidates. However, if we're using RTCP MUX, + * then we should ignore RTCP candidates. + */ + continue; + } + candidate.foundation = foundation; candidate.transport = transport; @@ -851,6 +874,26 @@ static int setup_media_encryption(struct ast_sip_session *session, return 0; } +static void set_ice_components(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +{ + struct ast_rtp_engine_ice *ice; + + ast_assert(session_media->rtp != NULL); + + ice = ast_rtp_instance_get_ice(session_media->rtp); + if (!session->endpoint->media.rtp.ice_support || !ice) { + return; + } + + if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux) { + /* We both support RTCP mux. Only one ICE component necessary */ + ice->change_components(session_media->rtp, 1); + } else { + /* They either don't support RTCP mux or we don't know if they do yet. */ + ice->change_components(session_media->rtp, 2); + } +} + /*! \brief Function which negotiates an incoming media stream */ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) @@ -895,6 +938,11 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct return -1; } + session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL); + set_ice_components(session, session_media); + + enable_rtcp(session, session_media, stream); + res = setup_media_encryption(session, session_media, sdp, stream); if (res) { if (!session->endpoint->media.rtp.encryption_optimistic || @@ -1065,6 +1113,9 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as return -1; } + set_ice_components(session, session_media); + enable_rtcp(session, session_media, NULL); + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { return -1; @@ -1228,6 +1279,12 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as attr->name = STR_SENDRECV; media->attr[media->attr_count++] = attr; + /* If we've got rtcp-mux enabled, just unconditionally offer it in all SDPs */ + if (session->endpoint->rtcp_mux) { + attr = pjmedia_sdp_attr_create(pool, "rtcp-mux", NULL); + pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr); + } + /* Add the media stream to the SDP */ sdp->media[sdp->media_count++] = media; @@ -1262,6 +1319,11 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a return -1; } + session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL); + set_ice_components(session, session_media); + + enable_rtcp(session, session_media, remote_stream); + res = setup_media_encryption(session, session_media, remote, remote_stream); if (!session->endpoint->media.rtp.encryption_optimistic && res) { /* If optimistic encryption is disabled and crypto should have been enabled but was not @@ -1293,7 +1355,9 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a return -1; } ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0)); - ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1)); + if (!session->endpoint->rtcp_mux || !session_media->remote_rtcp_mux) { + ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1)); + } /* If ICE support is enabled find all the needed attributes */ process_ice_attributes(session, session_media, remote, remote_stream); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 346db604c..62fe4fd4a 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -331,6 +331,7 @@ struct ast_rtp { struct ao2_container *ice_active_remote_candidates; /*!< The remote ICE candidates */ struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */ struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */ + unsigned int ice_num_components; /*!< The number of ICE components */ #endif #ifdef HAVE_OPENSSL_SRTP @@ -419,6 +420,7 @@ struct ast_rtcp { * own address every time */ char *local_addr_str; + enum ast_rtp_instance_rtcp type; }; struct rtp_red { @@ -660,6 +662,22 @@ static int ice_reset_session(struct ast_rtp_instance *instance) pj_ice_sess_change_role(rtp->ice, role); } + /* If we only have one component now, and we previously set up TURN for RTCP, + * we need to destroy that TURN socket. + */ + if (rtp->ice_num_components == 1 && rtp->turn_rtcp) { + 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, }; + + ast_mutex_lock(&rtp->lock); + pj_turn_sock_destroy(rtp->turn_rtcp); + rtp->turn_state = PJ_TURN_STATE_NULL; + while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) { + ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts); + } + ast_mutex_unlock(&rtp->lock); + } + return res; } @@ -775,11 +793,12 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance) ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance); } - if (!has_rtcp) { + /* If we're only dealing with one ICE component, then we don't care about the lack of RTCP candidates */ + if (!has_rtcp && rtp->ice_num_components > 1) { ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance); } - if (has_rtp && has_rtcp) { + if (has_rtp && (has_rtcp || rtp->ice_num_components == 1)) { pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]); char reason[80]; @@ -1271,6 +1290,21 @@ static char *generate_random_string(char *buf, size_t size) return buf; } +static void ast_rtp_ice_change_components(struct ast_rtp_instance *instance, int num_components) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + /* Don't do anything if ICE is unsupported or if we're not changing the + * number of components + */ + if (!icesupport || !rtp->ice || rtp->ice_num_components == num_components) { + return; + } + + rtp->ice_num_components = num_components; + ice_reset_session(instance); +} + /* ICE RTP Engine interface declaration */ static struct ast_rtp_engine_ice ast_rtp_ice = { .set_authentication = ast_rtp_ice_set_authentication, @@ -1283,6 +1317,7 @@ static struct ast_rtp_engine_ice ast_rtp_ice = { .ice_lite = ast_rtp_ice_lite, .set_role = ast_rtp_ice_set_role, .turn_request = ast_rtp_ice_turn_request, + .change_components = ast_rtp_ice_change_components, }; #endif @@ -1542,6 +1577,7 @@ static int ast_rtp_dtls_active(struct ast_rtp_instance *instance) static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + int rtcp_dtls_unique = (rtp->dtls.ssl != rtp->rtcp->dtls.ssl); dtls_srtp_stop_timeout_timer(instance, rtp, 0); @@ -1559,7 +1595,7 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) if (rtp->rtcp) { dtls_srtp_stop_timeout_timer(instance, rtp, 1); - if (rtp->rtcp->dtls.ssl) { + if (rtp->rtcp->dtls.ssl && rtcp_dtls_unique) { SSL_free(rtp->rtcp->dtls.ssl); rtp->rtcp->dtls.ssl = NULL; ast_mutex_destroy(&rtp->rtcp->dtls.lock); @@ -1787,7 +1823,7 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) #ifdef HAVE_OPENSSL_SRTP dtls_perform_handshake(instance, &rtp->dtls, 0); - if (rtp->rtcp) { + if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); } #endif @@ -2027,7 +2063,7 @@ static int dtls_srtp_renegotiate(const void *data) SSL_do_handshake(rtp->dtls.ssl); dtls_srtp_check_pending(instance, rtp, 0); - if (rtp->rtcp && rtp->rtcp->dtls.ssl) { + if (rtp->rtcp && rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) { SSL_renegotiate(rtp->rtcp->dtls.ssl); SSL_do_handshake(rtp->rtcp->dtls.ssl); dtls_srtp_check_pending(instance, rtp, 1); @@ -2618,7 +2654,7 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad passwd = pj_str(rtp->local_passwd); /* Create an ICE session for ICE negotiation */ - if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2, + if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, rtp->ice_num_components, &ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &rtp->ice) == PJ_SUCCESS) { /* Make this available for the callbacks */ rtp->ice->user_data = instance; @@ -2627,9 +2663,10 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP, TRANSPORT_SOCKET_RTP); - /* Only add the RTCP candidates to ICE when replacing the session. New sessions + /* Only add the RTCP candidates to ICE when replacing the session and if + * the ICE session contains more than just an RTP component. New sessions * handle this in a separate part of the setup phase */ - if (replace && rtp->rtcp) { + if (replace && rtp->rtcp && rtp->ice_num_components > 1) { rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP); @@ -2714,6 +2751,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, #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_log(LOG_NOTICE, "Failed to start ICE session\n"); @@ -2723,7 +2761,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, } } #endif - /* Record any information we may need */ rtp->sched = sched; @@ -4154,63 +4191,21 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets) rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; } -static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) +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_sockaddr addr; - unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET]; - unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); - int res, packetwords, position = 0; + unsigned int *rtcpheader = (unsigned int *)(rtcpdata); + int packetwords, position = 0; int report_counter = 0; struct ast_rtp_rtcp_report_block *report_block; struct ast_frame *f = &ast_null_frame; - /* Read in RTCP data from the socket */ - if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET, - sizeof(rtcpdata) - AST_FRIENDLY_OFFSET, - 0, &addr)) < 0) { - ast_assert(errno != EBADF); - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", - (errno) ? strerror(errno) : "Unspecified"); - return NULL; - } - return &ast_null_frame; - } - - /* If this was handled by the ICE session don't do anything further */ - if (!res) { - return &ast_null_frame; - } - - if (!*(rtcpdata + AST_FRIENDLY_OFFSET)) { - struct sockaddr_in addr_tmp; - struct ast_sockaddr addr_v4; - - if (ast_sockaddr_is_ipv4(&addr)) { - ast_sockaddr_to_sin(&addr, &addr_tmp); - } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) { - ast_debug(1, "Using IPv6 mapped address %s for STUN\n", - ast_sockaddr_stringify(&addr)); - ast_sockaddr_to_sin(&addr_v4, &addr_tmp); - } else { - ast_debug(1, "Cannot do STUN for non IPv4 address %s\n", - ast_sockaddr_stringify(&addr)); - return &ast_null_frame; - } - if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, rtcpdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT)) { - ast_sockaddr_from_sin(&addr, &addr_tmp); - ast_sockaddr_copy(&rtp->rtcp->them, &addr); - } - return &ast_null_frame; - } - - packetwords = res / 4; + packetwords = size / 4; if (ast_rtp_instance_get_prop(instance, 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(&rtp->rtcp->them, addr)) { + ast_sockaddr_copy(&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)); @@ -4218,7 +4213,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) } } - ast_debug(1, "Got RTCP report of %d bytes\n", res); + ast_debug(1, "Got RTCP report of %zu bytes\n", size); while (position < packetwords) { int i, pt, rc; @@ -4246,9 +4241,9 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) return &ast_null_frame; } - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose("\n\nGot RTCP from %s\n", - ast_sockaddr_stringify(&addr)); + ast_sockaddr_stringify(addr)); ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" : (pt == RTCP_PT_RR) ? "Receiver Report" : (pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown"); @@ -4271,7 +4266,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) (unsigned int)ntohl(rtcpheader[i + 1]), &rtcp_report->sender_information.ntp_timestamp); rtcp_report->sender_information.rtp_timestamp = ntohl(rtcpheader[i + 2]); - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose("NTP timestamp: %u.%06u\n", (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec, (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec); @@ -4303,7 +4298,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) report_block->dlsr = ntohl(rtcpheader[i + 5]); if (report_block->lsr && update_rtt_stats(rtp, report_block->lsr, report_block->dlsr) - && rtcp_debug_test_addr(&addr)) { + && rtcp_debug_test_addr(addr)) { struct timeval now; unsigned int lsr_now, lsw, msw; gettimeofday(&now, NULL); @@ -4320,7 +4315,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) update_lost_stats(rtp, report_block->lost_count.packets); rtp->rtcp->reported_jitter_count++; - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction); ast_verbose(" Packets lost so far: %u\n", report_block->lost_count.packets); ast_verbose(" Highest sequence number: %u\n", report_block->highest_seq_no & 0x0000ffff); @@ -4348,7 +4343,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) case RTCP_PT_FUR: /* Handle RTCP FIR as FUR */ case RTCP_PT_PSFB: - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose("Received an RTCP Fast Update Request\n"); } rtp->f.frametype = AST_FRAME_CONTROL; @@ -4360,13 +4355,13 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) f = &rtp->f; break; case RTCP_PT_SDES: - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose("Received an SDES from %s\n", ast_sockaddr_stringify(&rtp->rtcp->them)); } break; case RTCP_PT_BYE: - if (rtcp_debug_test_addr(&addr)) { + if (rtcp_debug_test_addr(addr)) { ast_verbose("Received a BYE from %s\n", ast_sockaddr_stringify(&rtp->rtcp->them)); } @@ -4381,6 +4376,58 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) rtp->rtcp->rtcp_info = 1; return f; + +} + +static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct ast_sockaddr addr; + unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET]; + unsigned char *read_area = rtcpdata + AST_FRIENDLY_OFFSET; + size_t read_area_size = sizeof(rtcpdata) - AST_FRIENDLY_OFFSET; + int res; + + /* Read in RTCP data from the socket */ + if ((res = rtcp_recvfrom(instance, read_area, read_area_size, + 0, &addr)) < 0) { + ast_assert(errno != EBADF); + if (errno != EAGAIN) { + ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", + (errno) ? strerror(errno) : "Unspecified"); + return NULL; + } + return &ast_null_frame; + } + + /* If this was handled by the ICE session don't do anything further */ + if (!res) { + return &ast_null_frame; + } + + if (!*(read_area)) { + struct sockaddr_in addr_tmp; + struct ast_sockaddr addr_v4; + + if (ast_sockaddr_is_ipv4(&addr)) { + ast_sockaddr_to_sin(&addr, &addr_tmp); + } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) { + ast_debug(1, "Using IPv6 mapped address %s for STUN\n", + ast_sockaddr_stringify(&addr)); + ast_sockaddr_to_sin(&addr_v4, &addr_tmp); + } else { + ast_debug(1, "Cannot do STUN for non IPv4 address %s\n", + ast_sockaddr_stringify(&addr)); + return &ast_null_frame; + } + if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT)) { + ast_sockaddr_from_sin(&addr, &addr_tmp); + ast_sockaddr_copy(&rtp->rtcp->them, &addr); + } + return &ast_null_frame; + } + + return ast_rtcp_interpret(instance, read_area, read_area_size, &addr); } static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen) @@ -4487,19 +4534,54 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int return 0; } +static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet) +{ + uint8_t version; + uint8_t pt; + uint8_t m; + + if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) { + return 0; + } + + version = (packet[0] & 0XC0) >> 6; + if (version == 0) { + /* version 0 indicates this is a STUN packet and shouldn't + * be interpreted as a possible RTCP packet + */ + return 0; + } + + /* The second octet of a packet will be one of the following: + * For RTP: The marker bit (1 bit) and the RTP payload type (7 bits) + * For RTCP: The payload type (8) + * + * RTP has a forbidden range of payload types (64-95) since these + * will conflict with RTCP payload numbers if the marker bit is set. + */ + m = packet[1] & 0x80; + pt = packet[1] & 0x7F; + if (m && pt >= 64 && pt <= 95) { + return 1; + } + return 0; +} + 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_sockaddr addr; int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno; - unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp; + unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET; + size_t read_area_size = sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET; + unsigned int *rtpheader = (unsigned int*)(read_area), seqno, ssrc, timestamp; RAII_VAR(struct ast_rtp_payload_type *, payload, NULL, ao2_cleanup); struct ast_sockaddr remote_address = { {0,} }; struct frame_list frames; /* If this is actually RTCP let's hop on over and handle it */ if (rtcp) { - if (rtp->rtcp) { + if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { return ast_rtcp_read(instance); } return &ast_null_frame; @@ -4511,8 +4593,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc } /* Actually read in the data from the socket */ - if ((res = rtp_recvfrom(instance, rtp->rawdata + AST_FRIENDLY_OFFSET, - sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, + if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0, &addr)) < 0) { ast_assert(errno != EBADF); if (errno != EAGAIN) { @@ -4528,12 +4609,17 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc return &ast_null_frame; } + /* This could be a multiplexed RTCP packet. If so, be sure to interpret it correctly */ + if (rtcp_mux(rtp, read_area)) { + return ast_rtcp_interpret(instance, read_area, read_area_size, &addr); + } + /* Make sure the data that was read in is actually enough to make up an RTP packet */ if (res < hdrlen) { /* If this is a keepalive containing only nulls, don't bother with a warning */ int i; for (i = 0; i < res; ++i) { - if (rtp->rawdata[AST_FRIENDLY_OFFSET + i] != '\0') { + if (read_area[i] != '\0') { ast_log(LOG_WARNING, "RTP Read too short\n"); return &ast_null_frame; } @@ -4560,7 +4646,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc ast_sockaddr_stringify(&addr)); return &ast_null_frame; } - if ((ast_stun_handle_packet(rtp->s, &addr_tmp, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) && + if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) && ast_sockaddr_isnull(&remote_address)) { ast_sockaddr_from_sin(&addr, &addr_tmp); ast_rtp_instance_set_remote_address(instance, &addr); @@ -4609,7 +4695,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc /* do not update the originally given address, but only the remote */ ast_rtp_instance_set_incoming_source_address(instance, &addr); ast_sockaddr_copy(&remote_address, &addr); - if (rtp->rtcp) { + if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { ast_sockaddr_copy(&rtp->rtcp->them, &addr); ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(&addr) + 1); } @@ -4676,7 +4762,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc /* Remove any padding bytes that may be present */ if (padding) { - res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1]; + res -= read_area[res - 1]; } /* Skip over any CSRC fields */ @@ -4750,11 +4836,11 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc * by passing the pointer to the frame list to it so that the method * can append frames to the list as needed. */ - process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames); + process_dtmf_rfc2833(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames); } else if (payload->rtp_code == AST_RTP_CISCO_DTMF) { - f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark); + f = process_dtmf_cisco(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark); } else if (payload->rtp_code == AST_RTP_CN) { - f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark); + f = process_cn_rfc3389(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark); } else { ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, @@ -4810,7 +4896,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc rtp->f.src = "RTP"; rtp->f.mallocd = 0; rtp->f.datalen = res - hdrlen; - rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; + rtp->f.data.ptr = read_area + hdrlen; rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; rtp->f.seqno = seqno; @@ -4921,19 +5007,29 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro if (value) { struct ast_sockaddr local_addr; - if (rtp->rtcp) { + if (rtp->rtcp && rtp->rtcp->type == value) { ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance); return; } - /* Setup RTCP to be activated on the next RTP write */ - if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) { - return; + + if (!rtp->rtcp) { + rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)); + if (!rtp->rtcp) { + return; + } + rtp->rtcp->s = -1; + rtp->rtcp->dtls.timeout_timer = -1; + rtp->rtcp->schedid = -1; } + rtp->rtcp->type = value; + /* Grab the IP address and port we are going to use */ ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us); - ast_sockaddr_set_port(&rtp->rtcp->us, - ast_sockaddr_port(&rtp->rtcp->us) + 1); + if (value == AST_RTP_INSTANCE_RTCP_STANDARD) { + ast_sockaddr_set_port(&rtp->rtcp->us, + ast_sockaddr_port(&rtp->rtcp->us) + 1); + } ast_sockaddr_copy(&local_addr, &rtp->rtcp->us); if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) { @@ -4943,6 +5039,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro ast_sockaddr_copy(&local_addr, &rtp->rtcp->us); } + ast_free(rtp->rtcp->local_addr_str); rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr)); if (!rtp->rtcp->local_addr_str) { ast_free(rtp->rtcp); @@ -4950,43 +5047,67 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro return; } - if ((rtp->rtcp->s = - create_new_socket("RTCP", - ast_sockaddr_is_ipv4(&rtp->rtcp->us) ? - AF_INET : - ast_sockaddr_is_ipv6(&rtp->rtcp->us) ? - AF_INET6 : -1)) < 0) { - ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance); - ast_free(rtp->rtcp->local_addr_str); - ast_free(rtp->rtcp); - rtp->rtcp = NULL; - return; - } - - /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */ - if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) { - ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance); - close(rtp->rtcp->s); - ast_free(rtp->rtcp->local_addr_str); - ast_free(rtp->rtcp); - rtp->rtcp = NULL; - return; - } - - ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance); - rtp->rtcp->schedid = -1; + if (value == AST_RTP_INSTANCE_RTCP_STANDARD) { + /* We're either setting up RTCP from scratch or + * switching from MUX. Either way, we won't have + * a socket set up, and we need to set it up + */ + if ((rtp->rtcp->s = + create_new_socket("RTCP", + ast_sockaddr_is_ipv4(&rtp->rtcp->us) ? + AF_INET : + ast_sockaddr_is_ipv6(&rtp->rtcp->us) ? + AF_INET6 : -1)) < 0) { + ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance); + ast_free(rtp->rtcp->local_addr_str); + ast_free(rtp->rtcp); + rtp->rtcp = NULL; + return; + } + /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */ + if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) { + ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance); + close(rtp->rtcp->s); + ast_free(rtp->rtcp->local_addr_str); + ast_free(rtp->rtcp); + rtp->rtcp = NULL; + return; + } #ifdef HAVE_PJPROJECT - if (rtp->ice) { - rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP); - } + if (rtp->ice) { + rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP); + } #endif - #ifdef HAVE_OPENSSL_SRTP - rtp->rtcp->dtls.timeout_timer = -1; - dtls_setup_rtcp(instance); + dtls_setup_rtcp(instance); #endif + } else { + struct ast_sockaddr addr; + /* RTCPMUX uses the same socket as RTP. If we were previously using standard RTCP + * then close the socket we previously created. + * + * It may seem as though there is a possible race condition here where we might try + * to close the RTCP socket while it is being used to send data. However, this is not + * a problem in practice since setting and adjusting of RTCP properties happens prior + * to activating RTP. It is not until RTP is activated that timers start for RTCP + * transmission + */ + if (rtp->rtcp->s > -1) { + close(rtp->rtcp->s); + } + rtp->rtcp->s = rtp->s; + ast_rtp_instance_get_remote_address(instance, &addr); + ast_sockaddr_copy(&rtp->rtcp->them, &addr); +#ifdef HAVE_OPENSSL_SRTP + if (rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + } + rtp->rtcp->dtls.ssl = rtp->dtls.ssl; +#endif + } + ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance); return; } else { if (rtp->rtcp) { @@ -5001,9 +5122,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro } rtp->rtcp->schedid = -1; } - close(rtp->rtcp->s); + if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) { + close(rtp->rtcp->s); + } #ifdef HAVE_OPENSSL_SRTP - if (rtp->rtcp->dtls.ssl) { + if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) { SSL_free(rtp->rtcp->dtls.ssl); } #endif @@ -5045,10 +5168,12 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance); ast_sockaddr_copy(&rtp->rtcp->them, addr); if (!ast_sockaddr_isnull(addr)) { - ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1); + if (rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { + ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1); - /* Update the local RTCP address with what is being used */ - ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1); + /* Update the local RTCP address with what is being used */ + ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1); + } ast_sockaddr_copy(&rtp->rtcp->us, &local); ast_free(rtp->rtcp->local_addr_str); @@ -5336,7 +5461,7 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance) dtls_perform_handshake(instance, &rtp->dtls, 0); - if (rtp->rtcp) { + if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) { dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); } |