[asterisk-commits] oej: trunk r64142 - /trunk/channels/chan_sip.c

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sun May 13 12:20:36 MST 2007


Author: oej
Date: Sun May 13 14:20:36 2007
New Revision: 64142

URL: http://svn.digium.com/view/asterisk?view=rev&rev=64142
Log:
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.

Modified:
    trunk/channels/chan_sip.c

Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?view=diff&rev=64142&r1=64141&r2=64142
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Sun May 13 14:20:36 2007
@@ -1915,6 +1915,15 @@
 	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 @@
 {
 	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 @@
 		}
 
 		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 @@
 {
 	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 @@
 		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 @@
 			(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 @@
 		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 @@
 	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 @@
 	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 @@
 {
 	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 @@
 		}
 		/* 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 @@
 
 	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 @@
 		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 @@
 	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 @@
 		/* 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 @@
 		/* 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 @@
 
 	/* 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_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 @@
 	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;
 }



More information about the asterisk-commits mailing list