Handle network errors, like host or network unreachable, in a better way. This means that

calls to hosts or qualify (OPTION) messages will fail quicker if the TCP/IP stack tells us
that there is an issue.

Since this is an unconnected UDP socket, we will not get error messages directly
in most cases, but maybe on the second and third try.

This is already implemented in trunk.


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@64280 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Olle Johansson 18 years ago
parent e694a86517
commit e041d57175

@ -1751,6 +1751,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;
@ -1852,6 +1861,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 */
ast_mutex_lock(&pkt->owner->lock);
@ -1891,19 +1901,25 @@ 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);
ast_mutex_unlock(&pkt->owner->lock);
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 of transaction in 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;
@ -1953,6 +1969,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;
@ -1977,13 +1994,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) */
@ -2175,7 +2199,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;
@ -2771,7 +2795,7 @@ static int auto_congest(void *nothing)
* used from the dial() application */
static int sip_call(struct ast_channel *ast, char *dest, int timeout)
{
int res;
int res, xmitres = 0;
struct sip_pvt *p;
struct varshead *headp;
struct ast_var_t *current;
@ -2845,7 +2869,10 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
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);
xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
if (xmitres == -2)
return -1; /* Transmission error */
p->invitestate = INV_CALLING;
/* Initialize auto-congest time */
@ -6417,7 +6444,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;
@ -7510,7 +7539,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;
@ -11587,16 +11618,16 @@ static struct ast_custom_function sipchaninfo_function = {
static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
{
char tmp[BUFSIZ];
char *s, *e;
char *s, *e, *uri;
char *domain;
ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp));
s = get_in_brackets(tmp);
s = strsep(&s, ";"); /* strip ; and beyond */
uri = ast_strdupa(s);
if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
if (!strncasecmp(s, "sip:", 4))
s += 4;
e = strchr(s, '/');
e = strchr(s, ';');
if (e)
*e = '\0';
if (option_debug)
@ -11612,14 +11643,19 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
/* No username part */
domain = tmp;
}
e = strchr(tmp, '/');
e = strchr(s, ';'); /* Strip of parameters in the username part */
if (e)
*e = '\0';
e = strchr(domain, ';'); /* Strip of parameters in the domain part */
if (e)
*e = '\0';
if (!strncasecmp(s, "sip:", 4))
s += 4;
if (option_debug > 1)
ast_log(LOG_DEBUG, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain);
if (p->owner) {
pbx_builtin_setvar_helper(p->owner, "SIPREDIRECTURI", uri);
pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain);
ast_string_field_set(p->owner, call_forward, s);
}
@ -11653,6 +11689,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;
@ -11832,13 +11869,13 @@ 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 == 401 ? WWW_AUTH : PROXY_AUTH);
@ -11861,7 +11898,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);
@ -11870,7 +11907,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);
@ -11879,7 +11916,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);
@ -11888,14 +11925,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
@ -11925,18 +11962,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
@ -13078,6 +13117,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 */
@ -15220,6 +15260,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
@ -15265,12 +15306,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;
}

Loading…
Cancel
Save