From b11a6643cf9eaf62c26fb6c30eaf75e023515e48 Mon Sep 17 00:00:00 2001
From: Ben Ford <bford@digium.com>
Date: Mon, 10 Sep 2018 11:28:09 -0500
Subject: [PATCH] res_rtp_asterisk.c: Add "seqno" strictrtp option

When networks experience disruptions, there can be large gaps of time
between receiving packets. When strictrtp is enabled, this created
issues where a flood of packets could come in and be seen as an attack.
Another option - seqno - has been added to the strictrtp option that
ignores the time interval and goes strictly by sequence number for
validity.

Change-Id: I8a42b8d193673899c8fc22fe7f98ea87df89be71
---
 CHANGES                         | 11 +++++
 configs/samples/rtp.conf.sample |  4 ++
 res/res_rtp_asterisk.c          | 73 ++++++++++++++++++++++-----------
 3 files changed, 65 insertions(+), 23 deletions(-)

diff --git a/CHANGES b/CHANGES
index 26748f776e..fef72124b3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,17 @@
 ===
 ==============================================================================
 
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 16.0.0 to Asterisk 16.1.0 ------------
+------------------------------------------------------------------------------
+
+res_rtp_asterisk
+------------------
+ * The existing strictrtp option in rtp.conf has a new choice availabe, called
+   'seqno', which behaves the same way as setting strictrtp to 'yes', but will
+   ignore the time interval during learning so that bursts of packets can still
+   trigger learning our source.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15 to Asterisk 16 --------------------
 ------------------------------------------------------------------------------
diff --git a/configs/samples/rtp.conf.sample b/configs/samples/rtp.conf.sample
index de9d59007c..26a70d2e16 100644
--- a/configs/samples/rtp.conf.sample
+++ b/configs/samples/rtp.conf.sample
@@ -31,6 +31,10 @@ rtpend=20000
 ; seconds after starting learning mode.  Once learning mode completes the
 ; current stream is locked in and cannot change until the next
 ; renegotiation.
+; Valid options are "no" to disable strictrtp, "yes" to enable strictrtp,
+; and "seqno", which does the same thing as strictrtp=yes, but only checks
+; to make sure the sequence number is correct rather than checking the time
+; interval as well.
 ; This option is enabled by default.
 ; strictrtp=yes
 ;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 192840ca38..5f7cd9f957 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -157,6 +157,12 @@ enum strict_rtp_state {
 	STRICT_RTP_CLOSED,   /*! Drop all RTP packets not coming from source that was learned */
 };
 
+enum strict_rtp_mode {
+	STRICT_RTP_NO = 0,	/*! Don't adhere to any strict RTP rules */
+	STRICT_RTP_YES,		/*! Strict RTP that restricts packets based on time and sequence number */
+	STRICT_RTP_SEQNO,	/*! Strict RTP that restricts packets based on sequence number */
+};
+
 /*!
  * \brief Strict RTP learning timeout time in milliseconds
  *
@@ -166,7 +172,7 @@ enum strict_rtp_state {
  */
 #define STRICT_RTP_LEARN_TIMEOUT	5000
 
-#define DEFAULT_STRICT_RTP -1	/*!< Enabled */
+#define DEFAULT_STRICT_RTP STRICT_RTP_YES	/*!< Enabled by default */
 #define DEFAULT_ICESUPPORT 1
 
 extern struct ast_srtp_res *res_srtp;
@@ -3154,28 +3160,31 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
 		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();
+	/* Only check time if strictrtp is set to yes. Otherwise, we only needed to check seqno */
+	if (strictrtp == STRICT_RTP_YES) {
+		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;
 		}
-		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;
@@ -6736,6 +6745,8 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 			&& 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));
+			ast_test_suite_event_notify("STRICT_RTP_LEARN", "Source: %s",
+				ast_sockaddr_stringify(&rtp->strict_rtp_address));
 			rtp->strict_rtp_state = STRICT_RTP_CLOSED;
 		} else {
 			struct ast_sockaddr target_address;
@@ -6822,6 +6833,16 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		}
 		ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
 			rtp, ast_sockaddr_stringify(&addr));
+#ifdef TEST_FRAMEWORK
+	{
+		static int strict_rtp_test_event = 1;
+		if (strict_rtp_test_event) {
+			ast_test_suite_event_notify("STRICT_RTP_CLOSED", "Source: %s",
+				ast_sockaddr_stringify(&addr));
+			strict_rtp_test_event = 0; /* Only run this event once to prevent possible spam */
+		}
+	}
+#endif
 		return &ast_null_frame;
 	case STRICT_RTP_OPEN:
 		break;
@@ -8110,7 +8131,13 @@ static int rtp_reload(int reload)
 		};
 	}
 	if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) {
-		strictrtp = ast_true(s);
+		if (ast_true(s)) {
+			strictrtp = STRICT_RTP_YES;
+		} else if (!strcasecmp(s, "seqno")) {
+			strictrtp = STRICT_RTP_SEQNO;
+		} else {
+			strictrtp = STRICT_RTP_NO;
+		}
 	}
 	if ((s = ast_variable_retrieve(cfg, "general", "probation"))) {
 		if ((sscanf(s, "%d", &learning_min_sequential) != 1) || learning_min_sequential <= 1) {
-- 
GitLab