diff options
author | Olle Johansson <oej@edvina.net> | 2007-05-13 19:20:36 +0000 |
---|---|---|
committer | Olle Johansson <oej@edvina.net> | 2007-05-13 19:20:36 +0000 |
commit | fc057b18909e002f1c4a7331a11eebc959f55d47 (patch) | |
tree | 2d18f55b9001b6fda38b6aea8612ee3fd4c5dd72 /channels | |
parent | b71e691b3e100d5c76bdfdbc11c51cb0566e4351 (diff) |
Improve handling network errors on transmission to hosts that don't reply or are unreachable
With this code, the call will fail as soon as we get a network error. This may happen on
first xmit or a later one, so the retransmit code handles this too.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@64142 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 101 |
1 files changed, 72 insertions, 29 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 915a1b909..e9e38d08f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1915,6 +1915,15 @@ static int __sip_xmit(struct sip_pvt *p, char *data, int len) const struct sockaddr_in *dst = sip_real_dst(p); res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in)); + if (res == -1) { + switch (errno) { + case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */ + case EHOSTUNREACH: /* Host can't be reached */ + case ENETDOWN: /* Inteface down */ + case ENETUNREACH: /* Network failure */ + res = -2; /* Don't bother with trying to transmit again */ + } + } if (res != len) ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno)); return res; @@ -2009,6 +2018,7 @@ static int retrans_pkt(void *data) { struct sip_pkt *pkt = data, *prev, *cur = NULL; int reschedule = DEFAULT_RETRANS; + int xmitres = 0; /* Lock channel PVT */ sip_pvt_lock(pkt->owner); @@ -2048,19 +2058,27 @@ static int retrans_pkt(void *data) } append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data); - __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); + xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); sip_pvt_unlock(pkt->owner); - return reschedule; + if (xmitres == -2) { + ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); + } else { + return reschedule; + } } /* Too many retries */ - if (pkt->owner && pkt->method != SIP_OPTIONS) { + if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) { if (ast_test_flag(pkt, FLAG_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)\n", pkt->owner->callid, pkt->seqno, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request"); - } else { - if ((pkt->method == SIP_OPTIONS) && sipdebug) + } else if (pkt->method == SIP_OPTIONS && sipdebug) { ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid); - } - append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); + + } + if (xmitres == -2) { + ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid); + append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); + } else + append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); pkt->retransid = -1; @@ -2105,6 +2123,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res { struct sip_pkt *pkt; int siptimer_a = DEFAULT_RETRANS; + int xmitres = 0; if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1))) return AST_FAILURE; @@ -2129,13 +2148,20 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res ast_log(LOG_DEBUG, "*** SIP TIMER: Initalizing retransmit timer on packet: Id #%d\n", pkt->retransid); pkt->next = p->packets; p->packets = pkt; - - __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */ if (sipmethod == SIP_INVITE) { /* Note this is a pending invite */ p->pendinginvite = seqno; } - return AST_SUCCESS; + + xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */ + + if (xmitres == -2) { /* Serious network trouble, no need to try again */ + append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); + ast_sched_del(sched, pkt->retransid); /* No more retransmission */ + pkt->retransid = -1; + return AST_FAILURE; + } else + return AST_SUCCESS; } /*! \brief Kill a SIP dialog (called by scheduler) */ @@ -2333,7 +2359,7 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text); } res = (reliable) ? - __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) : + __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) : __sip_xmit(p, req->data, req->len); if (res > 0) return 0; @@ -3163,14 +3189,19 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username); res = -1; } else { + int xmitres; + p->t38.jointcapability = p->t38.capability; if (option_debug > 1) ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); - transmit_invite(p, SIP_INVITE, 1, 2); - p->invitestate = INV_CALLING; + xmitres = transmit_invite(p, SIP_INVITE, 1, 2); + if (xmitres != -2) { + p->invitestate = INV_CALLING; - /* Initialize auto-congest time */ - p->initid = ast_sched_add(sched, SIP_TRANS_TIMEOUT, auto_congest, p); + /* Initialize auto-congest time */ + p->initid = ast_sched_add(sched, SIP_TRANS_TIMEOUT, auto_congest, p); + } else + res = -1; } return res; @@ -6991,7 +7022,9 @@ static void copy_request(struct sip_request *dst, const struct sip_request *src) dst->rlPart2 += offset; } -/*! \brief Used for 200 OK and 183 early media */ +/*! \brief Used for 200 OK and 183 early media + \return Will return -2 for network errors. +*/ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable) { struct sip_request resp; @@ -8090,7 +8123,9 @@ static int transmit_info_with_vidupdate(struct sip_pvt *p) return send_request(p, &req, XMIT_RELIABLE, p->ocseq); } -/*! \brief Transmit generic SIP request */ +/*! \brief Transmit generic SIP request + returns -2 if transmit failed with a critical error (don't retry) +*/ static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch) { struct sip_request resp; @@ -12495,6 +12530,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru { int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING); int res = 0; + int xmitres = 0; int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); struct ast_channel *bridgepeer = NULL; @@ -12678,14 +12714,14 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru } /* If I understand this right, the branch is different for a non-200 ACK only */ p->invitestate = INV_TERMINATED; - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); check_pendings(p); break; case 407: /* Proxy authentication */ case 401: /* Www auth */ /* First we ACK */ - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->options) p->options->auth_type = resp; @@ -12706,7 +12742,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru case 403: /* Forbidden */ /* First we ACK */ - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From")); if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); @@ -12715,7 +12751,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru break; case 404: /* Not found */ - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + 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); sip_alreadygone(p); @@ -12724,7 +12760,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru case 481: /* Call leg does not exist */ /* Could be REFER caused INVITE with replaces */ ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid); - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->owner) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -12733,14 +12769,14 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru /* We have sent CANCEL on an outbound INVITE This transaction is already scheduled to be killed by sip_hangup(). */ - transmit_request(p, SIP_ACK, seqno, 0, 0); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) ast_queue_hangup(p->owner); else if (!ast_test_flag(req, SIP_PKT_IGNORE)) update_call_counter(p, DEC_CALL_LIMIT); break; case 488: /* Not acceptable here */ - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (reinvite && p->udptl) { /* If this is a T.38 call, we should go back to audio. If this is an audio call - something went @@ -12770,18 +12806,20 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru /* 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 */ - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); + 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); break; case 501: /* Not implemented */ - transmit_request(p, SIP_ACK, seqno, 0, 0); + xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->owner) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); break; } + if (xmitres == -2) + ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid); } /* \brief Handle SIP response in REFER transaction @@ -13934,6 +13972,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in /* Answer the incoming call and set channel to UP state */ transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE); + ast_setstate(c, AST_STATE_UP); /* Stop music on hold and other generators */ @@ -16075,6 +16114,7 @@ static int sip_poke_noanswer(void *data) static int sip_poke_peer(struct sip_peer *peer) { struct sip_pvt *p; + int xmitres = 0; if (!peer->maxms || !peer->addr.sin_addr.s_addr) { /* IF we have no IP, or this isn't to be monitored, return @@ -16120,12 +16160,15 @@ static int sip_poke_peer(struct sip_peer *peer) ast_set_flag(&p->flags[0], SIP_OUTGOING); #ifdef VOCAL_DATA_HACK ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username)); - transmit_invite(p, SIP_INVITE, 0, 2); + xmitres = transmit_invite(p, SIP_INVITE, 0, 2); #else - transmit_invite(p, SIP_OPTIONS, 0, 2); + xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2); #endif gettimeofday(&peer->ps, NULL); - peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer); + if (xmitres == -2) + sip_poke_noanswer(peer); /* Immediately unreachable, network problems */ + else + peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer); return 0; } |