aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c48
-rw-r--r--channels/sip/include/sip.h4
2 files changed, 40 insertions, 12 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 750ade278..5d6d2f198 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -3225,11 +3225,20 @@ static int retrans_pkt(const void *data)
struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
int reschedule = DEFAULT_RETRANS;
int xmitres = 0;
+ /* how many ms until retrans timeout is reached */
+ int64_t diff = pkt->retrans_stop_time - ast_tvdiff_ms(ast_tvnow(), pkt->time_sent);
+
+ /* Do not retransmit if time out is reached. This will be negative if the time between
+ * the first transmission and now is larger than our timeout period. This is a fail safe
+ * check in case the scheduler gets behind or the clock is changed. */
+ if ((diff <= 0) || (diff > pkt->retrans_stop_time)) {
+ pkt->retrans_stop = 1;
+ }
/* Lock channel PVT */
sip_pvt_lock(pkt->owner);
- if (pkt->retrans < MAX_RETRANS) {
+ if (!pkt->retrans_stop) {
pkt->retrans++;
if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */
if (sipdebug) {
@@ -3269,19 +3278,35 @@ static int retrans_pkt(const void *data)
append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str);
xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
sip_pvt_unlock(pkt->owner);
- if (xmitres == XMIT_ERROR) {
- ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid);
- } else {
+
+ /* If there was no error during the network transmission, schedule the next retransmission,
+ * but if the next retransmission is going to be beyond our timeout period, mark the packet's
+ * stop_retrans value and set the next retransmit to be the exact time of timeout. This will
+ * allow any responses to the packet to be processed before the packet is destroyed on the next
+ * call to this function by the scheduler. */
+ if (xmitres != XMIT_ERROR) {
+ if (reschedule >= diff) {
+ pkt->retrans_stop = 1;
+ reschedule = diff;
+ }
return reschedule;
}
}
- /* Too many retries */
+ /* At this point, either the packet's retransmission timed out, or there was a
+ * transmission error, either way destroy the scheduler item and this packet. */
+
+ pkt->retransid = -1; /* Kill this scheduler item */
+
if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) {
- if (pkt->is_fatal || sipdebug) {/* Tell us if it's critical or if we're debugging */
- ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s) -- See doc/sip-retransmit.txt.\n",
- pkt->owner->callid, pkt->seqno,
- pkt->is_fatal ? "Critical" : "Non-critical", pkt->is_resp ? "Response" : "Request");
+ if (pkt->is_fatal || sipdebug) { /* Tell us if it's critical or if we're debugging */
+ ast_log(LOG_WARNING, "Retransmission timeout reached on transmission %s for seqno %d (%s %s) -- See doc/sip-retransmit.txt.\n"
+ "Packet timed out after %dms with no response\n",
+ pkt->owner->callid,
+ pkt->seqno,
+ pkt->is_fatal ? "Critical" : "Non-critical",
+ pkt->is_resp ? "Response" : "Request",
+ (int) ast_tvdiff_ms(ast_tvnow(), pkt->time_sent));
}
} else if (pkt->method == SIP_OPTIONS && sipdebug) {
ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See doc/sip-retransmit.txt.\n", pkt->owner->callid);
@@ -3294,8 +3319,6 @@ static int retrans_pkt(const void *data)
append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
}
- pkt->retransid = -1;
-
if (pkt->is_fatal) {
while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
sip_pvt_unlock(pkt->owner); /* SIP_PVT, not channel */
@@ -3408,6 +3431,9 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
if (pkt->timer_t1)
siptimer_a = pkt->timer_t1;
+ pkt->time_sent = ast_tvnow(); /* time packet was sent */
+ pkt->retrans_stop_time = 64 * (pkt->timer_t1 ? pkt->timer_t1 : DEFAULT_TIMER_T1); /* time in ms after pkt->time_sent to stop retransmission */
+
/* Schedule retransmission */
AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
if (sipdebug)
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 92fa68e82..2644536cd 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -83,7 +83,6 @@
#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */
#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */
-#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */
#define DEFAULT_TIMER_T1 500 /*!< SIP timer T1 (according to RFC 3261) */
#define SIP_TRANS_TIMEOUT 64 * DEFAULT_TIMER_T1 /*!< SIP request timeout (rfc 3261) 64*T1
* \todo Use known T1 for timeout (peerpoke)
@@ -1118,6 +1117,9 @@ struct sip_pkt {
int retransid; /*!< Retransmission ID */
int timer_a; /*!< SIP timer A, retransmission timer */
int timer_t1; /*!< SIP Timer T1, estimated RTT or 500 ms */
+ struct timeval time_sent; /*!< When pkt was sent */
+ int64_t retrans_stop_time; /*!< Time in ms after 'now' that retransmission must stop */
+ int retrans_stop; /*!< Timeout is reached, stop retransmission */
int packetlen; /*!< Length of packet */
struct ast_str *data;
};