From 98f7e9251f5dc9a7cf2eb82c77ec2796f00bce2f Mon Sep 17 00:00:00 2001
From: Richard Mudgett <rmudgett@digium.com>
Date: Mon, 11 Dec 2017 18:20:06 -0600
Subject: [PATCH] res_rtp_asterisk.c: Disable packet flood detection for video
 streams.

We should not do flood detection on video RTP streams.  Video RTP streams
are very bursty by nature.  They send out a burst of packets to update the
video frame then wait for the next video frame update.  Really only audio
streams can be checked for flooding.  The others are either bursty or
don't have a set rate.

* Added code to selectively disable packet flood detection for video RTP
streams.

ASTERISK-27440

Change-Id: I78031491a6e75c2d4b1e9c2462dc498fe9880a70
---
 configs/samples/rtp.conf.sample | 14 +++++++---
 include/asterisk/rtp_engine.h   | 10 ++++++++
 main/rtp_engine.c               | 19 ++++++++++++++
 res/res_rtp_asterisk.c          | 45 +++++++++++++++++++++++++--------
 4 files changed, 74 insertions(+), 14 deletions(-)

diff --git a/configs/samples/rtp.conf.sample b/configs/samples/rtp.conf.sample
index 9bc3de3cf8..de9d59007c 100644
--- a/configs/samples/rtp.conf.sample
+++ b/configs/samples/rtp.conf.sample
@@ -21,9 +21,17 @@ rtpend=20000
 ; rtcpinterval = 5000 	; Milliseconds between rtcp reports
 			;(min 500, max 60000, default 5000)
 ;
-; Enable strict RTP protection. This will drop RTP packets that
-; do not come from the source of the RTP stream. This option is
-; enabled by default.
+; Enable strict RTP protection.  This will drop RTP packets that do not come
+; from the recoginized source of the RTP stream.  Strict RTP qualifies RTP
+; packet stream sources before accepting them upon initial connection and
+; when the connection is renegotiated (e.g., transfers and direct media).
+; Initial connection and renegotiation starts a learning mode to qualify
+; stream source addresses.  Once Asterisk has recognized a stream it will
+; allow other streams to qualify and replace the current stream for 5
+; seconds after starting learning mode.  Once learning mode completes the
+; current stream is locked in and cannot change until the next
+; renegotiation.
+; This option is enabled by default.
 ; strictrtp=yes
 ;
 ; Number of packets containing consecutive sequence values needed
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index f9d686aca8..c77be4584b 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -1382,6 +1382,16 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
  */
 void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload);
 
+/*!
+ * \brief Determine the type of RTP stream media from the codecs mapped.
+ * \since 13.19.0
+ *
+ * \param codecs Codecs structure to look in
+ *
+ * \return Media type or AST_MEDIA_TYPE_UNKNOWN if no codecs mapped.
+ */
+enum ast_media_type ast_rtp_codecs_get_stream_type(struct ast_rtp_codecs *codecs);
+
 /*!
  * \brief Retrieve rx payload mapped information by payload type
  *
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 2431ffc0cd..68c53e7ffe 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -1176,6 +1176,25 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp
 	ast_rwlock_unlock(&codecs->codecs_lock);
 }
 
+enum ast_media_type ast_rtp_codecs_get_stream_type(struct ast_rtp_codecs *codecs)
+{
+	enum ast_media_type stream_type = AST_MEDIA_TYPE_UNKNOWN;
+	int payload;
+	struct ast_rtp_payload_type *type;
+
+	ast_rwlock_rdlock(&codecs->codecs_lock);
+	for (payload = 0; payload < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); ++payload) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_rx, payload);
+		if (type && type->asterisk_format) {
+			stream_type = ast_format_get_type(type->format);
+			break;
+		}
+	}
+	ast_rwlock_unlock(&codecs->codecs_lock);
+
+	return stream_type;
+}
+
 struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *codecs, int payload)
 {
 	struct ast_rtp_payload_type *type = NULL;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index bdc83301eb..51e509c777 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -256,6 +256,8 @@ struct rtp_learning_info {
 	struct timeval received; /*!< The time of the first received packet */
 	int max_seq;	/*!< The highest sequence number received */
 	int packets;	/*!< The number of remaining packets before the source is accepted */
+	/*! Type of media stream carried by the RTP instance */
+	enum ast_media_type stream_type;
 };
 
 #ifdef HAVE_OPENSSL_SRTP
@@ -3095,18 +3097,30 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
 		info->received = ast_tvnow();
 	}
 
-	/*
-	 * Protect against packet floods by checking that we
-	 * received the packet sequence in at least the minimum
-	 * allowed time.
-	 */
-	if (ast_tvzero(info->received)) {
-		info->received = ast_tvnow();
-	} else if (!info->packets && (ast_tvdiff_ms(ast_tvnow(), info->received) < learning_min_duration )) {
-		/* Packet flood; reset */
-		info->packets = learning_min_sequential - 1;
-		info->received = ast_tvnow();
+	switch (info->stream_type) {
+	case AST_MEDIA_TYPE_UNKNOWN:
+	case AST_MEDIA_TYPE_AUDIO:
+		/*
+		 * Protect against packet floods by checking that we
+		 * received the packet sequence in at least the minimum
+		 * allowed time.
+		 */
+		if (ast_tvzero(info->received)) {
+			info->received = ast_tvnow();
+		} else if (!info->packets
+			&& ast_tvdiff_ms(ast_tvnow(), info->received) < learning_min_duration) {
+			/* Packet flood; reset */
+			info->packets = learning_min_sequential - 1;
+			info->received = ast_tvnow();
+		}
+		break;
+	case AST_MEDIA_TYPE_VIDEO:
+	case AST_MEDIA_TYPE_IMAGE:
+	case AST_MEDIA_TYPE_TEXT:
+	case AST_MEDIA_TYPE_END:
+		break;
 	}
+
 	info->max_seq = seq;
 
 	return info->packets;
@@ -5951,6 +5965,15 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 			 * source and we should switch to it.
 			 */
 			if (!ast_sockaddr_cmp(&rtp->rtp_source_learn.proposed_address, &addr)) {
+				if (rtp->rtp_source_learn.stream_type == AST_MEDIA_TYPE_UNKNOWN) {
+					struct ast_rtp_codecs *codecs;
+
+					codecs = ast_rtp_instance_get_codecs(instance);
+					rtp->rtp_source_learn.stream_type =
+						ast_rtp_codecs_get_stream_type(codecs);
+					ast_verb(4, "%p -- Strict RTP qualifying stream type: %s\n",
+						rtp, ast_codec_media_type2str(rtp->rtp_source_learn.stream_type));
+				}
 				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",
-- 
GitLab