Merge "res_rtp_asterisk.c: Fix rtp source address learning for broken clients"
[asterisk/asterisk.git] / res / res_rtp_asterisk.c
index 839753e..2730611 100644 (file)
 #define ZFONE_PROFILE_ID 0x505a
 
 #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4
+/*!
+ * \brief Calculate the min learning duration in ms.
+ *
+ * \details
+ * The min supported packet size represents 10 ms and we need to account
+ * for some jitter and fast clocks while learning.  Some messed up devices
+ * have very bad jitter for a small packet sample size.  Jitter can also
+ * be introduced by the network itself.
+ *
+ * So we'll allow packets to come in every 9ms on average for fast clocking
+ * with the last one coming in 5ms early for jitter.
+ */
+#define CALC_LEARNING_MIN_DURATION(count) (((count) - 1) * 9 - 5)
+#define DEFAULT_LEARNING_MIN_DURATION CALC_LEARNING_MIN_DURATION(DEFAULT_LEARNING_MIN_SEQUENTIAL)
 
 #define SRTP_MASTER_KEY_LEN 16
 #define SRTP_MASTER_SALT_LEN 14
@@ -151,6 +165,7 @@ static int nochecksums;
 #endif
 static int strictrtp = DEFAULT_STRICT_RTP; /*!< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */
 static int learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; /*!< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */
+static int learning_min_duration = DEFAULT_LEARNING_MIN_DURATION; /*!< Lowest acceptable timeout between the first and the last sequential RTP frame. */
 #ifdef HAVE_PJPROJECT
 static int icesupport = DEFAULT_ICESUPPORT;
 static struct sockaddr_in stunaddr;
@@ -231,7 +246,7 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
 struct rtp_learning_info {
        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 */
+       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 */
 };
@@ -3065,25 +3080,28 @@ 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) {
-               /*
-                * Reject a flood of packets as acceptable for learning.
-                * Reset the needed packets.
-                */
-               info->packets = learning_min_sequential - 1;
-       } else if (seq == (uint16_t) (info->max_seq + 1)) {
+       if (seq == (uint16_t) (info->max_seq + 1)) {
                /* packet is in sequence */
                info->packets--;
        } else {
                /* Sequence discontinuity; reset */
                info->packets = learning_min_sequential - 1;
+               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();
        }
        info->max_seq = seq;
-       info->received = ast_tvnow();
 
        return info->packets;
 }
@@ -7153,6 +7171,7 @@ static int rtp_reload(int reload)
        dtmftimeout = DEFAULT_DTMF_TIMEOUT;
        strictrtp = DEFAULT_STRICT_RTP;
        learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL;
+       learning_min_duration = DEFAULT_LEARNING_MIN_DURATION;
 
        /** This resource is not "reloaded" so much as unloaded and loaded again.
         * In the case of the TURN related variables, the memory referenced by a
@@ -7217,10 +7236,12 @@ static int rtp_reload(int reload)
                strictrtp = ast_true(s);
        }
        if ((s = ast_variable_retrieve(cfg, "general", "probation"))) {
-               if ((sscanf(s, "%d", &learning_min_sequential) <= 0) || learning_min_sequential <= 0) {
+               if ((sscanf(s, "%d", &learning_min_sequential) != 1) || learning_min_sequential <= 1) {
                        ast_log(LOG_WARNING, "Value for 'probation' could not be read, using default of '%d' instead\n",
                                DEFAULT_LEARNING_MIN_SEQUENTIAL);
+                       learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL;
                }
+               learning_min_duration = CALC_LEARNING_MIN_DURATION(learning_min_sequential);
        }
 #ifdef HAVE_PJPROJECT
        if ((s = ast_variable_retrieve(cfg, "general", "icesupport"))) {