Asterisk configuration
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 endpoints.
First, here's the 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=yes ;; can we be behind a firewall?
port=5060 ;; default SIP port
callerid=device <312-915-7980>
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 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 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)
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.
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.
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
exten => 3001,n,Goto(${DIALSTATUS},1)
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
betthttp://www.voip-info.org/wiki/view/Asterisk+cmd+Dialer 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)
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.