aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoroej <oej@f38db490-d61c-443f-a65b-d21fe96a405b>2007-12-10 14:04:44 +0000
committeroej <oej@f38db490-d61c-443f-a65b-d21fe96a405b>2007-12-10 14:04:44 +0000
commit63a09fd7b0050ece8b4e6efdfbd53d45668a85f2 (patch)
tree46d5674a15c493e551c73be6e55b5df8e108294a
parentecbbfe3fb82f0a9493aa1d75c9b7146168c6b703 (diff)
Avoid reinvite race situations with two Asterisks trying
to reinvite each other in 1.4 and trunk. This patch implements support for the 491 error code that Asterisk 1.4 generates on situations where we get an incoming INVITE and already has one in progress. Thanks to mavetju for reporting and to Raj Jain for an excellent explanation of the problem. Patch by myself. Tested with 8 Asterisk servers connected to each other in a training network. Closes issue #10481 git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@92158 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_sip.c41
1 files changed, 36 insertions, 5 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 0407b2771..7bcf20d5a 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -986,6 +986,7 @@ static struct sip_pvt {
int maxtime; /*!< Max time for first response */
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 */
@@ -1326,7 +1327,6 @@ static int expire_register(const void *data);
static void *do_monitor(void *data);
static int restart_monitor(void);
static int sip_send_mwi_to_peer(struct sip_peer *peer);
-static void sip_destroy(struct sip_pvt *p);
static int sip_addrcmp(char *name, struct sockaddr_in *sin); /* Support for peer matching */
static int sip_refer_allocate(struct sip_pvt *p);
static void ast_quiet_chan(struct ast_channel *chan);
@@ -3043,6 +3043,8 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner)
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);
@@ -3562,6 +3564,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);
}
}
@@ -4366,6 +4371,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;
@@ -11892,7 +11898,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) {
if (option_debug)
ast_log(LOG_DEBUG, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid);
} else {
@@ -11905,6 +11911,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)
{
@@ -12205,9 +12225,20 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
/* 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 && !ast_test_flag(req, SIP_PKT_IGNORE))
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
+ if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) {
+ if (p->owner->_state != AST_STATE_UP) {
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
+ } 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);
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Reinvite race. Waiting %d secs before retry\n", wait);
+ }
+ }
break;
case 501: /* Not implemented */