Dial Plan for multiple GSM gateways

I have multiple GSM gateways that I have created for endpoints in asterisks, however, I facing a challenge in setting up some sort of logic to explicitly route calls to the individual gsm gateways.

For instance, GSM 1 has it’s users assigned by DID, the same applies to the other GSM gateways. I have a cloud PBX that manages these DIDs hence sending the respective call to the designated GSM gateway.

With the below Dialplan, I am able to push the call to one GSM gateway at a time, I need guidance on sending calls to any of the gateways depending on which DID the call is coming from

[from-cloud]
exten => _256.,1,NoOp(Incoming call from Cloud to ${EXTEN})
same => n,Dial(PJSIP/${EXTEN:0}@gsm_endpoint)  ; 
same => n,Hangup()

DID is a term used in the FreePBX world, but is not an official Asterisk, or even SIP, term. In FreePBX it refers to the PSTN number that the call is coming in via, not where it is coming from. Caller ID is where it coming from, originally.

Could you please provide the contents of the incoming INVITE request and explain which part of it represents what you call the DID.

(Whilst DID has simply come to mean a PSTN destination number, in the commercial SIP world, the origin of the term is direct in dialling, where a PABX would have a block of PSTN numbers allocated to, without having an individual line for each. The PSTN operator would forward, at least, the trailing part of the number.)

I might have used the wrong terms here but allow me to rephrase my problem.

I have GSM gateways on-prem configured and trunked to asterisk. Asterisk has a PEER connection to the cloud pbx that handles most of the calling agent functionalities(please contextualize and visualize my scenario, let’s not pick out the unnecessary bits). So, groups of say 20 people are given a single DID from the cloud contact center(cloud pbx) and the DID is attached to a specific GSM gateway on-prem.

Here is my problem, I have setup asterisk with PJSIP and I have managed to make calls via one(1) GSM gateway at a time, however, with the project’s scalability, all the GSM gateways(6 in total) should be able to make/process calls as per the assigned user group. I know this has to do with the dialplan/contexts in how they should be setup, to my best belief, the dialplans should be set up explicitly to process the calls respectively.

Scenario.
Group 1 Reqs.
DID: +256772100100
GSM Gateway: 192.168.90.3

All calls made by users in group one are processed by GSM Gateway 1. There shouldn’t be any dynamic routing in asterisk’s dialplan to check for which gateway is available to process the call, no, the calls are explicitly sent to the respective gateway which has been setup via endpoints.

This is my working dialplan for 1 gateway.

[from-cloud]
exten => _256.,1,NoOp(Incoming call from Cloud to ${EXTEN})
same => n,Dial(PJSIP/${EXTEN:0}@gsm_endpoint)  ; 
same => n,Hangup()

“from-cloud” dialplan is the entry point that handles calls being sent via the softphones then it should be able to find which gateway is the call destined to for processing.

Hope this is clear enough.

As I said, please show the INVITE and highlight the DID. The dialplan is expecting the outgoing side number, with a Uganda country code and no +, in the request URI, which is where you would normally find the “DID”, unless the request URI was completely fixed. In addition, your DID’s start with the Uganda code, preceded by a +, which is an additional indication that the request URI is not carrying the DID. We need to see the INVITE so that we can tell if your requirement can be met, at all, without changing the sending end, and if so what has to be done to determine the DID.

As you are forwarding the call to the request URI, that cannot be one of your DIDs, with the + implied, as that would cause the call to loop back to you.

Incidentally, ${variable:0} is a degenerate case. People would normally use just ${variable}. If there is a reason for why using :0 is essential, it is likely based on a misunderstanding.

Let me see if I understand what you want: You want to send the incoming calls to different gateways depending on their DID number? Sort of if the incoming call comes from +256776XXXXXX you want to route the call to gateway 1, if it starts with +256114XXXXX you want to route it to gateway 2, and so on?

1 Like

I finally figured it out, this is my final DIAL plan with the desired logic:

[from-cloud]
 exten => _X.,1,NoOp(Incoming call from cloud to ${EXTEN})
 same => n,Verbose(1, *** Extracting DID from From Header ***)
 same => n,Set(RAW_FROM_HEADER=${PJSIP_HEADER(read,From)}) ; Use PJSIP_HEADER to extract the From header
 same => n,Verbose(1, From Header: ${RAW_FROM_HEADER})

 ; Extract only the phone number from the From header using regex
 same => n,Set(CALLER_DID=${CUT(CUT(RAW_FROM_HEADER,"<sip:",2),@,1)})
 same => n,Set(CALLER_DID=${CUT(CALLER_DID,:,2)}) ; Removes the 'sip:' prefix if it exists
 same => n,Verbose(1, Extracted Caller DID: ${CALLER_DID})

 ; Route based on the extracted DID
 same => n,GotoIf($["${CALLER_DID}" = "+256772100100"]?gsm_endpoint,${EXTEN},1)
 same => n,GotoIf($["${CALLER_DID}" = "+256772100101"]?gsm2_endpoint,${EXTEN},1)
 same => n,GotoIf($["${CALLER_DID}" = "+256772100111"]?gsm3_endpoint,${EXTEN},1)
 same => n,GotoIf($["${CALLER_DID}" = "+256772100112"]?gsm4_endpoint,${EXTEN},1)
 same => n,GotoIf($["${CALLER_DID}" = "+256772100115"]?gsm5_endpoint,${EXTEN},1)
 same => n,GotoIf($["${CALLER_DID}" = "+256772100118"]?gsm6_endpoint,${EXTEN},1)
 same => n,Verbose(1, No matching DID found. Hanging up.)
 same => n,Hangup()

[gsm_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm_endpoint)

[gsm2_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm2_endpoint)

[gsm3_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm3_endpoint)

[gsm4_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm4_endpoint)

[gsm5_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm5_endpoint)

[gsm6_endpoint]
exten => _X.,1,Dial(PJSIP/${EXTEN}@gsm6_endpoint)

I had to work with data from the INVITE to extract the DID attached to a specific gsm endpoint then have the call processed accordingly.

So you wanted to route based on some definition of the caller ID, not based on what people normally call the DID!

As you still haven’t provided the incoming INVITE, I can’t tell if you are taking the actual caller ID from other headers, or whether this is the only source of caller ID information. If it is just a simple caller ID, your solution is over engineered. Firstly Asterisk does the parsing of the From header for you, and, secondly, there are special dialplan features for matching on caller ID.

The precedence for the source of caller ID in Asterisk, highest priority first, is:

  1. one set by the dialplan
  2. one set in the endpoint definition
  3. the user part of the URI in a P-Asserted-Identity, or Remote-Party-ID header, if present, and set to trusted by the endpoint definition.
  4. the user part of URI in the From header.

So, in the simple case, it will take the caller ID from where you took it from

In the dialplan, you can follow the extension number or pattern with a / and a caller ID number or pattern. You can have multiple dialplan priorities with the same priority number, and the best match is used, so one way of routing based on caller ID is just to repeat the Dial with a different pattern in the caller ID part of the match each time.

You can, of course just read and test CALLERID(num), or assign it to a variable, and test that (preferably not one containing “DID”, as that will confuse anyone trying to maintain it in the future).

Your recommendations are more applicable to incoming calls where Caller ID is used to route calls. In my case, outbound calls are being routed based on the “From” header, which contains a DID (like +256772100115). This DID is associated with a specific GSM gateway on my Asterisk system for call termination.

My existing approach, where I parse the “From” header to extract the DID and use it to determine the GSM gateway for outbound routing, is valid for my scenario.

Here’s why:

Custom Parsing Is Needed:

Asterisk does not automatically use the “From” header for routing in the way I need.
Parsing the “From” header to extract the DID is necessary to implement my routing logic.

Dialplan Logic for Outbound Routing:
After extracting the DID from the “From” header, my dialplan can map the DID to the appropriate GSM gateway. This is consistent with the flexibility that Asterisk provides for outbound routing.

Efficient Outbound Routing:
Using the “From” header DID as the key for determining which GSM gateway to use ensures calls are routed correctly without ambiguity.

Asterisk does automatically parse the From header. It is one of the most fundamental things its does for SIP requests.

As we seem to be going round in circles, I’ll leave it there and hope anyone encountering this thread in the future will read it carefully.

(As long as nothing overrides the From header, in this case, for which I’d need to see the original INVITE, your whole diaplan can be reduced to something like:

; Route based on the Caller ID
[from-cloud]
exten => _256.,1,NoOp(Incoming call from Cloud to ${EXTEN})
exten =>_256./+256772100100,n,Dial(PJSIP/${EXTEN}@gsm_endpoint)  ; 
exten =>_256./+256772100101,s,Dial(PJSIP/${EXTEN}@gsm_endpoint2)  ; 
exten =>_256./+256772100111,s,Dial(PJSIP/${EXTEN}@gsm_endpoint3)  ; 
exten =>_256./+256772100112,s,Dial(PJSIP/${EXTEN}@gsm_endpoint4)  ; 
exten =>_256./+256772100115,s,Dial(PJSIP/${EXTEN}@gsm_endpoint5)  ; 
exten =>_256./+256772100118,s,Dial(PJSIP/${EXTEN}@gsm_endpoint6)  ; 
exten =>_256.,s,Verbose(1, No matching Caller ID found. Hanging up.)
same => n,Hangup()

)

Hi @david551

Here is a sample INVITE as you had requested.

INVITE sip:256752603334@62.56.168.236 SIP/2.0
Via: SIP/2.0/UDP 3.9.15.5:5060;branch=z9hG4bK307fb5ce;rport
Max-Forwards: 70
From: "+256772100101" <sip:+256772100101@3.9.15.5>;tag=as16c9c484
To: <sip:256752603334@62.56.168.236>
Contact: <sip:+256772100101@3.9.15.5:5060>
Call-ID: 089e09c914654c8005942697014fbfd9@3.9.15.5:5060
CSeq: 102 INVITE
User-Agent: FPBX-14.0.13.34(16.26.1)
Date: Tue, 03 Dec 2024 09:07:19 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Content-Type: application/sdp
Content-Length: 346

v=0
o=root 2084629611 2084629611 IN IP4 3.9.15.5
s=Asterisk PBX 16.26.1
c=IN IP4 3.9.15.5
t=0 0
m=audio 16956 RTP/AVP 0 8 3 111 9 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:3 GSM/8000
a=rtpmap:111 G726-32/8000
a=rtpmap:9 G722/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=maxptime:150
a=sendrecv

I will test out your Dialplan over the weekend when we have closed, at the moment, my Dialplan is working as expected, if your summarized Dialplan meets my expectations, I will most definitely try to use the same.

Key takeaways:

From: "+256772100101" <sip:+256772100101@3.9.15.5>;tag=as16c9c484

This is the entry point from the cloud server that pushes the call to the designated gsm endpoint, since each endpoint has a specific DID attached to it for outbound call processing.

To: <sip:256752603334@62.56.168.236>
After the endpoint has been identified, the call is now processed from here

I will post here my observations in a couple days once I have tested further your suggestions. Thank you.

This is what becomes the extension.