newspaint

Documenting Problems That Were Difficult To Find The Answer To

Category Archives: Asterisk

Capturing Incoming SIP Address With Asterisk

When a call comes into your Asterisk server via a SIP trunk or just over SIP it will usually have ${CALLERID(num)} set to the incoming number if that call originated from the plain old telephony system (POTS).

However how do you capture caller ID from a SIP address such as john.smith@sip.company.com?

Well, in this case ${CALLERID(num)} will contain the text john.smith – but getting the domain name sip.company.com is trickier.

In actual fact the domain will be found in the ${CHANNEL} variable looking something like SIP/sip.company.com-000000f4. Now you can use the ${CUT(CHANNEL,-,2)} in a vain hope to trim off that hexadecimal number at the end – but this will fail miserably if there is a dash in the domain name (a perfectly legal thing to do).

Instead we need to define a macro that will trim off everything up to the right-most dash. The following will do this and store the result in ${trimresult}:

[macro-trim-to-dash]
exten => s,1,NoOp()
  same => n,Verbose(parameter is <${ARG1}>)
  same => n(loop),NoOp()
  same => n,GotoIf($["${ARG1}" = ""]?end)
  same => n,GotoIf($[${LEN(${ARG1})} < 2]?end)
  same => n,GotoIf($["${ARG1:-1}" = "-"]?founddash)
  same => n,Set(ARG1=${ARG1:0:$[${LEN(${ARG1})}-1]})
  same => n,Goto(loop)
  same => n(founddash),Set(ARG1=${ARG1:0:$[${LEN(${ARG1})}-1]})
  same => n(end),Set(trimresult=${ARG1})

Now we can define a macro that filters our incoming caller ID to prevent any nasty characters from having side effects when creating files or calling system binaries (result is in ${FILTERED_ID}):

[macro-whocalling]
exten => s,1,NoOp()
  same => n,GotoIf($["${CUT(CHANNEL,/,1)}" != "SIP"]?notsip)
  same => n,Macro(trim-to-dash,${CHANNEL})
  same => n,Set(sipdomainin=${CUT(trimresult,/,2)})
  same => n,Set(FILTERED_ID=${FILTER(0-9a-zA-Z@._ +,${CALLERID(num)}@${sipdomainin})})
  same => n,Goto(end)
  same => n(notsip),NoOp()
  same => n,GotoIf($["${CALLERID(num)}" = "s"]?nonum)
  same => n,GotoIf($["${CALLERID(num)}" = ""]?nonum)
  same => n,GotoIf($["${FILTER(0-9a-zA-Z@._ +,${CALLERID(num)})}" = ""]?nonum)
  same => n,Set(FILTERED_ID=${FILTER(0-9a-zA-Z@._ +,${CALLERID(num)})})
  same => n,Goto(end)
  same => n(nonum),Set(FILTERED_ID=${FILTER(0-9a-zA-Z@._ +,${CALLERID(name)})})
  same => n(end),NoOp()

Be aware that when you have an incoming call you may want to capture the incoming caller ID into the ${FILTERED_ID} variable – but this will not be inherited when you issue the Dial() command.

So if you’re planning to use the incoming caller ID by a dialled extension you need to subsequently create another variable with a leading underscore – such variables are inherited by sub-channels with the prefixed underscore removed. E.g.:

  same => n,Set(_FILTERED_ID=${FILTERED_ID})
  same => Dial(SIP/101&SIP/102, 15, M(write-debug-file))

Linksys SIP Call Terminates After 32 Seconds Because of Invalid Asterisk Contact Header

I had a friend call me from their Linksys VoIP phone to my Asterisk server using SIP (over the Internet).

The call would come in – ring my internal extension just fine. I would pick up the phone – and we could both hear each other.

However approximately 30 seconds later the call would terminate suddenly. It turned out the call was terminated precisely 32 seconds later.

Using debugging (sip set debug on) I captured the SIP packets for debugging.

The incoming INVITE from my friend looked like this (anonymised):

INVITE sip:222@sip.mydomain.com SIP/2.0
Via: SIP/2.0/UDP 123.123.123.123:5060;branch=z9hG4bK-fe593a22;rport
From: "Test Client" <sip:1234567890@sip.pennytel.com>;tag=a824af307af335a1o0
To: <sip:222@sip.mydomain.com>
Call-ID: 1c5d63e3-6ade2eca@123.123.123.123
CSeq: 101 INVITE
Max-Forwards: 70
Contact: "Test Client" <sip:1234567890@123.123.123.123:5060>
Expires: 240
User-Agent: Linksys/SPA942-6.1.5(a)
Content-Length: 399
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER
Supported: replaces
Content-Type: application/sdp

All good. Well, things would go well until I picked up the call and I would see the SIP OK from my Asterisk server being retransmitted without acknowledgement from my friend’s Linksys phone:

Retransmitting #10 (NAT) to 123.123.123.123:5060:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 123.123.123.123:5060;branch=z9hG4bK-fe593a22;received=123.123.123.123;rport=5060
From: "Test Client" <sip:1234567890@sip.pennytel.com>;tag=a824af307af335a1o0
To: <sip:222@sip.mydomain.com>
Call-ID: f8227f59-90d685a6@123.123.123.123
CSeq: 101 INVITE

[Sep 8 13:48:08] WARNING[15643]: chan_sip.c:3641 retrans_pkt: Retransmission timeout reached on transmission f8337f49-91d68a12@123.123.123.123 for seqno 101 (Critical Response) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions
Packet timed out after 32000ms with no response

I used tcpdump to confirm that no SIP packets were being transmitted to me after I picked up the call.

So I looked up a little further to see what Asterisk was telling the Linksys phone after it attempted to INVITE me to a call.

SIP/2.0 200 OK
Via: SIP/2.0/UDP 123.123.123.123:5060;branch=z9hG4bK-fe593a22;received=123.123.123.123;rport=5060
From: "Test Client" <sip:1234567890@sip.pennytel.com>;tag=a824af307af335a1o0
To: <sip:222@sip.mydomain.com>;tag=as7cf44a28
Call-ID: 91d68a12-f8337f49@123.123.123.123
CSeq: 101 INVITE
Server: Asterisk PBX 1.8.10.1~dfsg-1ubuntu1
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Contact: <sip:222@192.168.2.12:5060>
Content-Type: application/sdp
Content-Length: 277

My Asterisk server was sending a Contact: header with an internal IP address! And the Linksys phone was using this Contact-header (Contact-URI) to send all future SIP packets. Thus I stopped receiving SIP packets from the Linksys phone once I answered the call. Now my friend and I could talk – RTP was working – but the call would be terminated by Asterisk after 32 seconds.

How To Fix The Contact Header In Asterisk

There were two steps. Firstly I needed to specify what my external IP address was. There are two ways. If you know your external IP address and it doesn’t change then specify it directly:

[general]
externip=123.123.123.123

However if you’re running an Asterisk server at home you probably have a dynamic address. I personally use a free Dynamic DNS account and have my SIP domain name as a CNAME to my Dynamic DNS domain name. In this case specify:

[general]
externhost=sip.mydomain.com
externrefresh=180           ; update address from domain name every 3 minutes

The next step is to ensure your Asterisk server knows what a local address is – so that it can present this externip or externhost in the Contact header when INVITEd from outside your local network:

[general]
localnet=192.168.0.0/255.255.0.0
localnet=172.16.0.0/255.240.0.0
localnet=10.0.0.0/255.0.0.0

Now you should see outgoing Contact: URLs using your external IP address – and the Linksys phone should now be able to correctly contact your Asterisk server once you’ve picked up your extension to answer.

Alternative Method

What if your Asterisk server is NATted behind another NAT? For example – in my configuration I had a router (running NAT) connected to the Internet and my home network. On my home network I had a Linux server (also running NAT) – and hosted on that Linux server I had a LXC container running my Asterisk server.

Linux has the nf_nat_sip module which should re-write the appropriate Contact: header.

Try loading the module on your NAT server by typing modprobe nf_nat_sip, confirm it is loaded with lsmod |grep nf_nat_sip, and re-try your call.

Creating A Basic IVR With Asterisk

So you want to start your home business using Asterisk. And you’d like incoming callers to be treated to the customary interactive voice response (IVR) that many modern businesses have.

Some Useful Links

When playing with dialplans keep these Asterisk links handy: application commands, variables, functions, and standard extensions.

The Audio

Start by recording a few voice prompts, such as “Welcome to Widgets Incorporated”. Save this as a .wav file.

Note that, for Asterisk to handle the voice files appropriately, they should be in 16-bit 8000Hz format. You can use ffmpeg to convert your audio files into the appropriate output format:

user@host:~> ffmpeg -i welcome-to-widgets-orig.wav \
  -ac 1 -ab 128000 -ar 8000 welcome-to-widgets-8000.wav
Input #0, wav, from 'welcome-to-widgets-orig.wav':
  Duration: 00:00:01.96, bitrate: 705 kb/s
    Stream #0.0: Audio: pcm_s16le, 44100 Hz, 1 channels, s16, 705 kb/s
Output #0, wav, to 'welcome-to-widgets-8000.wav':
  Metadata:
    encoder         : Lavf53.21.1
    Stream #0.0: Audio: pcm_s16le, 8000 Hz, 1 channels, s16, 128 kb/s
Stream mapping:
  Stream #0.0 -> #0.0
Press ctrl-c to stop encoding
size=      31kB time=1.96 bitrate= 128.2kbits/s
video:0kB audio:31kB global headers:0kB muxing overhead 0.146721%

Next – where do you put your audio files? You have to look it up from /etc/asterisk/asterisk.conf:

user@host:~> grep astdatadir /etc/asterisk/asterisk.conf
astdatadir => /usr/share/asterisk

So on my system sounds are, by default, at /usr/share/asterisk/sounds/. You can create subfolders but always be sure to prefix the subfolder name in the extensions.conf dialplan if you do.

The Dialplan

Now we have to create our dialplan. For the sake of testing let’s define an extension that will hook straight into our IVR.

exten => _755,1,NoOp()
  same => n,Goto(ivr-root,s,1)

This says that if 755 is dialled then we should jump into the ivr-root context with the s extension.

Now, what do we want our IVR to do? Maybe answer the phone and then pause for 500ms before the next step (the Answer() function does this). Next read out a message in the background while listening for an extension (the Background() function does this). Finally wait after the background message has been played for several seconds for an extension to be dialed (the WaitExten()function does this). The following would accomplish this:

[ivr-root]
exten => s,1,NoOp()
  same => n,Answer(500)
  same => n,Background(welcome-to-widgets-8000&enter-extension)
  same => n,WaitExten(3)
  same => n,HangUp()

You could write the Background statement as two separate statements (might be easier to read):

[ivr-root]
exten => s,1,NoOp()
  same => n,Answer(500)
  same => n,Background(custom/welcome-to-widgets-8000)
  same => n,Background(custom/enter-extension)
  same => n,WaitExten(3)
  same => n,HangUp()

Now you can give options and jump to other contexts. Imagine if you had a message “Please press 1 for Jim, press 2 for Bob, press 3 to hear this option again” saved as option-readout.wav you could have:

[ivr-root]
exten => s,1,NoOp()
  same => n,Answer(500)
  same => n,Background(custom/welcome-to-widgets-8000)
  same => n(my_marker),Background(custom/option-readout.wav)
  same => n,WaitExten(3)
  same => n,HangUp()

exten => 1,1,Dial(SIP/101) ; dial Jim

exten => 2,1,Dial(SIP/102) ; dial Bob

exten => 3,1,Goto(s,my_marker) ; repeat options

What if you want to press 2 for a new (e.g. sales) menu? Press 3 for a different (e.g. technical) menu? Create new contexts for the new menus.

[ivr-root]
exten => s,1,NoOp()
  same => n,Answer(500)
  same => n,Background(custom/welcome-to-widgets-8000)
  same => n(my_marker),Background(custom/option-readout)
  same => n,WaitExten(3)
  same => n,HangUp()

exten => 1,1,Dial(SIP/101) ; dial Jim

exten => 2,1,Goto(ivr-menu-sales,s,1)
exten => 3,1,Goto(ivr-menu-tech,s,1)

exten => 3,1,Goto(s,my_marker) ; repeat options

; ----
; sales group
[ivr-menu-sales]
exten => s,1,NoOp()
  same => n,Background(custom/options-sales)
  same => n,WaitExten(3)
  same => n,HangUp()

exten => 0,1,Goto(ivr-root,s,my_marker) ; go back to main manu

exten => 1,1,Dial(SIP/102) ; dial Bob

; ----
; technical group
[ivr-menu-tech]
exten => s,1,NoOp()
  same => n,Background(custom/options-tech)
  same => n,WaitExten(3)
  same => n,HangUp()

exten => 0,1,Goto(ivr-root,s,my_marker) ; go back to main manu

exten => 1,1,Playback(custom/our-website-location)

exten => 2,1,Playback(custom/frequently-asked-questions)

exten => 3,1,Dial(SIP/103) ; dial Fred

Diagnosis

To diagnose your IVR dialplan it is probably best to run Asterisk in verbose mode.

user@host:~> asterisk -rvvvv

To switch off verbose mode run asterisk again:

user@host:~> asterisk -r
host*CLI> core set verbose 1
Verbosity was 4 and is now 1

Issues

Frequency Mismatch

[Sep  3 13:22:55] WARNING[23571]: format_wav.c:110 check_header_fmt: Unexpected frequency mismatch 44100 (expecting 8000)

You may have to convert the file into an appropriate format using ffmpeg or similar. See also this blog post.

File Not Found

[Sep  3 13:25:05] WARNING[23726]: file.c:663 ast_openstream_full: File custom/welcome-to-widgets-8000 does not exist in any format
[Sep  3 13:25:05] WARNING[23726]: file.c:958 ast_streamfile: Unable to open custom/welcome-to-widgets-8000 (format 0x4 (ulaw)): No such file or directory

You need to double check the spelling of the file name, that the file actually exists in the directory, that the permissions of the file and directory allow reading, and that the Asterisks thinks sounds are where you think they are.

You can try specifying the full path to the file, e.g.:

  same => n,Background(/usr/local/share/asterisk/sounds/custom/welcome-to-widgets-8000)

If that succeeds then double check your asterisk.conf file.

Voipfone Peer With Asterisk Reports No Funds

I had set up Voipfone (a VoIP provider) as a peer to my Asterisk server. It registered fine and my extensions could call the Voipfone test numbers without any problems (155 the confirmation test, 152 the echo test).

But whenever I tried to call a real phone number (my mobile or a local store) I got the following message in a British female voice:

Sorry your call can't be connected. Please try again.

I turned on SIP debugging in Asterisk:

myuser@myhost:~# asterisk -r
myhost*CLI> sip set debug on
myhost*CLI>

Note that in this example my Asterisk server is on 192.168.1.2. The Voipfone SIP server is at 195.189.173.27. My extension is 234 that I’m making the call from.

Next I tried making a call (to Pizza Hut at Thorpe Park) to 01932567159. Of interest were the following two entries:

Reliably Transmitting (NAT) to 195.189.173.27:5060:
INVITE sip:01932567159@sip.voipfone.net SIP/2.0
Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bK3700cafe;rport
From: "234" <sip:234@sip.voipfone.net>;tag=as36249d34
To: <sip:01932567159@sip.voipfone.net>
CSeq: 102 INVITE
User-Agent: Asterisk PBX 1.8.10.1~dfsg-1ubuntu1

...

<--- SIP read from UDP:195.189.173.27:5060 --->
SIP/2.0 183 Session Progress
Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bK3700cafe;received=86.157.212.100;rport=5060
From: "234" <sip:234@sip.voipfone.net>;tag=as36249d34
To: <sip:01932567159@sip.voipfone.net>;tag=VFa4bb2530035671ac22635ffaface
CSeq: 102 INVITE
User-Agent: Voipfone Sip Network

At this point I’m hearing the message on the phone to please try again. Now when the phone call ends I get:

<--- SIP read from UDP:195.189.173.27:5060 --->
SIP/2.0 603 No Funds
Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bK3700cafe;received=86.157.212.100;rport=5060
From: "234" <sip:234@sip.voipfone.net>;tag=as36249d34
To: <sip:01932567159@sip.voipfone.net>;tag=VFa4bb2530035671ac22635ffaface
CSeq: 102 INVITE
User-Agent: Voipfone Sip Network

Now I know I have funds. I logged into Voipfone and discovered I had plenty of funds in my account. So what was going wrong?

Well turns out Voipfone wasn’t keen on the user (From: e-mail address) being something other than my Voipfone account number.

I needed to add the following line to my sip.conf file under my peer block:

[voipfone]
type=friend
fromdomain=sip.voipfone.net
fromuser=account_number

and re-loading the SIP configuration:

myuser@myhost:~# asterisk -r
myhost*CLI> sip reload
myhost*CLI>

So my sip.conf now looks like:

[general]
register => 31234567:123456@sip.voipfone.net/voipfone
transport=udp

[voipfone]
type=friend
insecure=invite
dtmfmode=rfc2833
context=fromvoipfone
deny=0.0.0.0/0.0.0.0
permit=195.189.173.27/255.255.255.255
defaultuser=31234567
secret=123456
fromdomain=sip.voipfone.net
fromuser=31234567
host=sip.voipfone.net

Now, when I dial, I see the following sent to Voipfone from Asterisk:

Reliably Transmitting (NAT) to 195.189.173.27:5060:
INVITE sip:01932567159@sip.voipfone.net SIP/2.0
Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bK2a703134;rport
From: "234" <sip:31234567@sip.voipfone.net>;tag=as6b9ff512
To: <sip:01932567159@sip.voipfone.net>;tag=VFa4bb2530035671ac22635ffadefa
CSeq: 102 INVITE

… and I get dialtone.

Asterisk – Setting Up A Simple Home/Office Phone Network

Goal

My goal was to set up a very simple home/office phone network with a single upstream VoIP connection that allowed dialling into the plain old telephone system (POTS) via SIP. I used Voipfone as my provider in the UK and was paying £2.40/month for an ordinary geographical phone number.

The idea was to have several extensions at home which could call each other. Each extension should be able to call the outside world by prefixing the number with the digit 9.

Incoming calls to the ordinary phone number would cause all extensions to ring – with the first one picking up to take the call.

Home PABX Design Goal

Home PABX Design Goal

Setting Up

Linux

On an Ubuntu server I merely issued the command:

apt-get install asterisk

Configuration Files

I deleted the provided sip.conf and extensions.conf files.

Then I set up my trunk/peer (Voipfone SIP provider) along with 3 extensions numbered 201, 202, and 203.

[general]
register => username:password@sip.voipfone.net/voipfone
transport=udp

[voipfone]
type=peer
insecure=invite
dtmfmode=rfc2833
context=fromvoipfone
deny=0.0.0.0/0.0.0.0
permit=195.189.173.27/255.255.255.255
defaultuser=username
secret=password
fromdomain=myhomedomain.net
host=sip.voipfone.net

[201]
type=friend
dtmfmode=rfc2833
context=fromhomeoffice
secret=ext201
host=dynamic
mailbox=201@default

[202]
type=friend
dtmfmode=rfc2833
context=fromhomeoffice
secret=ext202
host=dynamic
mailbox=202@default

[203]
type=friend
dtmfmode=rfc2833
context=fromhomeoffice
secret=ext203
host=dynamic
mailbox=203@default

It is worth noting that the context refers to a block in the dialplan that gets called when a number originates from that extension. Clearly we want phone calls coming from the plain old telephone system (POTS) to be treated differently to calls coming from internal extensions.

The dialplan is stored in a file called extensions.conf.

;----------
; calls from the outside world ring all extensions
;----------
[fromvoipfone]
exten => voipfone,1,NoOp()
exten => voipfone,n,Dial(SIP/201&SIP/202&SIP/203)

;----------
; first check intra-office call, then outbound call
;----------
[fromhomeoffice]
include => internal
include => remote

;----------
; calling POTS by putting a 9 prefix
;----------
[remote]
exten => _9XX.,1,NoOp()
exten => _9XX.,n,Dial(SIP/voipfone/${EXTEN:1})
exten => _9XX.,n,Hangup()

;----------
; internal extensions calling internal extensions
;----------
[internal]
exten => _2XX,1,Dial(SIP/${EXTEN})

Asterisk – Getting 407 Proxy Authentication Required When Dialing Out

Using the PABX software Asterisk v1.8 on Linux I was trying to get calls from my internal network routed out via my paid-for external VoIP account.

I tried debugging by issuing the command sip set debug on but was getting messages like:

Reliably Transmitting (NAT) to 195.189.173.27:5060:
INVITE sip:155@sip.voipfone.net SIP/2.0

followed by

<--- SIP read from UDP:195.189.173.27:5060 --->
SIP/2.0 407 Proxy Authentication Required

at which point the call would fail.

What I needed in my sip.conf file in my peer definition was the following lines:

[general]
register => username:password@sip.voipfone.net/voipfone
transport=udp

[voipfone]
defaultuser=username
secret=password
type=peer
dtmfmode=rfc2833
context=fromvoipfone
deny=0.0.0.0/0.0.0.0
permit=195.189.173.27/255.255.255.255
fromuser=username
fromdomain=sip.voipfone.net
host=sip.voipfone.net

so that Asterisk could reply to this request for authentication:

Reliably Transmitting (NAT) to 195.189.173.27:5060:
INVITE sip:152@sip.voipfone.net SIP/2.0
Proxy-Authorization: Digest username='testuser', realm='asterisk', algorithm=MD5, uri='sip:152@sip.voipfone.net', nonce='42db9e63', response='ded720cda12519e7bde82e11ea21798d'