This page was created by nicotine to keep notes on his evaluation of linking a main office running FreePBX with a branch office running FreePBX by utilizing DUNDi in the hopes that some of it may be useful for a future FreepbxCluster module or DUNDiModule.
The layout
Mainoffice location, hostname mainoffice.company.com, has 15 devices and 20 users. Houses the company T1 PRI, and 3 POTS lines on NPA 515. All incoming calls to the POTS lines are re-routed by the telco to the T1 PRI. Mainoffice also holds the main IVR, all Agent Queues, and a few shared voicemail boxes, along with the company parking lot.
Branchoffice location, hostname branchoffice.company.com, has 10 devices and 12 users. Houses 3 POTS lines on NPA 315. All incoming calls to POTS lines are re-routed by telco to the Mainoffice T1 PRI. User voicemail stored locally.
Connection between the two machines are company.com owned leased lines. Sufficient bandwidth is available for unlimited IAX calls between the two.
Company.com requirements: Seamless dialplan between both offices.
Initial setup
Followed Freepbx setup guide to a T, with the following exception: Patch from #1807 applied to Freepbx source to allow "general" vm context for IVR directory
After initial install, started hacking to meet objectives:
Objective 1: Call extension on Branchoffice from Mainoffice, vice versa
Solution 1: DUNDi. Read on!
In order to simplify DUNDi configuration, decision was made to block out extension ranges based on function for *.company.com:
100-199 = Extensions, Mainoffice
200-299 = Extensions, Branchoffice
600-699 = Services, Mainoffice (Currently, Queues)
700-799 = Services, Branchoffice (Currently, none)
In the (misguided?) interests of security, instead of a single DUNDi cloud, seperate DUNDi clouds were setup between the machines for the following:
extensions
services
parking
pstn-connection (not setup, still waiting on PSTN delivery)
Started with http://blog.thegoldfish.net/dundi-tutorial-for-asteriskhome/ - then started modifying the heck out of it. Unless mentioned below, the guide at the above link was followed. Disliked specifying custom trunks to try dundi, as one would have to allow access to trunk contexts in order to get dundi to work. Decided to go with carefully placed custom contexts.
dundi.conf on mainoffice.company.com:
[general]
; Define the max depth in which to search the DUNDi system (also max # of
; seconds to wait for a reply)
;
ttl=32
; If we don.t get ACK to our DPREQUEST within 2000ms, and autokill is set
; to yes, then we cancel the whole thing (that.s enough time for one
; retransmission only). This is used to keep things from stalling for a long
; time for a host that is not available, but would be ill advised for bad
; connections. In addition to .yes. or .no. you can also specify a number
; of milliseconds. See .qualify. for individual peers to turn on for just
; a specific peer.
;
autokill=yes
; If the cache time is too large, a route may change and be unreachable
; until the cache expires. By contrast, if you have a large network, it is
; extremely inefficient to be sending every single search out to the network
; simply because one.s cache expires after a few seconds. Try to adjust
; this based upon the size of your network. By default this is set to
; 3600 seconds (one hour). This will work fine for a fairly static setup,
; but should be lowered for a more dynamic environment.
cachetime=3600
[mappings]
services => dundi-services,0,IAX,services:${SECRET}@10.10.10.58/${NUMBER},nopartial
extensions => dundi-extensions,0,IAX,extensions:${SECRET}@10.10.10.58/${NUMBER},nopartial
parking => dundi-parking,0,IAX,parking:${SECRET}@10.10.10.58/${NUMBER},nopartial
[00:13:00:00:00:01] ;branchoffice.company.com
model = symmetric
host = branchoffice.company.com
inkey = branchoffice.company.com
outkey = mainoffice.company.com
include = all
permit = all
qualify = yes
order = primary
mainoffice iax_custom.conf:
[services] type=user dbsecret=dundi/secret context=dundi-services-local [extensions] type=user dbsecret=dundi/secret context=dundi-extensions-local [parking] type=user dbsecret=dundi/secret context=dundi-parking-local
Mainoffice extensions_custom.conf:
[ext-local-custom]
include => trydundi-extensions
[from-did-direct-ivr-custom]
include => trydundi-extensions
[dundi-services]
include => ext-queues
[dundi-services-local]
include => dundi-services
[dundi-services-switch]
switch => DUNDi/services
[dundi-services-lookup]
;include => dundi-services-local
include => dundi-services-switch
[macro-dundi-services]
exten => s,1,Goto(${ARG1},1)
include => dundi-services-lookup
[trydundi-services]
; Currently no remote services
[dundi-extensions]
include => ext-local
[dundi-extensions-local]
include => dundi-extensions
[dundi-extensions-switch]
switch => DUNDi/extensions
[dundi-extensions-lookup]
;Don't go to extensions, otherwise we loop
;include => dundi-extensions
include => dundi-extensions-switch
[macro-dundi-extensions]
exten => s,1,Goto(${ARG1},1)
include => dundi-extensions-lookup
[trydundi-extensions]
exten => _2XX,1,Macro(user-callerid,SKIPTTL)
exten => _2XX,n,Macro(record-enable,${CALLERID(number)},OUT)
exten => _2XX,n,Macro(dundi-extensions,${EXTEN})
exten => _2XX,n,Congestion(5)
exten => _2XX,n,Hangup
[dundi-parking]
exten => _7X,1,Goto(parkedcalls,${EXTEN},1)
[dundi-parking-local]
include => dundi-parking
[dundi-parking-switch]
switch => DUNDi/parking
[dundi-parking-lookup]
include => dundi-parking
include => dundi-parking-switch
[macro-dundi-parking]
exten => s,1,Goto(${ARG1},1)
include => dundi-parking-lookup
[trydundi-parking]
; no remote parking
Note: Looking for a better way to handle jumping into user-callerid macro-dundi-extensions rather than matching our remote extension range. Better recovery from a DUNDi lookup failure?
And the branchoffice side:
branchoffice dundi.conf:
[general]
; Define the max depth in which to search the DUNDi system (also max # of
; seconds to wait for a reply)
;
ttl=32
; If we don.t get ACK to our DPREQUEST within 2000ms, and autokill is set
; to yes, then we cancel the whole thing (that.s enough time for one
; retransmission only). This is used to keep things from stalling for a long
; time for a host that is not available, but would be ill advised for bad
; connections. In addition to .yes. or .no. you can also specify a number
; of milliseconds. See .qualify. for individual peers to turn on for just
; a specific peer.
;
autokill=yes
; If the cache time is too large, a route may change and be unreachable
; until the cache expires. By contrast, if you have a large network, it is
; extremely inefficient to be sending every single search out to the network
; simply because one.s cache expires after a few seconds. Try to adjust
; this based upon the size of your network. By default this is set to
; 3600 seconds (one hour). This will work fine for a fairly static setup,
; but should be lowered for a more dynamic environment.
cachetime=3600
[mappings]
services => dundi-services,0,IAX,services:${SECRET}@10.10.10.55/${NUMBER},nopartial
extensions => dundi-extensions,0,IAX,extensions:${SECRET}@10.10.10.55/${NUMBER},nopartial
parking => dundi-parking,0,IAX,parking:${SECRET}@10.10.10.55/${NUMBER},nopartial
[00:13:00:00:00:00] ;mainoffice.company.com
model = symmetric
host = mainoffice.company.com
inkey = mainoffice.company.com
outkey = branchoffice.company.com
include = all
permit = all
qualify = yes
order = primary
branchoffice iax_custom.conf:
[services] type=user dbsecret=dundi/secret context=dundi-services-local [extensions] type=user dbsecret=dundi/secret context=dundi-extensions-local [parking] type=user dbsecret=dundi/secret context=dundi-parking-local
branchoffice extensions_custom.conf:
[ext-local-custom]
include => trydundi-parking
include => trydundi-extensions
include => trydundi-services
[dundi-services]
[dundi-services-local]
include => dundi-services
[dundi-services-switch]
switch => DUNDi/services
[dundi-services-lookup]
include => dundi-services-local
include => dundi-services-switch
[macro-dundi-services]
exten => s,1,Goto(${ARG1},1)
include => dundi-services-lookup
[trydundi-services]
exten => _6XX!,1,Macro(user-callerid,SKIPTTL)
exten => _6XX!,n,Macro(record-enable,${CALLERID(number)},OUT)
exten => _6XX!,n,Macro(dundi-services,${EXTEN})
exten => _6XX!,n,Congestion(5)
exten => _6XX!,n,Hangup
[dundi-extensions]
exten => 244,1,Goto(ext-local,${EXTEN},1)
[dundi-extensions-local]
include => dundi-extensions
[dundi-extensions-switch]
switch => DUNDi/extensions
[dundi-extensions-lookup]
;include => dundi-extensions
include => dundi-extensions-switch
[macro-dundi-extensions]
exten => s,1,Goto(${ARG1},1)
include => dundi-extensions-lookup
[trydundi-extensions]
exten => _1XX,1,Macro(user-callerid,SKIPTTL)
exten => _1XX,n,Macro(record-enable,${CALLERID(number)},OUT)
exten => _1XX,n,Macro(dundi-extensions,${EXTEN})
exten => _1XX,n,Congestion(5)
exten => _1XX,n,Hangup
[dundi-parking]
; no local parking lot
[dundi-parking-local]
include => dundi-parking
[dundi-parking-switch]
switch => DUNDi/parking
[dundi-parking-lookup]
include => dundi-parking
include => dundi-parking-switch
[macro-dundi-parking]
exten => s,1,Goto(${ARG1},1)
include => dundi-parking-lookup
[trydundi-parking]
exten => _7X,1,Macro(dundi-parking,${EXTEN})
exten => _7X,2,Congestion(5)
exten => _7X,3,Hangup
Extension routing appears to be working at this time.
But now.....
Objective 2: Consolidated company directory
Problem: IVR on mainoffice has company directory enabled - how to include extensions from branchoffice in this directory?
Solution
Utilize a seperate VM context for each office, then rsync mirror the VM contexts regularly. In combination with #1807, this allows the directory AGI to read off remote extensions. Including the dundi search in ext-local-custom allows seamless routing to the remote machine.
Mainoffice has users: 144: Bob Dole, vm context "mainoffice" 145: John Smith vm context "mainoffice"
Branchoffice has user: 203: Jane Doe vm context "branchoffice"
IVR on Mainoffice has its directory context set to "General", which will include all defined contexts. The only things that remain are:
- Get the remote vm context listed in the local voicemail.conf tree
- Get the remote vm user recordings into the voicemail filesystem
