res_rtp_asterisk: Add support for DTLS handshake retransmissions
authorMatthew Jordan <mjordan@digium.com>
Fri, 25 Apr 2014 19:26:14 +0000 (19:26 +0000)
committerMatthew Jordan <mjordan@digium.com>
Fri, 25 Apr 2014 19:26:14 +0000 (19:26 +0000)
On congested networks, it is possible for the DTLS handshake messages to get
lost. This patch adds a timer to res_rtp_asterisk that will periodically
check to see if the handshake has succeeded. If not, it will retransmit the
DTLS handshake.

Review: https://reviewboard.asterisk.org/r/3337

ASTERISK-23649 #close
Reported by: Nitesh Bansal
patches:
  dtls_retransmission.patch uploaded by Nitesh Bansal (License 6418)
........

Merged revisions 413008 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 413009 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@413012 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_rtp_asterisk.c

index 2ad76b7..444b9f7 100644 (file)
@@ -283,6 +283,7 @@ struct ast_rtp {
        SSL *ssl;         /*!< SSL session */
        BIO *read_bio;    /*!< Memory buffer for reading */
        BIO *write_bio;   /*!< Memory buffer for writing */
+       ast_mutex_t dtls_timer_lock;           /*!< Lock for synchronization purposes */
        enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
        enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
        char local_fingerprint[160]; /*!< Fingerprint of our certificate */
@@ -291,6 +292,7 @@ struct ast_rtp {
        unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */
        unsigned int rekey; /*!< Interval at which to renegotiate and rekey */
        int rekeyid; /*!< Scheduled item id for rekeying */
+       int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */
 #endif
 };
 
@@ -402,6 +404,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 
 #ifdef HAVE_OPENSSL_SRTP
 static int ast_rtp_activate(struct ast_rtp_instance *instance);
+static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp);
 #endif
 
 static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
@@ -1320,9 +1323,43 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
 }
 
 #ifdef HAVE_OPENSSL_SRTP
+
+static int dtls_srtp_handle_timeout(const void *data)
+{
+       struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (!rtp)
+       {
+               return 0;
+       }
+
+       ast_mutex_lock(&rtp->dtls_timer_lock);
+       if (rtp->dtlstimerid == -1)
+       {
+               ast_mutex_unlock(&rtp->dtls_timer_lock);
+               ao2_ref(instance, -1);
+               return 0;
+       }
+
+       rtp->dtlstimerid = -1;
+       ast_mutex_unlock(&rtp->dtls_timer_lock);
+
+       if (rtp->ssl) {
+               DTLSv1_handle_timeout(rtp->ssl);
+       }
+
+       dtls_srtp_check_pending(instance, rtp);
+
+       ao2_ref(instance, -1);
+
+       return 0;
+}
+
 static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
 {
        size_t pending = BIO_ctrl_pending(rtp->write_bio);
+       struct timeval dtls_timeout; /* timeout on DTLS  */
 
        if (pending > 0) {
                char outgoing[pending];
@@ -1339,6 +1376,23 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as
 
                out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing));
 
+               /* Stop existing DTLS timer if running */
+               ast_mutex_lock(&rtp->dtls_timer_lock);
+               if (rtp->dtlstimerid > -1) {
+                       AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
+                       rtp->dtlstimerid = -1;
+               }
+
+               if (DTLSv1_get_timeout(rtp->ssl, &dtls_timeout)) {
+                       int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
+                       ao2_ref(instance, +1);
+                       if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) {
+                               ao2_ref(instance, -1);
+                               ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance);
+                       }
+               }
+               ast_mutex_unlock(&rtp->dtls_timer_lock);
+
                __rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0);
        }
 }
@@ -1972,6 +2026,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 
 #ifdef HAVE_OPENSSL_SRTP
        rtp->rekeyid = -1;
+       rtp->dtlstimerid = -1;
 #endif
 
        return 0;
@@ -4319,6 +4374,9 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 
 #ifdef HAVE_OPENSSL_SRTP
        AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
+       ast_mutex_lock(&rtp->dtls_timer_lock);
+       AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
+       ast_mutex_unlock(&rtp->dtls_timer_lock);
 #endif
 
        if (rtp->rtcp && rtp->rtcp->schedid > 0) {