Asterisk configuration
Peter Dordal, Loyola University CS Dept
Let's start with definitions for channels,
SIP channels in particular. These are the actual paths that connections come
in and go out over. Each analog
phone line (FSX/FSO interface) represents a channel. A T1 line is a set of
24 voice (DS0) channels. Networks tend to allow better multiplexing.
In Asterisk, SIP channels are defined in the file sip.conf.
Channels over the Internet are effectively synonymous with call endpoints.
A connected phone call is the connection of two channels (ignoring, for the
moment, conference calls, which actually work slightly differently).
First, here's a sample config file for the cisco phone
[cisphone001]
deny=0.0.0.0/0.0.0.0
permit=147.126.0.0/255.255.0.0 ;; what IP addresses we will accept
secret=MyP@ssW3rd ;; the SIP-authentication cleartext password
dtmfmode=rfc2833 ;; signaling method for touchtone buttons
canreinvite=yes ;; issue in SIP protocol
context=default ;; what context in extensions.conf we go to for incoming calls
host=dynamic ;; phone gets different IP addr depending on where it is plugged in
;; consequence: phone will contact Asterisk
type=friend ;; match incoming calls on both IP addr & SIP header info
nat=force_rport,comedia ;; can we be behind a firewall? (formerly "nat=yes")
port=5060 ;; default SIP port
callerid=device <312-915-7980>
The nat=force_rport,comedia
setting is for phones behind a NAT router, and influences the way SIP
handles connections. The "force_rport" causes the Asterisk server to use the
"rport" mechanism of RFC 3581:
the server will send replies back to the port from which they came, rather
than attempting to use the port number embedded in the data. The comedia
request enables "symmetric RTP".
For the flowroute account,
there is a slightly different set of parameters:
[flowroute] ;; just a name
type=friend ;; same as above
secret=0th3rP@ssw3rd
username=12345678 ;; used by flowroute accounting
host=sip.flowroute.com ;; Asterisk can be told a FIXED IP addr for the other end
;; consequence: Asterisk will contact flowroute
dtmfmode=rfc2833 ;; same
context=inbound ;; same as above
canreinvite=no ;; issue in sip protocol
allow=ulaw ;; allowed encoding (in particular, we don't alow g729)
insecure=port,invite ;; rules for incoming calls from elsewhere
fromdomain=sip.flowroute.com ;; must put this value in SIP From: header field
We have just created two named channels,
cisphone001 and flowroute. The flowroute channel may have multiple inbound
and outbound calls; the cisphone001 channel represents a single physical
device but we still might have several outbound lines
in use, and several inbound call attempts.
(Later we will see how to tell the flowroute channel what number we want to
dial.)
When the actual cisphone001 registers
itself, it will provide that name string, "cisphone001". This is how
Asterisk learns that the channel [cisphone001] belongs to the IP address of
that particular device.
For the sake of future examples, assume we've also defined cisphone002:
[cisphone002]
;; defined like cisphone001
Remember: Asterisk knows how to reach each of our three channels.
Finally, to reach these channels we have to prepend "SIP/" to the channel
name defined above.
Now for the extensions.conf file,
known as the dialplan file because
it has all the rules for how to repond to dialing. First we'll define how to
dial extension 3030, eg
[default]
exten => 3030,1,Dial(SIP/cisphone001,
15) ;; 15 = timeout in seconds
This is all we need to answer calls dialed to number 3030:
- Asterisk will match the 3030 in extensions.conf, and find the
reference to cisphone001
- This corresponds to a channel name defined in sip.conf
- That channel name in turn has been linked to a specific IP phone at
the time when that phone registered itself and gave the name.
(The Dial() function also takes a third parameter, which allows for options
such as keeping the connection if the other party hangs up (eg to dial
another number), or to play music while you wait. See http://www.voip-info.org/wiki/view/Asterisk+cmd+Dial.)
Here's a diagram from Asterisk: The Definitive Guide that shows the flow.
Phones here are given channel names corresponding to their Ethernet MAC
addresses. The important thing to note here is how a call placed by
the upper phone to the lower phone
(extension 101) is routed between the respective channels.
In my current configuration, I use the manufacturer name, last three bytes
of the MAC address, and the line number, eg cisco52ea4bline2.
The "line1" is included because each phone has multiple lines that can be
independently configured.
Dialplans
In Asterisk, a "dialplan" is the set of definitions of all the "extensions".
An extension is, abstractly, a way of reaching a number or class of numbers.
Specifically, an extension may represent
- A number corresponding to an attached phone (digital or analog)
- dialing one of our own extensions from another extension
- dialing one of our own extensions from the outside world
- A pattern corresponding to a
way to dial outbound numbers
- A "virtual" extension that you can call for information, or to
interact with via the phone keys
- A virtual extension that serves in effect as a "subroutine" for other
processing
The simplest extension in the first category is something like the
following:
exten =>
3030,1,Dial(SIP/cisphone001, 15)
;; 15 = timeout in seconds
If extension 3030 is dialed, Asterisk attempts to connect to the given channel cisphone001, using SIP. That
channel presumably corresponds to a physical phone.
If we have a number (a "direct inward dial", or DID) 312-123-1234 from our
VoIP provider, and we want to connect that to extension 3030 in context locals, we can use
exten =>
13121231234,1,Goto(local,3030,1)
This means that if someone in the outside world dials 1-312-123-1234, then
our provider routes the call to our Asterisk server, and our server, by dint
of the line above, routes the call to extension 3030. We could have set up
the connection from 1-312-123-1234 to the phone directly, without using the
extension:
exten => 13121231234,1,Dial(SIP/cisphone001,15)
This is inconvenient, though, as it makes later changes more difficult. It
also makes it impossible for someone to dial "3030" internally to reach the
phone.
At many sites, the extension is the last four (or five, as at Loyola) digits
of the phone number. So the four-digit extension above would be, not 3030,
but 1234.
It is best if incoming calls from the outside world start in a different
context from local extension definitions.
A simple example in the second, pattern, category might be
exten =>
_1NXXXXXXXXX,1,Dial(SIP/${EXTEN}@flowroute)
The "_" sign indicates that this is a pattern,
not literal bytes. N matches any digit 2-9; X matches any digit 0-9. The
pattern here has an N and nine X's, to match a 10-digit phone number (I've
added underlining to make it easier to count the X's). The
literal 1 at the beginning (not a "pattern") represents the leading 1 used
by NANP (North American Numbering Plan) dialing conventions.
Because we're matching a pattern, not a specific number, we need to tell the
Dial command exactly what number we want. That's where the magic variable
${EXTEN} comes in; it contains the actual number dialed that matched the
pattern here. The Dial() argument consists of the channel name (flowroute,
from the sip.conf file), prepended by SIP and the actual number we want
(${EXTEN}), with the required punctuation.
Dialing the extension here still results in a single channel, which is
connected by Asterisk to the dialer's channel.
A simple example in the third category might be
exten =>
2000,1,Playback(hello-world)
In Asterisk's default sounds directory is a file hello-world.gsm, which is
played. If you have, say, hello-world.gsm and hello-world.wav, Asterisk
picks whichever is "simpler" (ie requires less transcoding).
The above three examples were all single lines. However, most real examples
of extension scripts are multiple lines. The "1" in the second field of the
above extensions is a sequence number (line number). Usually for multi-line
extension scripts we use "n" for subsequent lines.
Be aware that all Asterisk extension scripts belong to one specific context,
and extensions in different contexts cannot reach one another unless you
take steps to allow that (by an explicit context-shifting command, for
example, or by including one
context in another). A context can be thought of as a namespace,
in that the scope of an extension name is its context and two different
contexts can define the same extensions. Contexts can be used to implement
different privilege levels for extensions; if your phone is in context
[lusers] and the outside lines are in context [outbound], you cannot reach
them unless things are explicitly configured to enable that. A new context
starts with a name in square brackets:
[localCallers]
Any subsequent extension scripts belong to that new context until the
context changes again.
There are a few "special" extensions, built in to Asterisk (from http://www.voip-info.org/wiki/view/Asterisk+standard+extensions):
- a: Called when '*' is pressed during
a voicemail greeting (but not
at other times, and not part of patterns)
- h: Hangup extension (allowing you to do something
after a hangup)
- i: invalid-number extension (much safer than the
match-everything pattern "_.", or even "_X"
- o: Operator extension, used for operator exit by
pressing zero in voicemail
- s: Start extension, used where we don't know the
actual extension but a match-everything pattern would be inappropriate.
Examples include macros, where
we don't know the actual extension, Gotos from other extensions (ditto),
and analog lines, where no called digits are transmitted by the telco.
- t: Timeout extension
- T: AbsoluteTimeout() extension
- failed: used if an auto-dial out call fails
- fax: used for fax detection on Zap channels
- talk: used in conjunction with BackgroundDetect
Here is a multi-line script for an extension that greets the user, tells
them their caller-ID number, and then says goodbye. The CALLERID(num)
function returns the number; including it in ${...} allows use of that value
as a string by the caller. The Hangup at the end is for clarity; Asterisk
would hang up by default if there were nothing left to do. This script would
work without the Answer() as well.
exten => 3001,1,Answer()
exten =>
3001,n,Playback(dir-welcome)
;;
"welcome to the directory"
exten => 3001,n,SayDigits(${CALLERID(num)})
;; num = numeric form of CallerID data
exten => 3001,n,Playback(vm-goodbye)
exten => 3001,n,Hangup
The second field of the first line is a 1, representing script entry number
1. The subsequent lines could have 2-5, but instead have "n" for next, which
makes later insertion of additional lines easier. We could also have done
the later lines as
same => n,Playback(dir-welcome)
This avoids having to re-type the extension number.
The expression CALLERID(num) represents a string containing the caller-id
value supplied by the caller; like all strings it must be enclosed in ${...}
to be used in a script. There is also CALLERID(name), but that's less useful
in scripts.
For any string STR, we can take the substring of
length n starting at position m with ${STR:m:n}; the first character of STR
is at position 0. Here is a simple example using variables and substrings:
exten => 2011,1,Answer()
same => n,Set(NUM=123456789)
same => n,Set(SNUM=${NUM:4:3})
same => n,SayDigits(${SNUM})
This reads back the 3 digits starting at position 4 (counting from 0): "5 6
7". Note that to use a variable you need both the $ and the {} around the
variable name.
If we leave off n, we get the substring from position m on to the end. If we
write -m instead, then the position counting starts from the end of the
string, towards the front, so that ${STR:-4} is the last four characters of
STR. If STR is "123456789", then ${STR:-4:3} is "678". If we want the last
four digits of the CALLERID(num) string, the simplest approach is to use
${CALLERID(num):-4}.
To evaluate an arithmetic expression or function call, enclose it in $[ ...
].
Here is a very simple way to allow calls to roll over into voicemail. If the
Dial line completes and the call is connected, then it ends with Hangup and
we don't continue to Voicemail. Otherwise we do. The Voicemail function
takes a first parameter of the form "mailbox@context", where "mailbox" is
usually the user's extension (eg 3001 here), and "context" is the section
where the mailbox/extension is defined (for mailboxes, usually "default").
Voicemail settings would have to be made to create the appropriate mailbox
here.
exten => 3041,1,Dial(SIP/cisco5534e4line1,
10) ;; quit after 10
seconds
exten => 3041,n,Voicemail(3041@default)
Here is a multi-line script allowing someone to dial extension 3001 and go
to voicemail as necessary (though, as noted, most of the lines are actually
not continuations). This version supports separate "busy" and "unavailable"
recorded greetings. We test the status of the Dial command and then use the
${DIALSTATUS} variable to continue to voicemail as appropriate. Lines 3-9
are actually different "virtual" extensions corresponding to different
values of ${DIALSTATUS}, so they have 1's.
VoicemailMain is the application for receiving
your voicemail.
exten => 3001,1,Dial(SIP/chanFor3001,15)
;; dial for up to 15 seconds; sets
DIALSTATUS variable
exten => 3001,n,Goto(${DIALSTATUS},1)
;; here we use
DIALSTATUS
exten => ANSWER,1,Hangup
;; call was answered (and is
now completed!)
exten => CANCEL,1,Hangup
;;
call was canceled by user
exten => NOANSWER,1,Voicemail(3001@default) ;; no answer
after 15 seconds
exten =>
BUSY,1,Voicemail(3001@default,b)
;;
b = use separate "busy" message, if one exists
exten => CONGESTION,1,Voicemail(3001@default,b) ;; b = use separate
"busy" message, if one exists
exten => CHANUNAVAIL,1,Voicemail(3001@default,u) ;; u = use
"unavailable" message, which is the default actually
exten => a,1,VoicemailMain(3001@default)
;; The "a" means the user pressed "*"
during the greeting
(One drawback with this version, which is
in popular use, is that if we don't list some value of ${DIALSTATUS} then
when that case arises we will not go to voicemail. A better solution might
be to test for the "busy" cases (arguably only BUSY; CONGESTION means
something quite different socially), and play the unavailable message as an
"else" option.
Now let's try playing with extension scripts that interact with users. We'll
put the script in its own context, so that we can define 1-digit extensions
(corresponding to single keypresses) that do not interfere with anything
else. The first parameter to Goto is the new context:
exten => 2003,1,Goto(menudemo,start,1)
Now here is context [menudemo]. The Background() function is like Playback()
except that it can be interrupted by keypresses. The user is prompted to
enter a 1 or a 2; if this is done, the system reads back the number and
returns to the main prompt. Any other number except 6 exits the system; if
the user presses 6, he or she is prompted to enter a multi-digit number
ending with #.
[menudemo]
exten => start,1,Answer
same => n,Playback(hello-world)
;; Allison again
same => n,Background(oneortwo)
; oneortwo = "please press a one or a two"
same => n,WaitExten(10)
same => n,Playback(goodbye)
exten => 1,1,Playback(digits/1)
;; user entered a 1
same => n,Goto(start,3)
exten => 2,1,Playback(digits/2)
;; user entered a 2
same => n,Goto(start,3)
exten => 6,1,Playback(enternumber)
;; user entered a 6; play "Enter a number, ending with pound"
same => n,Read(theNum)
;; reads
digits until #
same => n,SayDigits(${theNum})
same => n,Goto(start,3)
exten => _X,1,Playback(digits/${EXTEN}) ;; catchall
extension matches any single digit
same => n,Playback(wrong)
Here is the final example: a script that allows users to set a "magic"
number in the database. Each "account" has its own "magic" number. There is
no authentication here (how would you add that)?.
As before, we have a Goto to a special context from the [default] context:
exten =>
2004,1,Goto(dbdemo,start,1)
Now here is the actual [dbdemo] context. Within this context, any
single-digit extension applies only to this context. The dialplan functions
include
- Answer: effectively a no-op; all extensions are Answered anyway
- Background(soundfilename): play the sound, but allow keypresses during
playback
- Playback(soundfilename): like Background, but keypresses are an error
- SayNumber: read a number, eg "one hundred twenty three"
- SayDigits: read digits, eg "one two three"
- WaitExten(10): wait for 10 seconds for an extension (keypress);
applies only to menu keypresses
- Read(varName): read a string of keypress digits, ending in #, and
store in the named variable
- Noop(string): for diagnostics to the Asterisk console
- Value of a variable: ${theAccount}
- To retrieve the value stored under theAccount from the database:
- ${DB(dbdemo/${theAccount})}
- To save a value into the above database key:
- Set(DB(dbdemo/${theAccount})=${theValue})
- Gotoif(bool:tag1:tag2): evaluates boolean expression; goes to tag1 if
true, tag2 if false.
[dbdemo]
;; this version says the numbers as strings of digits: "one four nine"
exten => start,1,Answer
same => n,Background(yourkeyis)
same => n,SayDigits(${theAccount})
same => n,Noop(The_account_is${theAccount})
same => n,Background(onetwothreemagic) ;; 1 = set account; 2 = get magic, 3 = set magic
same => n,WaitExten(10)
same => n,Playback(goodbye)
exten => 1,1,Playback(digits/1) ;; set account
same => n,Background(enterkey) ;; "enter your key, ending with #"
same => n,Read(theAccount)
same => n,Noop(The_account_is${theAccount})
same => n,Noop(The_value_is:${DB(MagicDB/${theAccount})}:)
same => n,Background(yourkeyis) ;; "your key is"
same => n,SayDigits(${theAccount})
same => n,GotoIf($[${LEN(${DB(MagicDB/${theAccount})})}=0]?end:speak) ;; empty string: LEN=0
same => n(speak),Background(yourmagicis) ;; "your magic number is"
same => n,SayDigits(${DB(MagicDB/${theAccount})})
same => n(end),Goto(start,4)
exten => 2,1,Playback(digits/2) ;; get magic
same => n,Background(yourmagicis) ;; "your magic number is"
same => n,SayDigits(${DB(MagicDB/${theAccount})})
; same => n,Read(theValue)
; same => n,Set(DB(MagicDB/${theAccount})=${theValue})
same => n,Goto(start,1)
exten => 3,1,Playback(digits/3) ;; set magic
same => n,Background(entermagic) ;; "enter a magic number, ending with #"
same => n,Read(theMagic)
same => n,Set(DB(MagicDB/${theAccount})=${theMagic})
same => n,Goto(start,1)
exten => _X,1,Playback(digits/${EXTEN}) ;; match any digit
same => n,Playback(wrong)
You can examine all the stored database keys with the Asterisk command
database show
or, to look only at entries in the dbdemo subtree,
database show dbdemo
Some possible improvements:
- Require that the user enter an account; make the default value of
theAccount be the empty string
- Do not allow options 2 and 3 if the account is not set
- Have option 2 say "you have no magic" if no value is set for the magic
number
- Add authentication: have the user enter the key and also a password
to get started. Options after that point would include resetting the
password and getting/setting magic.