mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-05 12:42:49 +00:00
Merge "AST-2017-008: Improve RTP and RTCP packet processing." into 13
This commit is contained in:
@@ -124,7 +124,9 @@ enum strict_rtp_state {
|
||||
STRICT_RTP_CLOSED, /*! Drop all RTP packets not coming from source that was learned */
|
||||
};
|
||||
|
||||
#define DEFAULT_STRICT_RTP STRICT_RTP_CLOSED
|
||||
#define STRICT_RTP_LEARN_TIMEOUT 1500 /*!< milliseconds */
|
||||
|
||||
#define DEFAULT_STRICT_RTP -1 /*!< Enabled */
|
||||
#define DEFAULT_ICESUPPORT 1
|
||||
|
||||
extern struct ast_srtp_res *res_srtp;
|
||||
@@ -225,9 +227,11 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
|
||||
|
||||
/*! \brief RTP learning mode tracking information */
|
||||
struct rtp_learning_info {
|
||||
int max_seq; /*!< The highest sequence number received */
|
||||
int packets; /*!< The number of remaining packets before the source is accepted */
|
||||
struct ast_sockaddr proposed_address; /*!< Proposed remote address for strict RTP */
|
||||
struct timeval start; /*!< The time learning mode was started */
|
||||
struct timeval received; /*!< The time of the last received packet */
|
||||
int max_seq; /*!< The highest sequence number received */
|
||||
int packets; /*!< The number of remaining packets before the source is accepted */
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENSSL_SRTP
|
||||
@@ -256,7 +260,7 @@ struct ast_rtp {
|
||||
unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
|
||||
unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */
|
||||
unsigned int themssrc; /*!< Their SSRC */
|
||||
unsigned int rxssrc;
|
||||
unsigned int themssrc_valid; /*!< True if their SSRC is available. */
|
||||
unsigned int lastts;
|
||||
unsigned int lastrxts;
|
||||
unsigned int lastividtimestamp;
|
||||
@@ -1991,7 +1995,7 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PJPROJECT
|
||||
static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
|
||||
static void rtp_learning_start(struct ast_rtp *rtp);
|
||||
|
||||
/* PJPROJECT ICE callback */
|
||||
static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
||||
@@ -2030,8 +2034,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
||||
return;
|
||||
}
|
||||
|
||||
rtp->strict_rtp_state = STRICT_RTP_LEARN;
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
|
||||
ast_verb(4, "%p -- Strict RTP learning after ICE completion\n", rtp);
|
||||
rtp_learning_start(rtp);
|
||||
ao2_unlock(instance);
|
||||
}
|
||||
|
||||
@@ -2760,7 +2764,7 @@ static int create_new_socket(const char *type, int af)
|
||||
*/
|
||||
static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
|
||||
{
|
||||
info->max_seq = seq - 1;
|
||||
info->max_seq = seq;
|
||||
info->packets = learning_min_sequential;
|
||||
memset(&info->received, 0, sizeof(info->received));
|
||||
}
|
||||
@@ -2777,14 +2781,17 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
|
||||
*/
|
||||
static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq)
|
||||
{
|
||||
/*
|
||||
* During the learning mode the minimum amount of media we'll accept is
|
||||
* 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
|
||||
*/
|
||||
if (!ast_tvzero(info->received) && ast_tvdiff_ms(ast_tvnow(), info->received) < 5) {
|
||||
/* During the probation period the minimum amount of media we'll accept is
|
||||
* 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
|
||||
/*
|
||||
* Reject a flood of packets as acceptable for learning.
|
||||
* Reset the needed packets.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (seq == info->max_seq + 1) {
|
||||
info->packets = learning_min_sequential - 1;
|
||||
} else if (seq == (uint16_t) (info->max_seq + 1)) {
|
||||
/* packet is in sequence */
|
||||
info->packets--;
|
||||
} else {
|
||||
@@ -2794,7 +2801,23 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
|
||||
info->max_seq = seq;
|
||||
info->received = ast_tvnow();
|
||||
|
||||
return (info->packets == 0);
|
||||
return info->packets;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Start the strictrtp learning mode.
|
||||
*
|
||||
* \param rtp RTP session description
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void rtp_learning_start(struct ast_rtp *rtp)
|
||||
{
|
||||
rtp->strict_rtp_state = STRICT_RTP_LEARN;
|
||||
memset(&rtp->rtp_source_learn.proposed_address, 0,
|
||||
sizeof(rtp->rtp_source_learn.proposed_address));
|
||||
rtp->rtp_source_learn.start = ast_tvnow();
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t) rtp->lastrxseqno);
|
||||
}
|
||||
|
||||
#ifdef HAVE_PJPROJECT
|
||||
@@ -3068,9 +3091,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
|
||||
rtp->ssrc = ast_random();
|
||||
rtp->seqno = ast_random() & 0x7fff;
|
||||
rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
|
||||
if (strictrtp) {
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
|
||||
}
|
||||
|
||||
/* Create a new socket for us to listen on and use */
|
||||
if ((rtp->s =
|
||||
@@ -3644,7 +3664,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
|
||||
struct ast_sockaddr remote_address = { { 0, } };
|
||||
struct ast_rtp_rtcp_report_block *report_block = NULL;
|
||||
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
|
||||
ast_rtp_rtcp_report_alloc(rtp->themssrc ? 1 : 0),
|
||||
ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),
|
||||
ao2_cleanup);
|
||||
|
||||
if (!rtp || !rtp->rtcp) {
|
||||
@@ -3664,7 +3684,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
|
||||
calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
rtcp_report->reception_report_count = rtp->themssrc ? 1 : 0;
|
||||
rtcp_report->reception_report_count = rtp->themssrc_valid ? 1 : 0;
|
||||
rtcp_report->ssrc = rtp->ssrc;
|
||||
rtcp_report->type = sr ? RTCP_PT_SR : RTCP_PT_RR;
|
||||
if (sr) {
|
||||
@@ -3674,7 +3694,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
|
||||
rtcp_report->sender_information.octet_count = rtp->txoctetcount;
|
||||
}
|
||||
|
||||
if (rtp->themssrc) {
|
||||
if (rtp->themssrc_valid) {
|
||||
report_block = ast_calloc(1, sizeof(*report_block));
|
||||
if (!report_block) {
|
||||
return 1;
|
||||
@@ -4019,6 +4039,10 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
if (!rtp->themssrc_valid) {
|
||||
/* We don't know their SSRC value so we don't know who to update. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare RTCP FIR (PT=206, FMT=4) */
|
||||
rtp->rtcp->firseq++;
|
||||
@@ -4599,75 +4623,265 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
|
||||
rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
|
||||
}
|
||||
|
||||
static const char *rtcp_payload_type2str(unsigned int pt)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
switch (pt) {
|
||||
case RTCP_PT_SR:
|
||||
str = "Sender Report";
|
||||
break;
|
||||
case RTCP_PT_RR:
|
||||
str = "Receiver Report";
|
||||
break;
|
||||
case RTCP_PT_FUR:
|
||||
/* Full INTRA-frame Request / Fast Update Request */
|
||||
str = "H.261 FUR";
|
||||
break;
|
||||
case RTCP_PT_PSFB:
|
||||
/* Payload Specific Feed Back */
|
||||
str = "PSFB";
|
||||
break;
|
||||
case RTCP_PT_SDES:
|
||||
str = "Source Description";
|
||||
break;
|
||||
case RTCP_PT_BYE:
|
||||
str = "BYE";
|
||||
break;
|
||||
default:
|
||||
str = "Unknown";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshifted RTCP header bit field masks
|
||||
*/
|
||||
#define RTCP_LENGTH_MASK 0xFFFF
|
||||
#define RTCP_PAYLOAD_TYPE_MASK 0xFF
|
||||
#define RTCP_REPORT_COUNT_MASK 0x1F
|
||||
#define RTCP_PADDING_MASK 0x01
|
||||
#define RTCP_VERSION_MASK 0x03
|
||||
|
||||
/*
|
||||
* RTCP header bit field shift offsets
|
||||
*/
|
||||
#define RTCP_LENGTH_SHIFT 0
|
||||
#define RTCP_PAYLOAD_TYPE_SHIFT 16
|
||||
#define RTCP_REPORT_COUNT_SHIFT 24
|
||||
#define RTCP_PADDING_SHIFT 29
|
||||
#define RTCP_VERSION_SHIFT 30
|
||||
|
||||
#define RTCP_VERSION 2U
|
||||
#define RTCP_VERSION_SHIFTED (RTCP_VERSION << RTCP_VERSION_SHIFT)
|
||||
#define RTCP_VERSION_MASK_SHIFTED (RTCP_VERSION_MASK << RTCP_VERSION_SHIFT)
|
||||
|
||||
/*
|
||||
* RTCP first packet record validity header mask and value.
|
||||
*
|
||||
* RFC3550 intentionally defines the encoding of RTCP_PT_SR and RTCP_PT_RR
|
||||
* such that they differ in the least significant bit. Either of these two
|
||||
* payload types MUST be the first RTCP packet record in a compound packet.
|
||||
*
|
||||
* RFC3550 checks the padding bit in the algorithm they use to check the
|
||||
* RTCP packet for validity. However, we aren't masking the padding bit
|
||||
* to check since we don't know if it is a compound RTCP packet or not.
|
||||
*/
|
||||
#define RTCP_VALID_MASK (RTCP_VERSION_MASK_SHIFTED | (((RTCP_PAYLOAD_TYPE_MASK & ~0x1)) << RTCP_PAYLOAD_TYPE_SHIFT))
|
||||
#define RTCP_VALID_VALUE (RTCP_VERSION_SHIFTED | (RTCP_PT_SR << RTCP_PAYLOAD_TYPE_SHIFT))
|
||||
|
||||
#define RTCP_SR_BLOCK_WORD_LENGTH 5
|
||||
#define RTCP_RR_BLOCK_WORD_LENGTH 6
|
||||
#define RTCP_HEADER_SSRC_LENGTH 2
|
||||
|
||||
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);
|
||||
unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
|
||||
int packetwords, position = 0;
|
||||
unsigned int packetwords;
|
||||
unsigned int position;
|
||||
unsigned int first_word;
|
||||
/*! True if we have seen an acceptable SSRC to learn the remote RTCP address */
|
||||
unsigned int ssrc_seen;
|
||||
int report_counter = 0;
|
||||
struct ast_rtp_rtcp_report_block *report_block;
|
||||
struct ast_frame *f = &ast_null_frame;
|
||||
|
||||
packetwords = size / 4;
|
||||
|
||||
ast_debug(1, "Got RTCP report of %zu bytes\n", size);
|
||||
ast_debug(1, "Got RTCP report of %zu bytes from %s\n",
|
||||
size, ast_sockaddr_stringify(addr));
|
||||
|
||||
/*
|
||||
* Validate the RTCP packet according to an adapted and slightly
|
||||
* modified RFC3550 validation algorithm.
|
||||
*/
|
||||
if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
|
||||
ast_debug(1, "%p -- RTCP from %s: Frame size (%u words) is too short\n",
|
||||
rtp, ast_sockaddr_stringify(addr), packetwords);
|
||||
return &ast_null_frame;
|
||||
}
|
||||
position = 0;
|
||||
first_word = ntohl(rtcpheader[position]);
|
||||
if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
ast_debug(1, "%p -- RTCP from %s: Failed first packet validity check\n",
|
||||
rtp, ast_sockaddr_stringify(addr));
|
||||
return &ast_null_frame;
|
||||
}
|
||||
do {
|
||||
position += ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
|
||||
if (packetwords <= position) {
|
||||
break;
|
||||
}
|
||||
first_word = ntohl(rtcpheader[position]);
|
||||
} while ((first_word & RTCP_VERSION_MASK_SHIFTED) == RTCP_VERSION_SHIFTED);
|
||||
if (position != packetwords) {
|
||||
ast_debug(1, "%p -- RTCP from %s: Failed packet version or length check\n",
|
||||
rtp, ast_sockaddr_stringify(addr));
|
||||
return &ast_null_frame;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: RFC3605 points out that true NAT (vs NAPT) can cause RTCP
|
||||
* to have a different IP address and port than RTP. Otherwise, when
|
||||
* strictrtp is enabled we could reject RTCP packets not coming from
|
||||
* the learned RTP IP address if it is available.
|
||||
*/
|
||||
|
||||
/*
|
||||
* strictrtp safety needs SSRC to match before we use the
|
||||
* sender's address for symmetrical RTP to send our RTCP
|
||||
* reports.
|
||||
*
|
||||
* If strictrtp is not enabled then claim to have already seen
|
||||
* a matching SSRC so we'll accept this packet's address for
|
||||
* symmetrical RTP.
|
||||
*/
|
||||
ssrc_seen = rtp->strict_rtp_state == STRICT_RTP_OPEN;
|
||||
|
||||
position = 0;
|
||||
while (position < packetwords) {
|
||||
int i, pt, rc;
|
||||
unsigned int i;
|
||||
unsigned int pt;
|
||||
unsigned int rc;
|
||||
unsigned int ssrc;
|
||||
/*! True if the ssrc value we have is valid and not garbage because it doesn't exist. */
|
||||
unsigned int ssrc_valid;
|
||||
unsigned int length;
|
||||
unsigned int min_length;
|
||||
|
||||
struct ast_json *message_blob;
|
||||
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
|
||||
|
||||
i = position;
|
||||
length = ntohl(rtcpheader[i]);
|
||||
pt = (length & 0xff0000) >> 16;
|
||||
rc = (length & 0x1f000000) >> 24;
|
||||
length &= 0xffff;
|
||||
first_word = ntohl(rtcpheader[i]);
|
||||
pt = (first_word >> RTCP_PAYLOAD_TYPE_SHIFT) & RTCP_PAYLOAD_TYPE_MASK;
|
||||
rc = (first_word >> RTCP_REPORT_COUNT_SHIFT) & RTCP_REPORT_COUNT_MASK;
|
||||
/* RFC3550 says 'length' is the number of words in the packet - 1 */
|
||||
length = ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
|
||||
|
||||
rtcp_report = ast_rtp_rtcp_report_alloc(rc);
|
||||
if (!rtcp_report) {
|
||||
return &ast_null_frame;
|
||||
}
|
||||
rtcp_report->reception_report_count = rc;
|
||||
rtcp_report->ssrc = ntohl(rtcpheader[i + 1]);
|
||||
|
||||
if ((i + length) > packetwords) {
|
||||
if (rtpdebug) {
|
||||
ast_debug(1, "RTCP Read too short\n");
|
||||
/* Check expected RTCP packet record length */
|
||||
min_length = RTCP_HEADER_SSRC_LENGTH;
|
||||
switch (pt) {
|
||||
case RTCP_PT_SR:
|
||||
min_length += RTCP_SR_BLOCK_WORD_LENGTH;
|
||||
/* fall through */
|
||||
case RTCP_PT_RR:
|
||||
min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
|
||||
break;
|
||||
case RTCP_PT_FUR:
|
||||
case RTCP_PT_PSFB:
|
||||
break;
|
||||
case RTCP_PT_SDES:
|
||||
case RTCP_PT_BYE:
|
||||
/*
|
||||
* There may not be a SSRC/CSRC present. The packet is
|
||||
* useless but still valid if it isn't present.
|
||||
*
|
||||
* We don't know what min_length should be so disable the check
|
||||
*/
|
||||
min_length = length;
|
||||
break;
|
||||
default:
|
||||
ast_debug(1, "%p -- RTCP from %s: %u(%s) skipping record\n",
|
||||
rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
|
||||
if (rtcp_debug_test_addr(addr)) {
|
||||
ast_verbose("\n");
|
||||
ast_verbose("RTCP from %s: %u(%s) skipping record\n",
|
||||
ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
|
||||
}
|
||||
return &ast_null_frame;
|
||||
}
|
||||
|
||||
if ((rtp->strict_rtp_state != STRICT_RTP_OPEN) && (rtcp_report->ssrc != rtp->themssrc)) {
|
||||
/* Skip over this RTCP record as it does not contain the correct SSRC */
|
||||
position += (length + 1);
|
||||
ast_debug(1, "%p -- Received RTCP report from %s, dropping due to strict RTP protection. Received SSRC '%u' but expected '%u'\n",
|
||||
rtp, ast_sockaddr_stringify(addr), rtcp_report->ssrc, rtp->themssrc);
|
||||
position += length;
|
||||
continue;
|
||||
}
|
||||
if (length < min_length) {
|
||||
ast_debug(1, "%p -- RTCP from %s: %u(%s) length field less than expected minimum. Min:%u Got:%u\n",
|
||||
rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt),
|
||||
min_length - 1, length - 1);
|
||||
return &ast_null_frame;
|
||||
}
|
||||
|
||||
if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
|
||||
/* Get the RTCP record SSRC if defined for the record */
|
||||
ssrc_valid = 1;
|
||||
switch (pt) {
|
||||
case RTCP_PT_SR:
|
||||
case RTCP_PT_RR:
|
||||
rtcp_report = ast_rtp_rtcp_report_alloc(rc);
|
||||
if (!rtcp_report) {
|
||||
return &ast_null_frame;
|
||||
}
|
||||
rtcp_report->reception_report_count = rc;
|
||||
|
||||
ssrc = ntohl(rtcpheader[i + 1]);
|
||||
rtcp_report->ssrc = ssrc;
|
||||
break;
|
||||
case RTCP_PT_FUR:
|
||||
case RTCP_PT_PSFB:
|
||||
ssrc = ntohl(rtcpheader[i + 1]);
|
||||
break;
|
||||
case RTCP_PT_SDES:
|
||||
case RTCP_PT_BYE:
|
||||
default:
|
||||
ssrc = 0;
|
||||
ssrc_valid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rtcp_debug_test_addr(addr)) {
|
||||
ast_verbose("\n");
|
||||
ast_verbose("RTCP from %s\n", ast_sockaddr_stringify(addr));
|
||||
ast_verbose("PT: %u(%s)\n", pt, rtcp_payload_type2str(pt));
|
||||
ast_verbose("Reception reports: %u\n", rc);
|
||||
ast_verbose("SSRC of sender: %u\n", ssrc);
|
||||
}
|
||||
|
||||
if (ssrc_valid && rtp->themssrc_valid) {
|
||||
if (ssrc != rtp->themssrc) {
|
||||
/*
|
||||
* Skip over this RTCP record as it does not contain the
|
||||
* correct SSRC. We should not act upon RTCP records
|
||||
* for a different stream.
|
||||
*/
|
||||
position += length;
|
||||
ast_debug(1, "%p -- RTCP from %s: Skipping record, received SSRC '%u' != expected '%u'\n",
|
||||
rtp, ast_sockaddr_stringify(addr), ssrc, rtp->themssrc);
|
||||
continue;
|
||||
}
|
||||
ssrc_seen = 1;
|
||||
}
|
||||
|
||||
if (ssrc_seen && 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 (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(addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rtcp_debug_test_addr(addr)) {
|
||||
ast_verbose("\n\nGot RTCP from %s\n",
|
||||
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");
|
||||
ast_verbose("Reception reports: %d\n", rc);
|
||||
ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc);
|
||||
}
|
||||
|
||||
i += 2; /* Advance past header and ssrc */
|
||||
i += RTCP_HEADER_SSRC_LENGTH; /* Advance past header and ssrc */
|
||||
switch (pt) {
|
||||
case RTCP_PT_SR:
|
||||
gettimeofday(&rtp->rtcp->rxlsr, NULL);
|
||||
@@ -4691,7 +4905,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
|
||||
rtcp_report->sender_information.packet_count,
|
||||
rtcp_report->sender_information.octet_count);
|
||||
}
|
||||
i += 5;
|
||||
i += RTCP_SR_BLOCK_WORD_LENGTH;
|
||||
/* Intentional fall through */
|
||||
case RTCP_PT_RR:
|
||||
if (rtcp_report->type != RTCP_PT_SR) {
|
||||
@@ -4748,9 +4962,9 @@ 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,
|
||||
"rtt", rtp->rtcp->rtt);
|
||||
"from", ast_sockaddr_stringify(addr),
|
||||
"to", rtp->rtcp->local_addr_str,
|
||||
"rtt", rtp->rtcp->rtt);
|
||||
ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
|
||||
rtcp_report,
|
||||
message_blob);
|
||||
@@ -4773,26 +4987,23 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
|
||||
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(addr));
|
||||
}
|
||||
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(addr));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n",
|
||||
pt, ast_sockaddr_stringify(&rtp->rtcp->them));
|
||||
break;
|
||||
}
|
||||
position += (length + 1);
|
||||
position += length;
|
||||
}
|
||||
rtp->rtcp->rtcp_info = 1;
|
||||
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
/*! \pre instance is locked */
|
||||
@@ -5083,31 +5294,133 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
|
||||
}
|
||||
|
||||
/* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
|
||||
if (rtp->strict_rtp_state == STRICT_RTP_LEARN) {
|
||||
if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
|
||||
/* We are learning a new address but have received traffic from the existing address,
|
||||
* accept it but reset the current learning for the new source so it only takes over
|
||||
* once sufficient traffic has been received. */
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
|
||||
} else {
|
||||
/* Start trying to learn from the new address. If we pass a probationary period with
|
||||
* it, that means we've stopped getting RTP from the original source and we should
|
||||
* switch to it.
|
||||
*/
|
||||
if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
|
||||
ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets\n",
|
||||
rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
|
||||
return &ast_null_frame;
|
||||
}
|
||||
ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
|
||||
|
||||
ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
|
||||
switch (rtp->strict_rtp_state) {
|
||||
case STRICT_RTP_LEARN:
|
||||
/*
|
||||
* Scenario setup:
|
||||
* PartyA -- Ast1 -- Ast2 -- PartyB
|
||||
*
|
||||
* The learning timeout is necessary for Ast1 to handle the above
|
||||
* setup where PartyA calls PartyB and Ast2 initiates direct media
|
||||
* between Ast1 and PartyB. Ast1 may lock onto the Ast2 stream and
|
||||
* never learn the PartyB stream when it starts. The timeout makes
|
||||
* Ast1 stay in the learning state long enough to see and learn the
|
||||
* RTP stream from PartyB.
|
||||
*
|
||||
* To mitigate against attack, the learning state cannot switch
|
||||
* streams while there are competing streams. The competing streams
|
||||
* interfere with each other's qualification. Once we accept a
|
||||
* stream and reach the timeout, an attacker cannot interfere
|
||||
* anymore.
|
||||
*
|
||||
* Here are a few scenarios and each one assumes that the streams
|
||||
* are continuous:
|
||||
*
|
||||
* 1) We already have a known stream source address and the known
|
||||
* stream wants to change to a new source address. An attacking
|
||||
* stream will block learning the new stream source. After the
|
||||
* timeout we re-lock onto the original stream source address which
|
||||
* likely went away. The result is one way audio.
|
||||
*
|
||||
* 2) We already have a known stream source address and the known
|
||||
* stream doesn't want to change source addresses. An attacking
|
||||
* stream will not be able to replace the known stream. After the
|
||||
* timeout we re-lock onto the known stream. The call is not
|
||||
* affected.
|
||||
*
|
||||
* 3) We don't have a known stream source address. This presumably
|
||||
* is the start of a call. Competing streams will result in staying
|
||||
* in learning mode until a stream becomes the victor and we reach
|
||||
* the timeout. We cannot exit learning if we have no known stream
|
||||
* to lock onto. The result is one way audio until there is a victor.
|
||||
*
|
||||
* If we learn a stream source address before the timeout we will be
|
||||
* in scenario 1) or 2) when a competing stream starts.
|
||||
*/
|
||||
if (!ast_sockaddr_isnull(&rtp->strict_rtp_address)
|
||||
&& STRICT_RTP_LEARN_TIMEOUT < ast_tvdiff_ms(ast_tvnow(), rtp->rtp_source_learn.start)) {
|
||||
ast_verb(4, "%p -- Strict RTP learning complete - Locking on source address %s\n",
|
||||
rtp, ast_sockaddr_stringify(&rtp->strict_rtp_address));
|
||||
rtp->strict_rtp_state = STRICT_RTP_CLOSED;
|
||||
} else {
|
||||
struct ast_sockaddr target_address;
|
||||
|
||||
if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
|
||||
/*
|
||||
* We are open to learning a new address but have received
|
||||
* traffic from the current address, accept it and reset
|
||||
* the learning counts for a new source. When no more
|
||||
* current source packets arrive a new source can take over
|
||||
* once sufficient traffic is received.
|
||||
*/
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We give preferential treatment to the requested target address
|
||||
* (negotiated SDP address) where we are to send our RTP. However,
|
||||
* the other end has no obligation to send from that address even
|
||||
* though it is practically a requirement when NAT is involved.
|
||||
*/
|
||||
ast_rtp_instance_get_requested_target_address(instance, &target_address);
|
||||
if (!ast_sockaddr_cmp(&target_address, &addr)) {
|
||||
/* Accept the negotiated target RTP stream as the source */
|
||||
ast_verb(4, "%p -- Strict RTP switching to RTP target address %s as source\n",
|
||||
rtp, ast_sockaddr_stringify(&addr));
|
||||
ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trying to learn a new address. If we pass a probationary period
|
||||
* with it, that means we've stopped getting RTP from the original
|
||||
* source and we should switch to it.
|
||||
*/
|
||||
if (!ast_sockaddr_cmp(&rtp->rtp_source_learn.proposed_address, &addr)) {
|
||||
if (!rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
|
||||
/* Accept the new RTP stream */
|
||||
ast_verb(4, "%p -- Strict RTP switching source address to %s\n",
|
||||
rtp, ast_sockaddr_stringify(&addr));
|
||||
ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
|
||||
break;
|
||||
}
|
||||
/* Not ready to accept the RTP stream candidate */
|
||||
ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets.\n",
|
||||
rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
|
||||
} else {
|
||||
/*
|
||||
* This is either an attacking stream or
|
||||
* the start of the expected new stream.
|
||||
*/
|
||||
ast_sockaddr_copy(&rtp->rtp_source_learn.proposed_address, &addr);
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
|
||||
ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Qualifying new stream.\n",
|
||||
rtp, ast_sockaddr_stringify(&addr));
|
||||
}
|
||||
return &ast_null_frame;
|
||||
}
|
||||
/* Fall through */
|
||||
case STRICT_RTP_CLOSED:
|
||||
/*
|
||||
* We should not allow a stream address change if the SSRC matches
|
||||
* once strictrtp learning is closed. Any kind of address change
|
||||
* like this should have happened while we were in the learning
|
||||
* state. We do not want to allow the possibility of an attacker
|
||||
* interfering with the RTP stream after the learning period.
|
||||
* An attacker could manage to get an RTCP packet redirected to
|
||||
* them which can contain the SSRC value.
|
||||
*/
|
||||
if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
|
||||
break;
|
||||
}
|
||||
} else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED && ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
|
||||
ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
|
||||
rtp, ast_sockaddr_stringify(&addr));
|
||||
return &ast_null_frame;
|
||||
case STRICT_RTP_OPEN:
|
||||
break;
|
||||
}
|
||||
|
||||
/* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
|
||||
@@ -5140,7 +5453,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
|
||||
|
||||
AST_LIST_HEAD_INIT_NOLOCK(&frames);
|
||||
/* Force a marker bit and change SSRC if the SSRC changes */
|
||||
if (rtp->rxssrc && rtp->rxssrc != ssrc) {
|
||||
if (rtp->themssrc_valid && rtp->themssrc != ssrc) {
|
||||
struct ast_frame *f, srcupdate = {
|
||||
AST_FRAME_CONTROL,
|
||||
.subclass.integer = AST_CONTROL_SRCCHANGE,
|
||||
@@ -5168,8 +5481,8 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
|
||||
rtp->rtcp->received_prior = 0;
|
||||
}
|
||||
}
|
||||
|
||||
rtp->rxssrc = ssrc;
|
||||
rtp->themssrc = ssrc; /* Record their SSRC to put in future RR */
|
||||
rtp->themssrc_valid = 1;
|
||||
|
||||
/* Remove any padding bytes that may be present */
|
||||
if (padding) {
|
||||
@@ -5223,10 +5536,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
|
||||
prev_seqno = rtp->lastrxseqno;
|
||||
rtp->lastrxseqno = seqno;
|
||||
|
||||
if (!rtp->themssrc) {
|
||||
rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
|
||||
}
|
||||
|
||||
|
||||
/* If we are directly bridged to another instance send the audio directly out,
|
||||
* but only after updating core information about the received traffic so that
|
||||
@@ -5625,13 +5934,14 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
|
||||
|
||||
rtp->rxseqno = 0;
|
||||
|
||||
if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN && !ast_sockaddr_isnull(addr) &&
|
||||
ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
|
||||
if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN
|
||||
&& !ast_sockaddr_isnull(addr) && ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
|
||||
/* We only need to learn a new strict source address if we've been told the source is
|
||||
* changing to something different.
|
||||
*/
|
||||
rtp->strict_rtp_state = STRICT_RTP_LEARN;
|
||||
rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
|
||||
ast_verb(4, "%p -- Strict RTP learning after remote address set to: %s\n",
|
||||
rtp, ast_sockaddr_stringify(addr));
|
||||
rtp_learning_start(rtp);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user