Map SIP Hangup Codes and reason text 1:1

i am using A2Billing which uses Asterisk 13 (I know its EOL)
Is there an easy OOB method to parse back the SIP cause code the carrier is sending us when Asterisk is acting as a B2BUA.
Example, if the carrier legB sends back 604, asterisk sends 404 to the client
If the carrier LebB sends back a 487, asterisk sends a 603 to the client
I just want to parse back the exact same cause code and cause text to the UAC

same => n,Noop(${DIALSTATUS})
same => n,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?anotherattempt)
same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL"]?anotherattempt)
same => n,GotoIf($["${DIALSTATUS}" = "BUSY"]?totalhangup)
same => n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?totalhangup)
same => n(totalhangup),Hangup()
same => n(hangupbaduser),Hangup(57)
same => n(hangupnoratecard),Hangup(38)
same => n(hangupratecardnodest),Hangup(52)
same => n(hangupnotrunk),Hangup(44)

;Hangup the call
exten => h,1,GotoIf($[${strusername}=${CDR(accountcode)}]?h,oktolog:kickoff)   ; 2 for OK 31 for Kick off
same => n(oktolog),Set(StrSessiontime=${CDR(billsec)})
same => n,Set(StrCalledstation=${dst})
same => n,Set(StrDialstatus=${DIALSTATUS})
same => n,Set(StrDialstatus_value=9)
same => n,Set(sellcost=0)
same => n,Set(buycost=0)
same => n,Set(StrChannel=${CDR(channel)})
same => n,GotoIf($["${StrSessiontime}" = "0"]?h,setsellcost:calcsellcost)
same => n(calcsellcost),ExecIf($[${StrSessiontime}>${str__initblock}]?set(sellcost=$[CEIL(${StrSessiontime}/${str__billingblock})*${str__billingblock}/60*${str__rateinitial}]))
same => n,ExecIf($[${StrSessiontime}<=${str__initblock}]?set(sellcost=$[${str__initblock}/60*${str__rateinitial}]))
same => n(setsellcost),set(sellcost_all=${sellcost})
same => n,GotoIf($["${StrSessiontime}" = "0"]?h,setbuycost:calcbuycost)
same => n(calcbuycost),ExecIf($[${StrSessiontime}>${str__buyrateinitblock}]?set(buycost=$[CEIL(${StrSessiontime}/${str__buyrateincrement})*${str__buyrateincrement}/60*${str__buyrate}]))
same => n,ExecIf($[${StrSessiontime}<=${str__buyrateinitblock}] ?set(buycost=$[${str__buyrateinitblock}/60*${str__buyrate}]))
same => n(setbuycost),set(buycost_all=${buycost})
same => n,ExecIf($[ "${StrDialstatus}" = "CHANUNAVAIL" ]?Set(StrDialstatus_value=6))
same => n,ExecIf($[ "${StrDialstatus}" = "CANCEL" ]?Set(StrDialstatus_value=4))
same => n,ExecIf($[ "${StrDialstatus}" = "BUSY" ]?Set(StrDialstatus_value=2))
same => n,ExecIf($[ "${StrDialstatus}" = "CONGESTION" ]?Set(StrDialstatus_value=5))
same => n,ExecIf($[ "${StrDialstatus}" = "ANSWER" ]?Set(StrDialstatus_value=1))
same => n,ExecIf($[ "${StrDialstatus}" = "NOANSWER" ]?Set(StrDialstatus_value=3))
same => n,ExecIf($[ "${StrDialstatus}" = "" ]?Set(StrDialstatus_value=6))
same => n,Set(BILLING_CDRUPDATE()=${CDR(uniqueid)},${CDR(channel)},${strid},${StrSessiontime},${StrCalledstation},${StrDialstatus_value},${sellcost_all},${str_tariffgroup_id},${str_idtariffplan},${str_ratecard_id},${trunk_id_trunk},${CALLERID(num)},${buycost_all},${CDR(dnid)},${str_dialprefix})
same => n,NOOP(sellcost=${sellcost},buycost=${buycost})
same => n,NOOP(finished)
same => n(kickoff),NoOp(Kick Off)

I’m not aware of any version of Asterisk that allows control of technology specific cause codes being sent.

Asterisk was designed for circuit switching and had SIP bolted on. As a result, internally, it uses ISDN cause codes. Although there may be some violations, Asterisk is basically following rfc3398 on both legs. The ISDN cause codes don’t map 1:1 with SIP ones, so the conversion process will be lossy.

Note that, if either leg is a PSTN number, similar conversions will have taken place at the PSTN boundary.

Some carriers, will send you the Q.850 cause codes, if that’s the case, you can use that with the hangup function, to send what Asterisk think is the correct SIP response. They are usually send in a header prefixed with X- as they are not part of the SIP standard.

Hey guys. This is a pure SIP environment with 0 ISDN.
I just want to straight parse the returned SIP code without asterisk touching it. Can’t be done ?

Hey Guys,
Surely this is possible. I might have a go at this
https://wiki.asterisk.org/wiki/display/AST/Hangup+Cause
If you have already utlized this in your code at some point would be good to know if you had success

Have you tried ${HANGUPCAUSE(${CHANNEL(name)},tech)} ? Because I use that with chan_pjsip channels and it will give me the full SIP code/cause when the channel is hungup. You’ll get the full reason code like “180 Ringing” or “200 OK” so I end up parsing the reason text away to just have to code to deal with.

I don’t think the issue is reading the codes, it is that you can’t set the codes (and if the call originated or terminated on the PSTN, the codes will be translated by the PSTN provider, anyway).

Well as I read it, this seems more to do with how Asterisk handles the call between the channels. So I call from my phone to Asterisk which then has Asterisk call to another destination. That destination returns a 603 to Asterisk. Now Asterisk needs to tell my phone what happened so it sends back 503. This is a common issue with a Back2Back User Agent like Asterisk. I mean when the destination returns a 603, nothing states that Asterisk has to tell the endpoint it was a 603. I could have made that go out another Dial() to a backup destination.

I had this issue so this is why I use the hangupcause option I posted so I can process the response, send it back down with something as close as I can. Now it does help that I use Kamailio because Asterisk is not meant to deal with manipulating SIP packets and I do a little magic between the users and Asterisk systems.

Because outside of Asterisk having weird cause codes sometimes, I’ve had users with devices that send back things like a 603 instead of a 486 and that throws off failover routing or how things should respond. In those cases I’ve had to write some little extra code to handle error responses for certain users a different way.

Specifically Asterisk converts the SIP status to an ISDN one and then does the reverse if forwarding the status onto another SIP peer.

ok, so thinking outside the box. Am I able to create a custom SIP Header and parse that down to the A-Leg. I do not see the headers in eg the 404 message or 603 message asterisk sends to LegA
following Hangup Cause - Asterisk Project - Asterisk Project Wiki

[a2billing-test]
exten => _X.,1,Set(Strheader=${SIP_HEADER(TO):5})
;test code 1
same => n,Set(CHANNEL(hangup_handler_push)=a2billing-test-handler,s,1)
same => n,Set(dst=${CUT(Strheader,@,1)})
same => n,Set(dst=${EXTEN})
same => n,Dial(SIP/${EXTEN}@test-freeswitch,60)
same => n,Hangup()

[a2billing-test-handler]
exten => s,1,NoOp()
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
; start loop
same => n(hu_begin),NoOp()
; check exit condition (no more array to check)
same => n,GotoIf($[${LEN(${HANGUPCAUSE_STRING})}=0]?hu_exit)
; pull the next item
same => n,Set(ARRAY(item)=${HANGUPCAUSE_STRING})
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_STRING:${LEN(${item})}})
; display the channel name and cause codes
same => n,Verbose(0, Got Channel ID ${item} with Technology Cause Code ${HANGUPCAUSE(${item},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${item},ast)})
same => n,SIPAddHeader(X-SIP-Code: ${HANGUPCAUSE(${item},tech)})
same => n,SIPAddHeader(X-Asterisk-Code: ${HANGUPCAUSE(${item},ast)})
; check exit condition (no more array to check)
same => n,GotoIf($[${LEN(${HANGUPCAUSE_STRING})}=0]?hu_exit)
; we still have entries to process, so strip the leading comma
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_STRING:1})
; go back to the beginning of the loop
same => n,Goto(hu_begin)
same => n(hu_exit),NoOp()
same => n,Return()

== Spawn extension (a2billing-test, 0111111111, 6) exited non-zero on ‘SIP/5928962635-008f7f82’
– SIP/5928962635-008f7f82 Internal Gosub(a2billing-test-handler,s,1) start
– Executing [s@a2billing-test-handler:1] NoOp(“SIP/5928962635-008f7f82”, “”) in new stack
– Executing [s@a2billing-test-handler:2] Set(“SIP/5928962635-008f7f82”, “HANGUPCAUSE_STRING=SIP/test-freeswitch-008f7f83”) in new stack
– Executing [s@a2billing-test-handler:3] NoOp(“SIP/5928962635-008f7f82”, “”) in new stack
– Executing [s@a2billing-test-handler:4] GotoIf(“SIP/5928962635-008f7f82”, “0?hu_exit”) in new stack
– Executing [s@a2billing-test-handler:5] Set(“SIP/5928962635-008f7f82”, “ARRAY(item)=SIP/test-freeswitch-008f7f83”) in new stack
– Executing [s@a2billing-test-handler:6] Set(“SIP/5928962635-008f7f82”, “HANGUPCAUSE_STRING=”) in new stack
– Executing [s@a2billing-test-handler:7] Verbose(“SIP/5928962635-008f7f82”, “0, Got Channel ID SIP/test-freeswitch-008f7f83 with Technology Cause Code SIP 487 Request Terminated, Asterisk Cause Code Interworking, unspecified”) in new stack
Got Channel ID SIP/test-freeswitch-008f7f83 with Technology Cause Code SIP 487 Request Terminated, Asterisk Cause Code Interworking, unspecified
– Executing [s@a2billing-test-handler:8] SIPAddHeader(“SIP/5928962635-008f7f82”, “X-SIP-Code: SIP 487 Request Terminated”) in new stack
– Executing [s@a2billing-test-handler:9] SIPAddHeader(“SIP/5928962635-008f7f82”, “X-Asterisk-Code: Interworking, unspecified”) in new stack
– Executing [s@a2billing-test-handler:10] GotoIf(“SIP/5928962635-008f7f82”, “1?hu_exit”) in new stack
– Goto (a2billing-test-handler,s,13)
– Executing [s@a2billing-test-handler:13] NoOp(“SIP/5928962635-008f7f82”, “”) in new stack
– Executing [s@a2billing-test-handler:14] Return(“SIP/5928962635-008f7f82”, “”) in new stack
== Spawn extension (a2billing-test, 0111111111, 6) exited non-zero on ‘SIP/5928962635-008f7f82’
– SIP/5928962635-008f7f82 Internal Gosub(a2billing-test-handler,s,1) complete GOSUB_RETVAL=

SIP headers can only be added to an outgoing INVITE.

yeah thanks I had a feeling this was the case. There are some asterisk headers in the Bye message but this must be hard coded.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.