diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 596501ba6..15f821545 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1132,6 +1132,7 @@ struct sip_pvt { */ int initid; /*!< Auto-congest ID if appropriate (scheduler) */ + int waitid; /*!< Wait ID for scheduler after 491 or other delays */ int autokillid; /*!< Auto-kill ID (scheduler) */ enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ struct sip_refer *refer; /*!< REFER: SIP transfer data structure */ @@ -1691,7 +1692,7 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char Functions for enabling debug per IP or fully, or enabling history logging for a SIP dialog */ -static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ +static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to debuglog at end of dialog, before destroying data */ static inline int sip_debug_test_addr(const struct sockaddr_in *addr); static inline int sip_debug_test_pvt(struct sip_pvt *p); static void append_history_full(struct sip_pvt *p, const char *fmt, ...); @@ -2464,8 +2465,7 @@ static int __sip_autodestruct(const void *data) /* If there are packets still waiting for delivery, delay the destruction */ if (p->packets) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : ""); + ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : ""); append_history(p, "ReliableXmit", "timeout"); return 10000; } @@ -3690,6 +3690,8 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) ast_extension_state_del(p->stateid, NULL); if (p->initid > -1) ast_sched_del(sched, p->initid); + if (p->waitid > -1) + ast_sched_del(sched, p->waitid); if (p->autokillid > -1) ast_sched_del(sched, p->autokillid); @@ -4214,6 +4216,9 @@ static int sip_hangup(struct ast_channel *ast) but we can't send one while we have "INVITE" outstanding. */ ast_set_flag(&p->flags[0], SIP_PENDINGBYE); ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE); + if (p->waitid) + ast_sched_del(sched, p->waitid); + p->waitid = -1; sip_cancel_destroy(p); } } @@ -5055,6 +5060,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->method = intended_method; p->initid = -1; + p->waitid = -1; p->autokillid = -1; p->subscribed = NONE; p->stateid = -1; @@ -13338,7 +13344,7 @@ static void check_pendings(struct sip_pvt *p) sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) { /* if we can't REINVITE, hold it for later */ - if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) { + if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > 0) { ast_debug(2, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid); } else { ast_debug(2, "Sending pending reinvite on '%s'\n", p->callid); @@ -13349,6 +13355,20 @@ static void check_pendings(struct sip_pvt *p) } } +/*! \brief Reset the NEEDREINVITE flag after waiting when we get 491 on a Re-invite + to avoid race conditions between asterisk servers. + Called from the scheduler. +*/ +static int sip_reinvite_retry(const void *data) +{ + struct sip_pvt *p = (struct sip_pvt *) data; + + ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); + p->waitid = -1; + return 0; +} + + /*! \brief Handle SIP response to INVITE dialogue */ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) { @@ -13639,13 +13659,20 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru } break; case 491: /* Pending */ - /* we really should have to wait a while, then retransmit */ - /* We should support the retry-after at some point */ - /* At this point, we treat this as a congestion */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !req->ignore) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - p->needdestroy = 1; + if (p->owner && !req->ignore) { + if (p->owner->_state != AST_STATE_UP) { + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + p->needdestroy = 1; + } else { + /* This is a re-invite that failed. */ + /* Reset the flag after a while + */ + int wait = 3 + ast_random() % 5; + p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p); + ast_debug(2, "Reinvite race. Waiting %d secs before retry\n", wait); + } + } break; case 501: /* Not implemented */