Merge "res_rtp_asterisk.c: Fix rtp source address learning for broken clients"
authorJenkins2 <jenkins2@gerrit.asterisk.org>
Mon, 27 Nov 2017 22:33:38 +0000 (16:33 -0600)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Mon, 27 Nov 2017 22:33:38 +0000 (16:33 -0600)
1  2 
res/res_rtp_asterisk.c

diff --combined res/res_rtp_asterisk.c
  #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 +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 +246,7 @@@ static AST_RWLIST_HEAD_STATIC(host_cand
  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 +3080,28 @@@ static void rtp_learning_seq_init(struc
   */
  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;
  }
@@@ -3193,8 -3211,8 +3211,8 @@@ static void rtp_add_candidates_to_ice(s
        }
  
        /* If configured to use a STUN server to get our external mapped address do so */
 -      if (stunaddr.sin_addr.s_addr && count && ast_sockaddr_is_ipv4(addr)
 -              && !stun_address_is_blacklisted(addr)) {
 +      if (count && stunaddr.sin_addr.s_addr && !stun_address_is_blacklisted(addr) &&
 +              (ast_sockaddr_is_ipv4(addr) || ast_sockaddr_is_any(addr))) {
                struct sockaddr_in answer;
                int rsp;
  
                ao2_lock(instance);
                if (!rsp) {
                        pj_sockaddr base;
 -                      pj_sockaddr ext;
 -                      pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
 -                      int srflx = 1;
  
 -                      /* Use the first local host candidate as the base */
 -                      pj_sockaddr_cp(&base, &address[basepos]);
 -
 -                      pj_sockaddr_init(pj_AF_INET(), &ext, &mapped, ntohs(answer.sin_port));
 -
 -                      /* If the returned address is the same as one of our host candidates, don't send the srflx */
 -                      for (pos = 0; pos < count; pos++) {
 -                              if ((pj_sockaddr_cmp(&address[pos], &ext) == 0) && !rtp_address_is_ice_blacklisted(&address[pos])) {
 -                                      srflx = 0;
 +                      /* Use the first local IPv4 host candidate as the base */
 +                      for (pos = basepos; pos < count; pos++) {
 +                              if (address[pos].addr.sa_family == PJ_AF_INET &&
 +                                      !rtp_address_is_ice_blacklisted(&address[pos])) {
 +                                      pj_sockaddr_cp(&base, &address[pos]);
                                        break;
                                }
                        }
  
 -                      if (srflx) {
 -                              ast_rtp_ice_add_cand(instance, rtp, component, transport,
 -                                      PJ_ICE_CAND_TYPE_SRFLX, 65535, &ext, &base, &base,
 -                                      pj_sockaddr_get_len(&ext));
 +                      if (pos < count) {
 +                              pj_sockaddr ext;
 +                              pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
 +                              int srflx = 1;
 +
 +                              pj_sockaddr_init(pj_AF_INET(), &ext, &mapped, ntohs(answer.sin_port));
 +
 +                              /*
 +                               * If the returned address is the same as one of our host
 +                               * candidates, don't send the srflx
 +                               */
 +                              for (pos = 0; pos < count; pos++) {
 +                                      if (pj_sockaddr_cmp(&address[pos], &ext) == 0 &&
 +                                              !rtp_address_is_ice_blacklisted(&address[pos])) {
 +                                              srflx = 0;
 +                                              break;
 +                                      }
 +                              }
 +
 +                              if (srflx) {
 +                                      ast_rtp_ice_add_cand(instance, rtp, component, transport,
 +                                              PJ_ICE_CAND_TYPE_SRFLX, 65535, &ext, &base, &base,
 +                                              pj_sockaddr_get_len(&ext));
 +                              }
                        }
                }
        }
@@@ -7153,6 -7158,7 +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
                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"))) {