Trunk

This is my sample dialplan for handling incoming calls to and from my telephony Carrier (sometimes an Internet connection through my ISP, sometimes an ethernet cable from the phone carrier.)

We handle the context= from the SIP configuration to make some settings.

Inbound Calls

The dialplan context used for managing calls from our trunk provider is specified with the context= line in sip.conf.

File extract: /etc/asterisk/sip.conf

[trunk]
...
context=from-trunk
...

The most basic dialplan for managing incoming calls, is to recieve the call on the dialled extension, and send it to the local device. Such as:

File extract: /etc/asterisk/extensions.conf

1 [from-trunk]
2 exten => _0212345678,1,Dial(SIP/5001,60) ; Dial for 60 seconds
3  same => n,Hangup()
4 
5 exten => s,1,Dial(SIP/5501,60)
6  same => n,Hangup()

The above is nice and even scales well to multiple numbers, and multiple range of numbers.

For us, we started having problems with our inbound numbers so it became useful to do some clean up work on the incoming numbers so we adopted a two step process for incoming calls:

  • receipt pattern with from-trunk
  • routing patterns with route-internal (trunk to internal)

The basic form would be:

1 [from-trunk]
2 exten => _X!,1,Goto(route-internal,${EXTEN},1)

To ensure we only deal with the Full National Number (and avoid problems with any extra information from our carrier) we use basic variable manipulation [2] to send to route-internal only the 1st 10 numbers. (Full National Numbers in Australia are 10 digits.)

1 [from-trunk]
2 exten => _X!,1,Goto(route-internal,${EXTEN:0:10},1)

CallerID

Many carriers support passing the caller id with a telephone call and there is no additional step required in Asterisk. When we transfer a call (DIAL) to a local extension, it should display the caller id.

For some PBX scenarios, a prefix must be dialled before an outside line can be called. For these environments, we add explicit setting of the caller id.

For example: If your phone system requires dialling "9" before you can dial an external phone number, then we update the inbound callerid to include the prefix number "9":

4 Set(CALLERID(num)=9${CALLERID(num)})

We dress things up a bit and it looks like this now:

1 [from-trunk]
2 exten => _X!,1,Verbose(2,TO ${EXTEN} FROM ${CALLERID(num)})
3  same => n,GotoIf($["${CALLERID(num)}" = "anonymous" | "${CALLERID(num)}" = "unknown"]?anon)
4  same => n(setcid),Set(CALLERID(num)=9${CALLERID(num)})
5  same => n(anon),Goto(route-internal,${EXTEN:0:10},1)

We use GotoIf so that we don't put a prefix if the caller id says "anonymous."

Call Routing

Using the above logic for the [from-trunk] context we create a routing schedule for inbound numbers.

Below is a simple segment, dealing with only 2 inbound numbers. Using This context we can develop more elaborate catches for inbound calls/routing.

In all cases we just jump to a subroutine that presents the internal calls.

 7 [route-internal]
 8  
 9  exten => _0212345670,1,Dial(SIP/5500,60)
10  same => n,Hangup()
11  
12  exten => _0212345671,1,Dial(SIP/5501,60)
13  same => n,Hangup()

Of course, we use a Subroutine to actually DIAL the extension.

 7 [route-internal]
 8  
 9  exten => _0212345670,1,Gosub(stdexten,5500,1(,${GLOBAL(CONSOLE)}))
10  same => n,Hangup()
11  
12  exten => _0212345675,1,Gosub(stdexten,5005,1(,${GLOBAL(CONSOLE)}))
13  same => n,Hangup()
14  
15  exten => s,1,Gosub(stdexten,5500,1(,${GLOBAL(CONSOLE)}))
16  same => n,Hangup()

Outbound Calls

Making an outgoing call is similar to other calls handled by Asterisk.

exten => _0212345678.,1,DIAL(SIP/${EXTEN}@trunk,60) ; Ring for 60 seconds maximum

The above dialplan (if reached) will Dial the extension 12345678 through the trunk SIP configuration.

To expand to any number to any recieved by the dialplan.

exten => _X.,1,DIAL(SIP/${EXTEN}@trunk,60) ; Ring for 60 seconds maximum

We wrap the Dial command around some error checking.

[dial-trunk]
 exten => _X.,1,Dial(SIP/${EXTEN}@trunk,60)  ; Ring the interface 60 seconds maximum
  same => n,Gosub(trunk-${DIALSTATUS},1)
  same => n,Hangup()

Caller ID

[ Ref: Wiki, Asterisk Docs, Setting CallerID in Asterisk, Caller ID in SIP and Asterisk ]

Depending on your situation, you may or may not want to set CallerID on our Outbound calls.

If your Voice Carrier supports the P-Asserted-Identity and Privacy Headers (i.e. Calling Party and Network Identification Definition.) You can insert these values into Asterisk's SIP headers using: SIPAddHeader.

You can set the Caller ID Values such as the below.

1 [dial-trunk]
2 exten => _X.,1,Set(CALLERID(num)=021234${EXTEN})
3  same => n,SIPAddHeader(P-Asserted-Identity: <sip:+6121234${EXTEN}@example.com>) ; My E164 Number
4   same => n,Dial(SIP/${EXTEN}@trunk,60)  ; Ring the interface 60 seconds maximum
5   same => n,Gosub(trunk-${DIALSTATUS},1)
6   same => n,Hangup()

You will need to ensure the Channel Configuration (i.e. SIP) is enabled for sending P-Asserted-Id information with the following configuration:

File extract: /etc/asterisk/sip.conf

[trunk]
...
sendrpid=pai
trustrpid=yes

As always, refer to your vendor documentation for the features and settings required for Caller ID.

Enable

[Ref: RFC 5379, RFC 3323]

For some providers, doing the above is sufficent to enable Caller ID. For some others, you need to specify the Privacy setting, such as below:

3  same => n,SIPAddHeader(Privacy:none)

And our dial-trunk will look like this.

1 [dial-trunk]
2 exten => _X.,1,Set(CALLERID(num)=021234${EXTEN})
3  same => n,SIPAddHeader(P-Asserted-Identity: <sip:+6121234${EXTEN}@example.com>) ; My E164 Number
4  same => n,SIPAddHeader(Privacy:none)
5  same => n,Dial(SIP/${EXTEN}@trunk,60)  ; Ring the interface 60 seconds maximum
6  same => n,Gosub(trunk-${DIALSTATUS},1)
7  same => n,Hangup()
Disable

To disable caller id, specify the desired Privacy setting.

3   same => n,SIPAddHeader(Privacy:id)

And our dial-trunk will look like this.

1 [dial-trunk]
2 exten => _X.,1,Set(CALLERID(num)=021234${EXTEN})
3  same => n,SIPAddHeader(P-Asserted-Identity: <sip:+6121234${EXTEN}@example.com>) ; My E164 Number
4  same => n,SIPAddHeader(Privacy:id)
5  same => n,Dial(SIP/${EXTEN}@trunk,60)  ; Ring the interface 60 seconds maximum
6  same => n,Gosub(trunk-${DIALSTATUS},1)
7  same => n,Hangup()

Multiple Trunks

[Ref: Asterisk Expressions | More Dialplan Concepts]

There are several ways to deal with using Multiple Trunks. Below is one simple solution for using all of you available trunks when tryp to make outbound calls.

  • Try Trunk1 first
  • Try Trunk2 if Trunk1 fails
1 [trunk_dial]
2 exten => _X.,1,Set(EXTENSION=${EXTEN:1})
3  same => n,NoOP(From ${CALLERID(num)} to ${EXTENSION})
4  same => n,SIPAddHeader(Privacy:id)
5  same => n,Dial(SIP/${EXTENSION}@trunk1,30)  ; Ring the interface 30 seconds maximum
6  same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" | "${DIALSTATUS}" = "CONGESTION"]?:end)
7  same => n,Dial(SIP/${EXTENSION}@trunk2,30)  ; Ring the interface 30 seconds maximum
8  same => n(end),Gosub(trunk-${DIALSTATUS},1)
9  same => n,Hangup()

Remember: Line 1 strips the leading character ("9" in this scenario) we used in the earlier example, which is the dial-prefix on this PBX for making outbound (trunk) calls. Then we use ${EXTENSION} to simplify manipulation of the outbound telephone number.

We are classifying TRUNK errors as when the DIALSTATUS is "CHANUNAVAIL" or when it is "CONGESTION". Other errors are not being classified as errors with the TRUNK.

For example: if the error is "BUSY" that indicates the destination device/phone is busy (the trunk is OK)

GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" | "${DIALSTATUS}" = "CONGESTION"]?:end)
  • If the returned DIALSTATUS is "CHANUNAVAIL" or "CONGESTION"
    • Goto the next item (i.e. there is nothing between "?" and ":")
  • ELSE (e.g. "busy", "cancel", etc)
    • Goto "end"

Caller ID for Teams

Some deployments have a preference/requirement that certain teams use a published telephone number as their Caller ID (i.e. All outbound calls for the team is from this published telephone number.)

The simplest method for achieving this goal is assign a range of numbers to the team. We can then use a similar GotoIF(expression?trueLabel:falseLabel) as in the below:

GotoIf($[${CALLERID(num)}>=5001 & ${CALLERID(num)}<=5020]?TEAM1:continue)

The expression to be evaluated:

  • If the CallerID is between 5001 and 5020
    • Goto the label: "TEAM1"
  • Else:
    • Goto the label: "continue"
 1 [trunk_dial]
 2 exten => _X.,1,Set(EXTENSION=${EXTEN:1})
 3  same => n,NoOP(From ${CALLERID(num)} to ${EXTENSION})
 4  same => n(isTEAM1),GotoIf($[${CALLERID(num)}>=5001 & ${CALLERID(num)}<=5020]?TEAM1:continue)
 5  same => n(TEAM1),NoOp(TEAM1)
 6  same => n,Set(CALLERID(num)=XXXXXXXX)
 7  same => n,Set(CALLERID(all)="TEAM1" <${CALLERID(num)>)
 8  same => n,SIPAddHeader(P-Asserted-Identity: <sip:${CALLERID(num)}@example.com>)
 9  same => n,SIPAddHeader(Privacy:off)
10  same => n,Dial(SIP/${EXTENSION}@trunk1,30)  ; Ring the interface 30 seconds maximum
11  same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" | "${DIALSTATUS}" = "CONGESTION"]?:end)
12  same => n,Set(CALLERID(num)=YYYYYYYYY)
13  same => n,Set(CALLERID(all)="TEAM1" <${CALLERID(num)>)
14  same => n,SIPRemoveHeader(P-Asserted-Identity:)
15  same => n,SIPAddHeader(P-Asserted-Identity: <sip:${CALLERID(num)}@example.com>)
16  same => n,Dial(SIP/${EXTENSION}@trunk2,30)  ; Ring the interface 30 seconds maximum
17  same => n,Goto(end)
18  same => n(continue),NoOp(NOT TEAM1)
19  ...
20  ...
21  same => n(end),Gosub(trunk-${DIALSTATUS},1)
22  same => n,Hangup()

As in the above instructions on setting the caller-id, use designate a number XXXXXXXX approved for the Trunk1 by your carrier.

5  same => n(TEAM1),NoOp(TEAM1)
6  same => n,Set(CALLERID(num)=XXXXXXXX)
7  same => n,Set(CALLERID(all)="TEAM1" <${CALLERID(num)>)
8  same => n,SIPAddHeader(P-Asserted-Identity: <sip:${CALLERID(num)}@example.com>)
9  same => n,SIPAddHeader(Privacy:off)

Depending on what is accepted by the carrier, you will need the SIPAddHeader(Privacy:off) to enable caller id presentation.

With the caller-id configured, we DIAL the ${EXTENSION}

10  same => n,Dial(SIP/${EXTENSION}@trunk1,30)  ; Ring the interface 30 seconds maximum

As, this example, we have multiple trunks, we decide if the Trunk is problematic, to continue and try the next trunk.

11  same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" | "${DIALSTATUS}" = "CONGESTION"]?:end)

If the DIALSTATUS tells us a Trunk fail (i.e. CHANUNAVAIL or CONGESTION) then we want to try the next Trunk.

  1. set the new Caller ID
  2. reset the SIP Header: P-Asserted-Identity
12  same => n,Set(CALLERID(num)=YYYYYYYYY)
13  same => n,Set(CALLERID(all)="TEAM1" <${CALLERID(num)>)
14  same => n,SIPRemoveHeader(P-Asserted-Identity:)
15  same => n,SIPAddHeader(P-Asserted-Identity: <sip:${CALLERID(num)}@example.com>)
16  same => n,Dial(SIP/${EXTENSION}@trunk2,30)  ; Ring the interface 30 seconds maximum

Set the CallerID

Set(CALLERID(num)=YYYYYYYYY)

Where YYYYYYYYY is a valid number accepted by the Trunk2 carrier.

Because the Caller ID is changed, we need to change the SIP Header "P-Asserted-Identity". To make the change, we first have to remove any previous header (SIPRemoveHeader) and then re-add the Header (SIPAddHeader)

14  same => n,SIPRemoveHeader(P-Asserted-Identity:)
15  same => n,SIPAddHeader(P-Asserted-Identity: <sip:${CALLERID(num)}@example.com>)

A side-effect of how SIPAddHeader behaves, is that you have to explicitly remove previous SIPHeaders, or duplicates of the same SIPHeader will either cause a rejection or have unknown side-effects.

When we are at the last Trunk to call, then just go to the n(end)

17  same => n,Goto(end)

The last action is to Go to the trunk DIALSTATUS management.

23  same => n(end),Gosub(trunk-${DIALSTATUS},1)
24  same => n,Hangup()

The last part of the Dialplan, is what to do with calls that do not require Caller ID n(continue)

18  same => n(continue),NoOp(NOT TEAM1)
19  same => n,SIPAddHeader(Privacy:id)
20  same => n,Dial(SIP/${EXTENSION}@trunk1,30)  ; Ring the interface 30 seconds maximum
21  same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" | "${DIALSTATUS}" = "CONGESTION"]?:end)
22  same => n,Dial(SIP/${EXTENSION}@trunk2,30)  ; Ring the interface 30 seconds maximum
23  same => n(end),Gosub(trunk-${DIALSTATUS},1)
24  same => n,Hangup()

For this scenario, we:

  1. disable CallerID SIPADDHEADER(Privacy:id)
  2. Dial through Trunk1
  3. Dial through Trunk2 (if Trunk1 fails)
  4. Go to DIALSTATUS management.

DIALSTATUS Management

When the calls have completed (success of failure) we action the line:

Gosub(trunk-${DIALSTATUS},1)

The DIALSTATUS management is handled in the below dialplan.

26 exten => trunk-CANCEL,1,Return
27 
28 exten => trunk-NOANSWER,1,Playback(the-number-u-dialed)
29  same => n,Playback(is-curntly-unavail)
30  same => n,Playback(please-hang-up-and-try-again)
31  same => n,Return
32 
33 exten => trunk-BUSY,1,Busy
34  same => n,Playback(is-curntly-busy)
35  same => n,Return
36 
37 exten => trunk-CHANUNAVAIL,1,Playback(all-outgoing-lines-unavailable)
38  same => n,Playback(please-hang-up-and-try-again)
39  same => n,Return
40 
41 exten => trunk-CONGESTION,1,Playback(all-outgoing-lines-unavailable)
42  same => n,Playback(please-hang-up-and-try-again)
43  same => n,Return
44 
45 exten => trunk-.,1,Verbose(1,Unknown Error)
46  same => n,Return