Complete Issabel Configs for MSTeams Direct Routing

Related to previous post about how to setup Issabel as Direct Routing SBC to MSTeams,I got requests from acquintances about the complete configurations from Issabel side.

So here they are:

sip_custom.conf (modify this from Asterisk File Editor webGUI)

[MSTeams-Inbound]
disallow=all
host=sip.pstnhub.microsoft.com
type=friend
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-internal
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz

[MSTeams02-Inbound]
disallow=all
host=sip2.pstnhub.microsoft.com
type=friend
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-internal
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz

[MSTeams03-Inbound]
disallow=all
host=sip3.pstnhub.microsoft.com
type=friend
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-internal
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz

[Carrier-Inbound]
disallow=all
host=xxx.xxx.xxx.xxx
type=friend
port=xxxx
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-internal
insecure=port,invite
nat=force_rport,comedia

replace xxx are Carrier ip address, and port. replace fromdomain with your own FQDN Issabel server.

sip_additional.conf (modify this from Trunk webGUI menu)

[MSTeams-Outbound]
disallow=all
host=sip.pstnhub.microsoft.com
type=peer
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-trunk
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz
encryption=yes

[MSTeams02-Outbound]
disallow=all
host=sip.pstnhub.microsoft.com
type=peer
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-trunk
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz
encryption=yes

[MSTeams03-Outbound]
disallow=all
host=sip3.pstnhub.microsoft.com
type=peer
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-trunk
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz
encryption=yes

[Carrier-Outbound]
host=xxx.xxx.xxx.xxx
port=xxxx
canreinvite=yes
type=peer
insecure=port,invite
nat=force_rport,comedia
context=from-trunk
qualify=yes
dtmfmode=rfc2833

replace xxx are Carrier ip address, and port. replace fromdomain with your own FQDN Issabel server.

Outbound to MSTeams DID

I created 3 outbounds route with this order:

  • Outbound to MSTeams
  • Outbound to Carrier for REFER
  • Outbound to Carrier for each DID

The order should as such. And you dont need to setup any Inbound Routes.

Outbound route to MSTeams should contain your assigned DIDs toward MSTeams account. Should look like this:

Outbound Route to Accomodate REFER

Since I dont know how to replace user part of the URI with RPID, I just pick one DID to accomodate REFER. This is less desireable but the REFER outbound route can act as Global CallerID for the direct routing. Notice that the route contains disposable prefix 9 to avoid overriding from 3rd route (i.e Route for each DID).

This image has an empty alt attribute; its file name is image-2.png

Outbound Route to Carrier for Each DID

(extra) Asterisk SIP Settings from Unembedded IssabelPBX menu

Point the tlsprivatekey, tlscertfile, and tlscafile to your approriate files. And realm should be the Issabel server’s FQDN. The tlsbindaddr can by any ip address and port you wish to use.

That should do it. If I miss anything, please clue me at the comment.

Microsoft Teams Direct Routing with Issabel (Asterisk)

Why?

FYI

  • Should have Issabel server properly installed with ip public and . The server firewall should be hardened by only allowing Carrier and Microsoft cloud ip address blocks (52.112.0.0/14). Even better if the only maintenance can only be accessed by VPN. Dont open your Issabel webGUI, SSH, SIP & RTP ports to public internet whenever possible.
  • My Issbel (Asterisk) server is already connected to Ofon as the PSTN Carrier, and already has a DID assigned.
  • Requires to edit chan_sip.c to comply with Microsoft Teams Phone System that demands SIP From and Contact header host part to be FQDN. From header is easy, just add fromdomain at the Asterisk trunk config and you’re good. The Contact header is the tricky one, based on this screenshot clue (stolen from thread https://community.asterisk.org/t/fqdn-in-the-contact-header/77625):
  • By half-assed editing the chan_sip.c , I expect there will be repercussion somewhere else in the system (plus I am no good at C, should find a better way to insert the change with more elegant solution), so dont share the PBX with something else for production, and use this as POC only.
  • Dont ever think to commercialize this without Microsoft approval, nor use this for officials. You may not be able to open tickets if you encounter problems with this installation although you are a legit Office 365 tenant with Phone System license. The certified SBC list can be found here.
  • Should have access to DNS server to add TXT type record for domain authentication.
  • Should have global admin account to make changes in O365 portal.
  • In this example, I have two E1 O365 + Phone System licenses. One to be assigned to my account and one to be assigned to SIP trunk domain dummy user.
  • You cant do multi tenant with this installation. Asterisk itself is a single realm (CMIIW).
  • Still cant find how to modify Contact header in OPTIONS, hence you’ll find tlsdontverifyserver=yes in Issabel general config.
  • Will periodically update this post with something new whenever I find improvement. So forgive me if you find something different when you read back the post later.

Microsoft Teams Phone System Side Config

In this stage you will need to add an SSL’ed domain to Office 365 portal. Microsoft-recommended CA list can be found here. I myself use GeoTrust.

  • Microsoft has a very detailed documentation that can be found at https://docs.microsoft.com/en-us/microsoftteams/direct-routing-plan . I wont rewrite the details here.
  • Using global admin to add domain (mine is ast.ofon.biz), to O365 admin portal.
  • Add a dummy user for that domain and assign O365 + Phone System license for that dummy user. For example, my dummy user for the newly added domain is sip@ast.ofon.biz. If successfully added, I will find the user in Active users table like this:
  • that is how Microsoft Teams will recognize the SIP trunk to our Issabel (Asterisk) server and assign license to the trunk.
  • Login using powershell to your account using global admin user. Again, the powershell preparation wont be repeated here since the detailed documentation can be found at https://docs.microsoft.com/en-us/microsoftteams/direct-routing-configure. Next steps will require us to import Sfbonline module to powershell.
  • Start the session (you will be asked for password on a popped up windows) :
$sfb = New-CsOnlineSession -Username "admin@ofonio.onmicrosoft.com"
Import-PSSession $sfb -AllowClobber
  • Add the Issabel (Asterisk server) as PSTN Gateway:
New-CsOnlinePSTNGateway -Fqdn ast.ofon.biz -SipSignalingPort 5061 -ForwardCallHistory $true -Enabled $true -ForwardPai $true -MaxConcurrentSessions 5
  • See if the server is properly added with Get-CsOnlinePstnGateway :
 Get-CsOnlinePSTNGateway


Identity                            : ast.ofon.biz
InboundTeamsNumberTranslationRules  : {}
InboundPstnNumberTranslationRules   : {}
OutboundTeamsNumberTranslationRules : {}
OutboundPstnNumberTranslationRules  : {}
Fqdn                                : ast.ofon.biz
SipSignalingPort                    : 5061
FailoverTimeSeconds                 : 10
ForwardCallHistory                  : True
ForwardPai                          : True
SendSipOptions                      : True
MaxConcurrentSessions               : 5
Enabled                             : True
MediaBypass                         : False
GatewaySiteId                       :
GatewaySiteLbrEnabled               : False
FailoverResponseCodes               : 408,503,504
GenerateRingingWhileLocatingUser    : True
PidfLoSupported                     : False
MediaRelayRoutingLocationOverride   :
ProxySbc                            :
BypassMode                          : None
  • Create a routing and routing policy that will utilize the Issabel (Asterisk) server:
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="Default"}

New-CsOnlineVoiceRoute -Identity "CatchAll" -NumberPattern ".*" -OnlinePstnGatewayList ast.ofon.biz -Priority 1 -OnlinePstnUsages "Default"

New-CsOnlineVoiceRoutingPolicy -Identity "DefaultVoicePolicy" -OnlinePstnUsages "Default"
  • Assign DID & VoiceRoutingPolicy to my account.

Issabel (Asterisk) Side Config

We will have two SIP connection configs for Microsoft Teams Phone System and two SIP connection configs for Carrier. Each connection will have different contexts. One SIP connection for Microsoft Teams will be from-internal, so call from Microsoft Teams will be treated as call from extension. And the other SIP connection will be treated as outbound trunk with from-trunk context. Same deal with the two SIP connections toward Carrier.

Microsoft provides 3 hosts to connect a SIP trunk with:

  • sip.pstnhub.microsoft.com
  • sip2.pstnhub.microsoft.com
  • sip3.pstnhub.microsoft.com

This example will only test the first one (sip.pstnhub.microsoft.com).

  • Adding Trunk to Microsoft Teams from PBX -> PBX Configuration -> Trunks. Add SIP Trunk and fill these parameters :
Trunk Name: MSTeams-Outbound
PEER Details:
disallow=all
host=sip.pstnhub.microsoft.com
type=peer
transport=tls
port=5061
allow=all&ulaw
qualify=yes
dtmfmode=rfc2833
context=from-trunk
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz
encryption=yes
  • And as “extension” add the following configs to sip_custom.conf using PBX -> Tools -> Asterisk File Editor :
[MSTeams-Inbound]
disallow=all
host=sip.pstnhub.microsoft.com
type=friend
transport=tls
port=5061
allow=all
allow=ulaw
qualify=yes
dtmfmode=rfc2833
context=from-internal
insecure=port,invite
nat=force_rport,comedia
fromdomain=ast.ofon.biz
  • To refer to certifcate files, go to PBX -> PBX Configuration -> Unembedded IssabelPBX. At the following page, go to Settings -> Asterisk SIP Settings and then Add field at the Other SIP Settings with these:
realm=ast.ofon.biz
tlsenable=yes
tlsbindaddr=0.0.0.0:5061
tlsprivatekey=/etc/asterisk/keys/star_ofon_biz.key
tlscertfile=/etc/asterisk/keys/star_ofon_biz.pem
tlscafile=/etc/asterisk/keys/star_ofon_biz.crt
tlscipher=ALL
tlsclientmethod=tlsv1
tlsdontverifyserver=yes
  • Save and reload the config.
  • Add the Outbound Routes to Microsoft Teams and Carrier (Microsoft Teams outbound should be aboive the Carrier). Since my carrier mostly sends with prefix 0, I have to convert it to E.164. Please modify these according to your apporpriate settings:

At this point, you should be able to call outbound from Microsoft Teams account but not the other way (inbound). As mentioned before, Microsoft Teams Phone System require us to send From and Contact header host as FQDN. The trunk settings in Asterisk wont change Contact header host to FQDN hence we’ll receive this message:

INVITE sip:+622430000062@sip.pstnhub.microsoft.com:5061 SIP/2.0
Via: SIP/2.0/TLS xxx.xxx.xxx.xxx:5061;branch=z9hG4bK4e781f8a;rport
Max-Forwards: 70
From: "02139700001" <sip:02139700001@ast.ofon.biz>;tag=as36acd9d9
To: <sip:+622430000062@sip.pstnhub.microsoft.com:5061>
Contact: <sip:02139700001@xxx.xxx.xxx.xxx:5061;transport=tls>
Call-ID: 2239ff467456d8f03c4348826168d7a6@ast.ofon.biz
CSeq: 102 INVITE
User-Agent: Asterisk 16.7.0
Date: Fri, 08 May 2020 04:45:38 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Content-Type: application/sdp
Content-Length: 1324

...
...
...
SIP/2.0 403 Forbidden
FROM: "02139700001"<sip:02139700001@ast.ofon.biz>;tag=as36acd9d9
TO: <sip:+622430000062@sip.pstnhub.microsoft.com:5061>
CSEQ: 102 INVITE
CALL-ID: 2239ff467456d8f03c4348826168d7a6@ast.ofon.biz
VIA: SIP/2.0/TLS xxx.xxx.xxx.xxx:5061;branch=z9hG4bK4e781f8a;rport
REASON: Q.850;cause=63;text="735a16a2-5f83-4cb0-bdec-9cb64edff551;Provided Trunk FQDN 'xxx.xxx.xxx.xxx' is not allowed. Connection allows following fqdns: *.ofon.biz, ofon.biz, *.ofon.biz."
CONTENT-LENGTH: 0
ALLOW: INVITE,ACK,OPTIONS,CANCEL,BYE,NOTIFY
SERVER: Microsoft.PSTNHub.SIPProxy v.2020.5.6.2 i.ASEA.2

chan_sip.c

There is no Asterisk source in Issabel distro so we have to get it ourself. From Issabel linux console, git clone the Asterisk github and checkout for 16.7.0 (since this is my Issabel’s Asterisk version).:

cd /usr/src
git clone https://github.com/asterisk/asterisk.git
cd asterisk
git checkout 16.7.0

Edit file /usr/src/asterisk/channel/chan_sip.c. Look at build_contact() we will find these lines:

static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming)
{
        char tmp[SIPBUFSIZE];
        char *user = ast_uri_encode(p->exten, tmp, sizeof(tmp), ast_uri_sip_user);
        int use_sips;
        char *transport = ast_strdupa(sip_get_transport(p->socket.type));

        if (incoming) {
                use_sips = uas_sips_contact(req);
        } else {
                use_sips = uac_sips_contact(req);
        }

        if (p->socket.type == AST_TRANSPORT_UDP) {
                ast_string_field_build(p, our_contact, "<%s:%s%s%s>", use_sips ? "sips" : "sip",
                        user, ast_strlen_zero(user) ? "" : "@",
                        ast_sockaddr_stringify_remote(&p->ourip));
        } else {
                ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
                        use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
                        ast_sockaddr_stringify_remote(&p->ourip), ast_str_to_lower(transport));
        }
}

The last conditional lines are where the Contact header gets value from. Since we only need to modify for TCP transport, edit the line:

 else {
                ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
                        use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
                        ast_sockaddr_stringify_remote(&p->ourip), ast_str_to_lower(transport));
        }

to become :

 else {
                ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
                        use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
                        p->fromdomain, ast_str_to_lower(transport));
        }

p->fromdomain will follow whatever “fromdomain=” value in the SIP trunk config.

Update:
Got input from Igo Cunha that the call from MSteams to PSTN will be disconnected after 6 sec. I can confirm I got random disconnect call at about 6 sec from the same direction. Try replace:
 
ast_sockaddr_stringify_remote(&p->ourip) 

with your Asterisk's FQDN string (e.g "ast.ofon.biz" in my case) instead of p->fromdomain.

Thanks Igo!

I understand this is not elegant way, but that should do it. Compile the source and we will have brand new chan_sip.so. Backup the original one and replace it after:

copy /usr/lib64/asterisk/modules/chan_sip.so /usr/local/src/chan_sip.so.original
copy /usr/src/asterisk/channels/chan_sip.so /usr/lib64/asterisk/modules/chan_sip.so

then reload the module from Asterisk console:

asterisk -vr

ast*CLI> module reload chan_sip.so
Module 'chan_sip.so' reloaded successfully.
 Reloading SIP

Now test call to inbound, we should have correct SIP Contact header requirement as follows:

INVITE sip:+622430000062@sip.pstnhub.microsoft.com:5061 SIP/2.0
Via: SIP/2.0/TLS 103.135.74.15:5061;branch=z9hG4bK394b6483;rport
Max-Forwards: 70
From: "02139700001" <sip:02139700001@ast.ofon.biz>;tag=as20858906
To: <sip:+622430000062@sip.pstnhub.microsoft.com:5061>
Contact: <sip:02139700001@ast.ofon.biz;transport=tls>
Call-ID: 1a5d05f10eadb0036683f88a6177f80f@ast.ofon.biz
CSeq: 102 INVITE
User-Agent: Asterisk 16.7.0
Date: Fri, 08 May 2020 04:54:36 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Content-Type: application/sdp
Content-Length: 1324

and the call went through:

Remarks

Although it is possible to connect Direct Routing with Asterisk, still easier using Kamailio (and OpenSIPS) since they’re built solely for SIP packet engineering. Until some of them become certified with Microsoft, I guess I will be using the already ceritfied ones, like Sonus or AudioCodes, or just connect to a hosted Direct Routing Provider in Indonesia, like Ofon. Alternatively, there are Kamailo based products start providing hosted DR, like Gilawa and dSIPRouter. Check them out.

Complete Configs

Links:

Voice Conference dengan Ofon SmartPBX

Salah satu layanan conference call yang sering kami gunakan adalah Ofon SmartPBX Conference Call, selain menggunakan Google Meet, Zoom, Microsft Teams, atau Aplikabiz G-Net MeetNow. SmartPBX sendiri merupakan aplikasi hosted PBX buatan 2600hz dan merupakan bagian dari platform yang lebih besar lagi yang disebut Kazoo.

Ofon SmartPBX Conference Call cukup sering kami gunakan di kantor karena sifatnya yang sederhana, straightforward, dan cocok untuk situasi yang “mission critical”, di mana kadang jangkauan jaringan data selular tidak memadai, tapi masih bisa tetap bergabung melalui jalur PSTN yang bisa diakses luas bahkan sampai pelosok daerah. Untuk ekstensi yang masih dalam jangkauan jaringan data yang bagus, dapat bergabung melalui panggilan VoIP.

Kok ekstensi? Kayak PBX saja….

Lha kan memang layanan hosted PBX. Di gawai Android, saya menggunakan LinPhone dan GSWave untuk bisa menjadi ekstensi PBX tersebut, sementara teman-teman lain yang menggunakan iOS, lebih suka WeWei, walaupun ada LinPhone dan GSWave untuk iOS juga. Tapi jika panggilan data/VoIP tidak bisa dilakukan, panggilan dapat diteruskan melalui jaringan PSTN.

Semudah apa?

Bergabung ke SmartPBX Conference Call

Secara default, Ofon SmartPBX akan memberikan 3 nomer/DID. Satu DID sebagai nomer utama yang menjadi nomer identitas ketika melakukan panggilan PSTN. DID kedua untuk voice conference call (tampak dalam contoh di bawah nomer DID untuk Conference call: +622139710091), dan DID terakhir untuk menerima fax yang kemudian akan diforward ke email pengguna.

Setiap user Ofon SmartPBX dapat memiliki ruang konferensi sendiri. Misal berikut ini saya akan setting conference room milik saya. Pilih menu Users di panel sebelah kiri, lalu di halaman list user saya, klik kolom User Features:

Aktifkan Conference Bridge dengan klik button tersebut, lalu switch dari Disabled ke Enabled lalu isikan Personal Conference Room number. Bisa diisikan berapa saja, tapi saya lebih suka menyamakan dengan nomer ekstensi saya, misal, 6681.

Lalu klik Save Changes.

Ketika fitur Conference Bridge sudah aktif, akan muncul icon baru di tabel user saya berupa buble dialog warna hitam:

Setiap kali saya mengaktifkan fitur di user saya, akan muncul icon baru di situ. Misal, jika saya aktifkan CallForward dan Hotdesking, akan muncul icon baru seperti berikut:

oh ya, device saya di screenshoot terakhir sudah berwarna hijau karena barusan saya mengaktifkan softphone. Jika tidak aktif, icon device akan berwarna merah seperti gambar sebelumnya.

Untuk mencoba panggilan conference call dari PSTN, cukup dengan mendial nomer +622139710091 dengan memasukkan nomer room 6681 dan diakhiri tanda #.

Untuk pengguna yang hendak bergabung ke conference call dari ekstensi PBX tersebut, ada dua cara untuk bergabung. Yang pertama adalah dengan melakukan panggilan ke nomer conference call di atas (+622139710091), dan memasukkan room number seperti laiknya pengguna yang bergabung dari PSTN. Panggilan tersebut tidak akan dikenakan biaya, karena sama-sama menggunakan layanan dari Ofon dan dari kode area yang sama.

Cara kedua, ada satu ekstensi 9999 yang pada saat mendaftar sudah dibuatkan oleh Customer Service Ofon sesuai permintaan sendiri. Nomer tersebut ketika dihubungi dari ekstensi akan masuk ke Conference Call service, yang kemudian akan menayakan room number juga, seperti ketika melakukan panggilan dari PSTN. Di layanan conference call ini kita tidak bisa melakukan panggilan video atau screensharing, tapi cukup handal ketika peserta tidak sedang di area yang terjangkau internet. Seperti yang saya sampaikan di atas voice conference call ini cukup straightforward ketika di tengah situasi yang kritis atau darurat, seperti akhir-akhir ini. Koordinasi dapat berjalan dengan singkat, cepat dan jelas ketika berkoordinasi.

Bukannya jadi besar biaya ya kalau harus Dial In ke nomer tersebut?

Saya kurang tau apakah akhir-akhir ini masih banyak yang menggunakan pulsa regular untuk telepon via GSM/PSTN. Dengan tingginya minat terhada layanan data/ internet melalui operator selular, membuat dua layanan dasarnya; SMS & panggilan telepon, menjadi kurang diminati, sehingga mereka berlomba-lomba mengadakan paket SMS dan telpon ke semua operator dengan harga yang relatif cukup murah. Saya menggunakan operator XL Prioritas, dan selalu mendapat paket SMS dan panggilan ke semua operator dengan talk time yang cukup banyak:

Saya yakin sudah sangat umum bahwa perusahaan membekali karyawannya dengan kemudahan melakukan panggilan dari telepon genggam, dalam bentuk tunjangan bulanan ataupun berlangganan CUG.

Bagaimana Caranya Dial-Out

Untuk saat ini fitur dial-out conference call di Ofon SmartPBX belum ada, tapi dapat dilakukan dengan cara lain yaitu menggunakan conference phone sebagai conference bridge. Sebenarnya bisa saja sih menggunakan softphone atau ip phone biasa. Tapi perangkat tersebut harus cukup kuat untuk menjadi voice bridge karena akan menjadi pusat pertemuan media dari dan ke semua peserta. Untuk 3-4 peserta saya yakin masih lancar, tapi lebih dari itu biasanya akan berpengaruh pada kualitas suara karena resource yang digunakan juga naik seiring bertambahnya peserta. Oleh karena itu diperlukan perangkat khusus seperti conference phone. Ada macam-macam brand yang ada di Indonesia. Yang paling mahal dan tentunya paling bening biasanya keluaran Polycom dengan seri conference phones nya . Brand lain yang harganya cukup terjangkau adalah YeaLink Conference Phones dan Grandstream Audio Conferencing. Di kantor saya menggunakan Yealink CP920 ketika melakukan dial-out conference call. Perangkat tersebut teregistrasi sebagai device/ekstensi si SmartPBX. Jadi selain dial-out, pengguna lain juga dapat melakukan dial-in dari VoIP dengan menghubungi nomer ekstensinya.

Serius? Ndak ada cara lain dial-out supaya MUC nya ada di SmartPBX?

Jika Anda Programmer Web…

Ada sedikit rahasia :

https://api.ofon.io:8443/v2/

URL di atas masih dalam tahap trial, yang kelak akan diintegrasikan langsung ke SmartPBX. Tapi boleh lah dibuat coba-coba.

Contoh berikut akan menggunakan curl .

Lakukan autentikasi dengan login admin SmartPBX yang telah didapat dari OFON, buatlah md5sum, misal dari shell linux, dengan format “username:password” :

echo -n "admin@localhost.localdomain:r4h4514" | md5sum
65e26add3748f33039f119f3fd8a5735  -

berbekal md5sum -> 65e26add3748f33039f119f3fd8a5735 dan dan akun name, misal: YourCompanyPBX, dapatkan auth token:

curl -v -X PUT -H "content-type:application/json" \
-d '{"data":{"credentials":"65e26add3748f33039f119f3fd8a5735",\
"account_name":"yourcompanypbx"}}' \
https://api.ofon.io:8443/v2/user_auth | jq

Perintah di atas menghasilkan output panjang. Langsung ke bawah menuju bagian auth_token.

catatan: agar output JSON rapi, saya suka menggunakan tool jq. Jika tidak ada, bisa digantikan dengan -> | python -mjson.tool

kemudian perhatikan account id yang bisa dilihat di halaman SmartPBX bagian kanan bawah:

Berbekal auth_token dan account_id dapatkan ID conference call:

curl -X GET -H "X-Auth-Token: $AUTH_TOKEN"  \
https://api.ofon.io:8443/v2/accounts/$ACCOUNT_ID/conferences | jq

Output:

{
  "page_size": 1,
  "data": [
    {
      "owner_id": "ec356f13f300109ffb17a31edf94001a",
      "name": "Gabriel Wishnu SmartPBX Conference",
      "moderator": {
        "pins": [],
        "numbers": [],
        "join_muted": false,
        "join_deaf": false
      },
      "member": {
        "pins": [],
        "numbers": [],
        "join_muted": false,
        "join_deaf": false
      },
      "id": "7810f4b14b041e1f951c31ccfb124b1d",
      "flags": [],
      "conference_numbers": [
        "6681"
      ],

dari output JSON di atas kita mendapatkan identitas conference:

7810f4b14b041e1f951c31ccfb124b1d

dari conference room number 6681 yang sudah diset sebelumnya. Misal nomer telpon yang hendak didial-out adalah 081234567890, dan callerid yang akan kita gunakan untuk dial-out adalah nomer DID conference room di atas (+622139710091) maka cara panggilnya:

curl -v -X PUT -d '{"action": "dial", \ 
"data": {"endpoints":["081234567890"],"caller_id_number":"+622139710091"}}' \
-H "X-Auth-Token: $AUTH_TOKEN" \
https://api.ofon.io:8443/v2/accounts/$ACCOUNT_ID/conferences/7810f4b14b041e1f951c31ccfb124b1d  | jq

Atau jika ada beberapa nomer sekaligus (misal 081234567890, 081818181818, 08989898989, 02129393939):

curl -v -X PUT -d '{"action": "dial", \ 
"data": {"endpoints":["081234567890","081818181818", "08989898989", "02129393939"], \
"caller_id_number":"+622139710091"}}' \
-H "X-Auth-Token: $AUTH_TOKEN" \
https://api.ofon.io:8443/v2/accounts/$ACCOUNT_ID/conferences/7810f4b14b041e1f951c31ccfb124b1d  | jq

Perintah tersebut akan memanggil sekian nomer sekaligus untuk bergabung dalam voice conference.

catan:

sehubungan dengan adanya ratelimiter call di SBC Ofon untuk mencegah fraud, panggilan serentak ke nomer seperti diatas akan dibatasi 3 nomer dalam sekali waktu. Jadi jika peserta lebih dari 3, perlu dipecah per panggilan hanya dapat melakukan 3 panggilan keluar dari callerid yang sama. 

Panggilan yang terkena rate limit akan memberikan pesan:

     {
        "message": "failed to start call: OUTGOING_CALL_BARRED",
        "hangup_cause": "OUTGOING_CALL_BARRED",
        "status": "error",
        "endpoint_id": "081234567890"
      }

Sampai di sini dulu, kapan-kapan saya akan bahas lebih lanjut mengenai API ini.

Selamat mencoba!

Links:

[Tips] Mengganti Hostname Jitsi-Meet

Mengganti hostname di Jitsi-Meet ternyata ribet banget. Yang paling gampang diuninstall terlebih dahulu baru install ulang.

apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jicofo jitsi-videobridge2 nginx

Lalu hapus semua direktori nginx dan letsencrypt.

Setelah itu install ulang Nginx dan Jitsi-Meet. Pada saat tengah proses instalasi Jitsi-Meet kita akan kembali mengisikan hostname.

Replikasi usrloc Kamailio Dengan dmq_usrloc

Salah satu redundansi SIP yang digunakan di kantor adalah dengan replikasi usrloc antar dua SIP registrar di dua datacenter yang berbeda. Lalu hostname akan diresolve ke kedua server tersebut, sehingga jika pada saat ada panggilan masuk dari Offnet ke pelanggan, dan kebetulan salah satu server SIP registrar down beberapa detik sebelum panggilan diteruskan, di SIP server lain masih memiliki AoR/usrloc user yang dituju dan panggilan tetap bisa diteruskan. (Untuk redundansi outbound call tidak akan di bahas di tulisan ini).

Untuk mereplikasi AoR/usrloc saya menggunakan modul dmq

Berikut cara install dan konfigurasi dmq untuk dua SIP server yang saya beri nama sip01.domain.tld (10.0.0.1) dan sip02.domain.tld (10.0.0.2). Skenario berikut ini juga bisa dijalankan untuk dua datacenter yang berbeda, sehingga dmq melakukan sinkronisasi via internet.

sip01.domain.tld (10.0.0.1)

Dengan asumsi server yang dijalankan adalah Centos versi 7 dan ini adalah SIP server untuk Kazoo platform yang sudah disetup dan berjalan, install dmq:

yum install -y kamailio-dmq_userloc.x86_64

Karena ini adalah salah satu komponen dari Kazoo platform, edit file /etc/kazoo/kamailio/default.cfg, dan tambahkan baris berikut:

...
...
...
####### DMQ & dmq_userloc #######
loadmodule "dmq.so"
loadmodule "dmq_usrloc.so"
modparam("dmq", "server_address", "sip:10.0.0.1:5060")
modparam("dmq", "notification_address", "sip:10.0.0.2:5060")
modparam("dmq", "num_workers", 8)
modparam("dmq", "ping_interval", 90)
modparam("dmq_usrloc", "enable", 1)
modparam("dmq_usrloc", "sync", 0)
...
...
...

Perhatikan baris yang ada server_address dan notification_address. Untuk server pertama, server_address diisikan ip address dari server sip01.domain.tld, dan notification_address diisikan sip address sip02.domain.tld.

Lalu masih di dalam file yang sama, tapi di dalam block route, tambahkan baris:

...
...
...
# routing dmq 
    if(is_method("KDMQ"))
        {
                dmq_handle_message();
        }
...
...
...

Save dan restart service kamailio.

sip02.domain.tld (10.0.0.1)

Pada dasarnya kita melakukan hal yang sama dengan sip server pertama. Install module dmq di server sip server kedua.

yum install -y kamailio-dmq_userloc.x86_64

Lalu konfig file /etc/kazoo/kamailio/default.cfg dan tambahkan baris berikut:

...
...
...
####### DMQ & dmq_userloc #######
loadmodule "dmq.so"
loadmodule "dmq_usrloc.so"
modparam("dmq", "server_address", "sip:10.0.0.2:5060")
modparam("dmq", "notification_address", "sip:10.0.0.1:5060")
modparam("dmq", "num_workers", 8)
modparam("dmq", "ping_interval", 90)
modparam("dmq_usrloc", "enable", 1)
modparam("dmq_usrloc", "sync", 0)
...
...
...

Perhatikan di baris yang ada server_address dan notification_address. IP address diset berbalik dengan konfigurasi terhadap sip server pertama. Selanjutnya untuk blok route sama:

...
...
...
# routing dmq 
    if(is_method("KDMQ"))
        {
                dmq_handle_message();
        }
...
...
...

Save file konfigurasi dan reload Kamailio.

Test Hasil

Untuk melihat apakah jumlah pengguna yang teregistrasi di kedua sip server sudah sama (harusnya sama, jika ada perbedaan mungkin sedang dalam tahap sinkronisasi karena kadang-kadang pengguna terputus dan tersambung di sip server) jalankan perintah berikut:

kamctl ul show --brief | jq

Perhatikan output di bagian berikut untuk kedua server:

 "Stats": {
            "Records": 670,
            "Max-Slots": 4
          }

Untuk entri Records baik di server pertama maupun server kedua, harusnya berjumlah sama yaitu 670. Jika ada perbedaan paling hanya 5 – 10 angka saja. Dan jika ingin benar-benar memastikan, ambil contoh satu record dari hasil di atas (harusnya hasil di atas berupa list panjang, tapi dalam contoh di sini hanya diambil potongan output itu saja), misal untuk record:

{
   "AoR": "user_8udxaw@11778f.sip.domain.tld"
}

Kita bisa search AoR tersebut di kedua server dengan cara:

kamctl ul show user_8udxaw@11778f.sip.domain.tld | jq

Jika tanpa dmq user tersebut hanya ada di salah satu server. Jika dengan dmq yang terinstall dengan baik, maka di kedua server mencatat AoR yang sama sekaligus.

Yealink T19 E2 dengan OPUS

Yay!! Hari ini dapet kiriman Yalink T19 E2 untuk dicoba dengan codec OPUS dan berjalan baik sejauh ini. Belum tau bagaimana nanti jika sudah dipasang di lapangan. Ada keperluan panggilan melalui jalur data satelit yang sudah tentu latensinya tinggi. G729 tidak dapat mengakomodir komunikasi dengan baik, hingga akhirnya dicoba dengan OPUS dan diterima cukup baik.

Beberapa waktu yang lalu sudah mencoba mengontak beberapa produsen IP phone, rata-rata codec OPUS hanya didukung di perangat hi-end, maklum, butuh prosesor agak kenceng. Entah bagaimana Yealink bisa membuat firmware codec OPUS di perangkat low end seperti kelas T19 ini. Love it!

[Tips] Problem Mengaktifkan Kamailio TLS Dengan LetsEncrypt

Jadi barusan nyoba mengaktifkan TLS di SIP server Kamailio saya dengan LetsEncrypt. Pada intinya instalasi sertifikat dengan certbot-auto cukup mudah. Kali ini tinggal menambahkan konfigurasi di tls.cfg di dalam direktori konfigurasi Kamailio:

[server:default]
method = TLSv1
verify_certificate = no
require_certificate = no
certificate = /etc/letsencrypt/live/domain.tld/fullchain.pem
private_key = /etc/letsencrypt/live/domain.tld/privkey.pem

Lalu di file kamailio.cfg SIP server (ip address 192.168.0.123) edit/tambahkan baris berikut:

enable_tls = yes
listen=192.168.0.123:5061

####### TLS Parameters #########
loadmodule "tls.so"
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "low_mem_threshold1", 0)

Ketika mencoba reload Kamailio, saya mendapati error seperti berikut:

kamailio[18322]: ERROR: tls [tls_domain.c:529]: load_cert(): TLSs: Unable to load certificate file '/etc/letsencrypt/live/domain.tld/fullchain.pem'
kamailio[18322]: ERROR: tls [tls_util.h:42]: tls_err_ret(): load_cert:error:0200100D:system library:fopen:Permission denied
kamailio[18322]: ERROR: tls [tls_util.h:42]: tls_err_ret(): load_cert:error:20074002:BIO routines:FILE_CTRL:system lib
kamailio[18322]: ERROR: tls [tls_util.h:42]: tls_err_ret(): load_cert:error:140DC002:SSL routines:SSL_CTX_use_certificate_chain_file:system lib
kamailio[18322]: ERROR:  [core/sr_module.c:923]: init_mod_child(): Error while initializing module tls (/usr/lib64/kamailio/modules/tls.so)

f
Karena ada indikasi error yang disebabkan kepemilikan file cert, perlu diubah hak akses file dengan langkah berikut:

root# chmod go+x /etc/letsencrypt/archive
root# chmod go+x /etc/letsencrypt/live

catatan: gunakan sudo juga bukan login sebagai root.

Setelah itu dicoba kembali reload Kamailio dan masalah sudah tidak muncul lagi. Untuk check ,apakah port sudah listen ke TLS, jalankan:

root# ss -ln | egrep "5061"

Links:

[Tips] “sngrep” Sebagai Pengganti “ngrep”

Kadang satu atau dua server yang sedang saya tangani tidak selalu bisa terambung ke monitoring Homer. Jadi untuk troubleshoot masih harus meminta bantuan ngrepHanya saja perintahnya cukup panjang dan agak susah merunut ketika trafik sedang ramai. Belakangan nemu tool yang sebenarnya berbasis ngrep, hanya memang dikhususkan untuk trafik SIP, namanya sngrep

Di Yum packaging Centos 7 saat ini belum ada, jadi harus menambahkan file /etc/yum.repos.d/irontec.repo dengan isi:

[irontec]
name=Irontec RPMs repository
baseurl=http://packages.irontec.com/centos/$releasever/$basearch/

tambahkan public key:

rpm --import http://packages.irontec.com/public.key

jalankan update dan install:

[root@aio ~]# yum check-update -y
[root@aio ~]# yum install sngrep -y

Untuk menjalankan cukup ketik: sngrep

Dari tabel di atas kita bisa menggerakkan highlight ke atas dan ke bawah untuk memilih barus transaksi yang hendak dilihat. Misal hendak melihat salah satu proses Register, turunkan highlight ke daftar Method Register lalu tekan enter:

Contoh dialog/transaksi REGISTER yang berhasil dan tidak berhasil seperti berikut:

Lebih enak liatnya kan?

Ada kalanya gambar garis transaksi dengan anak panah di atas muncul sebagai rentetan huruf q seperti: qqqqqqqqqqqqqqqqqqqqqqqqq>
hal ini bisa diatasi dengan menjalankan perintah:

[root@aio ~]# export NCURSES_NO_UTF8_ACS=1

Agar tidak mengulang perintah tiap kali kita login, tambahkan baris berikut di /root/.bash_profile atau di dalam /etc/environment :

export NCURSES_NO_UTF8_ACS=1

Setiap kali login ke username yang sama, maka perintah tersebut akan otomatis dieksekusi.
Selamat mencoba!

[TIPS] Problem Kamailio “contact uri is too long”

Suatu hari saya menemukan masalah Mobile Dialer tidak dapat register ke SIP server kecuali port SIP client diganti ke 5060. Saya pikir firewall SIP server memblokir akses SIP port ke client dari server. Tapi ternyata, saya mendapati error di SIP server sebagai berikut:

kamailio[7968]: WARNING: registrar [sip_msg.c:194]: check_contacts(): contact uri is too long: [sip:021xxxxxxxx@11.2.33.444:48573;app-id=123412341234;pn-type=firebase;pn-tok=fnszKs2QQoA:AbCdEfGHIJkLmNoPQRsTuVwXyZ_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;pn-silent=1;transport=udp]

Secara default panjang maksimal SIP contact URI kamailio diset 255. Kalo kondisi normal sebenarnya baik-baik saja. Tapi kebetulan saya mengaktifkan Google Firebase Cloud Messaging sehingga contact URI menjadi lebih panjang… daaaaann… karena port SIP saya setting random, ada kalanya port SIP client menjadi sepanjang 5 digit. Setelah dihitung, panjang contact URI akan pas 255 kalau port nya 4 digit seperti 5060. Kalo pas kebetulan dapet 48573 seperti di log, akan kelebihan satu digit. Akhirnya di config Kamailio saya tambahkan baris:

modparam("registrar", "contact_max_size", 500)

Lalu setelah reload, akhirnya mobile dialer bisa register dengan lancar