In other parts of the world it’s known as a telephone test set, lineman’s handset, test phone, etc, but to me it’s a butt / butt set / buttinski.
They’re essentially ruggedized, portable telephones, often with an ability to monitor a line without looping it / going off hook, and used by techs and lineys throughout phone networks everywhere.
I carry Fluke TS52 Pro in my toolbox, it’s got a built in voltmeter, waterproof, backlit display and lots of memory storage options.
It’s a really beautiful bit of kit, (only thing missing is a TDR which the next model up has) but I very rarely use it.
The butt set is in my mind the quintessential piece of test gear, and I’ve got a few from various time periods.
The Telecom Ruggabut was launched in 1994/1995 and was standard issue prior to privatization, and was designed in Australia for the Australian market,
As such it features some uniquely Australian features such as detection for 12kHz Subscriber Pulse Metering (used in Payphones), while the “TLB” Button is tone-loop-break, a 100ms pause in dialling,
Prior to the Ruggabutt there was the Versadial and Versadial Mk2. Lightweight, tough and with a handy RJ12 jack for testing subscriber handpieces, these were made in huge numbers, by PMG and then Telecom.
And as far back as my “collection” goes is the Australian Post Office (APO) Telephone Test Handset No. 4, which lives on the Step exchange in my office, and is a simple rotary dial plus speaker, mic, switch and Neon light to denote ringing.
I had a discussion with a friend the other day about if hold is signified with a=sendonly or a=recvonly, which led me to revisiting the RFC to confirm, so here’s an overview of how “Call Hold” works in SIP.
By the Book
According to RFC 6337 a user can hold calls by sending a new SDP offer in an established session (Re-INVITE on active call), with an SDP payload of a=sendonly for each media stream the user want’s to hold.
The SIP Switch / PBX / UAS replies with an updated SDP where each media stream’s SDP contains a=recvonly.
So it’s both, depending on which leg you’re looking at.
In Common Practice
When a UAC puts a call in hold, it does so by sending a SIP re-INVITE, updating the SDP to include the attribute line “sendonly”
See the bottom line of the SDP is a=sendonly ? That’s denoting the call is to be put on hold,
If the call hold was sucesful the UAS sends back a 200 Ok, with the SDP attribute set to recvonly
The a=recvonly denotes the call has been held.
To retrieve the call another SIP re-invite is sent by the UAC, this time setting the media attribute back to sendrecv
If sucesful a 200 OK is sent by the UAS with the a=sendrecv also set.
Most people think of SIP when it comes to FreeSWITCH, Asterisk and Kamailio, but all three support WebRTC.
FreeSWITCH makes WebRTC fairly easy to use and treats it much the same way as any SIP endpoint, in terms of registration and diaplan.
Setting up the SIP Profile
On the SIP profile we’ll need to activate WebRTC you’ll need to ensure a few lines of config are present:
<!-- for sip over secure websocket support -->
<!-- You need wss.pem in $${certs_dir} for wss or one will be created for you -->
<param name="wss-binding" value=":7443"/>
Next you’ll need to restart FreeSWITCH and a self-signed certificate should get loaded,
Once you’ve restarted FreeSWITCH will fail to detect any WebSocket certificate and generate a self signed certificate for you. This means that you can verify FreeSWITCH is listening as expected using Curl:
curl https://yourhostname:7443 -vvv
You should see an error regarding the connection failing due to an invalid certificate, if so, great! Let’s put in a valid certificate.
If not double check the firewall on your server allow traffic to port TCP 7443,
Loading your TLS Certificate
WebRTC & websocket are recent standards – this means a valid TLS certificate is mandatory. So to get this to work you’ll need a valid SSL certificate.
When we restarted FreeSWITCH after adding the wss-binding config a certificate was automatically generated in the $${certs_dir} of FreeSWITCH,
You can verify where the certs_dir is by echoing out the variable in FreeSWITCH:
fs_cli -x 'eval $${certs_dir}'
Unless you’ve changed it you’ll probably find your certs in /etc/freeswitch/tls/
The certificate and private key are stored in a single file, with the Certificate and the Private Key appended to the end,
In my case the certificate is called “webrtc.pem” and the private key file is “webrtc-key.pem”,
I’ll need to start by replacing the contents of the current certificate/ key file wss.pem with the certificate I’ve got webrtc.pem, and then appending the private key – webrtc-key.pem to the end of wss.pem,
We’ll start by clicking the “Export Mode” button to set our wss:// URL;
Change the WebSocket Server URL to the URL of your FreeSWITCH instance (you must use a domain, not an IP Address)
If you’re running behind a NAT adding ICE servers is probably a good idea, although this will slow down connection times, you can use Google’s public STUN server by pasting in the below value:
[{ url: 'stun:stun.l.google.com:19302'}]
Finally we’ll save those settings and return back to the main tab,
You’ll need to register with a username and password that’s valid on the FreeSWITCH box, in my case I’m using 1000 with the password 1000 (exists by default),
Replace webrtc with the domain name of your FreeSWITCH instance,
Finally you should be able to click Login and see Connected above,
Then we can make calls to endpoints on FreeSWITCH using the dial box;
The Debug console in your browser will provide all the info you need to debug any issues, and you can trace WebSocket traffic using Sofia like any other SIP traffic.
Hopefully this was useful to you – I’ll cover more of WebRTC on Asterisk and also Kamailio in later posts!
It’s a seemingly simple question, the answer to which is – however you want, sorry if that’s not a simple answer.
I’ve talked about the strengths and weaknesses of Kamailio and Asterisk in my post Kamailio vs Asterisk, so how about we use them to work together?
The State of Play
So before we go into the nitty gritty, let’s imagine we’ve got an Asterisk box with a call queue with Alice and Bob in it, set to ring those users if they’re not already on a call.
Each time a call comes in, Asterisk looks at who in the queue is not already on a call, and rings their phone.
Now let’s imagine we’re facing a scenario where the single Asterisk box we’ve got is struggling, and we want to add a second to share the load.
We do it, and now we’ve got two Asterisk boxes and a Kamailio load balancer to split the traffic between the two boxes.
Now each time a call comes in, Kamailio sends the SIP INVITE to one of the two Asterisk boxes, and when it does, that Asterisk box looks at who is in the queue and not already on a call, and then rings their phone.
Let’s imagine a scenario where a Alice & Bob are both on calls on Asterisk box A, and another call comes in this call is routed to Asterisk box B. Asterisk box B looks at who is in the queue and who is already on a call, the problem is Alice and Bob are on calls on Asterisk box A, so Asterisk box B doesn’t know they’re both on a call and tries to ring them.
We have a problem.
Scaling stateful apps is a real headache,
So have a good long hard think about how to handle these issues before going down this path!
Recently I’ve been working on a few projects with FreeSWITCH, and looking at options for programmatically generating dialplans, instead of static XML files.
Why not Static XML?
So let’s say I define a static XML dialplan.
It works great, but if I want to change the way a call routes I need to do it from the dialplan,
That’s not ideal if you’re using a distributed cluster of FreeSWITCH instances, or if you want to update on the fly.
Static XML means we have to define our dialplan when setting up the server, and would have to reconfigure the server to change it.
So what about mod_xml_curl?
When I’ve done this in the past I’ve relied on the mod_xml_curl module.
mod_xml_curl gets the XML dialplan using Curl from a web server, and so you setup a web server using Flask/PHP/etc, and dynamically generate the dialplan when the call comes in.
This sounds good, except you can’t update it on the fly.
mod_xml_curl means call routing decisions are made at the start of the call, and can’t be changed midway through the call.
So what’s ESL?
ESL is the Event Socket Library, essentially a call comes in, an ESL request is made to an external server.
For each step in the dialplan, an ESL request will be sent to the external server which tells it to do,
ESL allows us to use all FreeSWITCH’s fantastic modules, without being limited as to having to perform the call routing logic in FreeSWITCH.
So how do I use ESL?
You’ll need two create an ESL server,
Luckily there’s premade examples for popular languages;
Sometimes you need Kamailio to serve as a User Agent Client, we covered using UAC to send SIP REGISTER messages and respond with the authentication info, but if you find you’re getting 401 or 407 responses back when sending an INVITE, you’ll need to use the UAC module, specifically the uac_auth() to authenticate the INVITE,
When Kamailio relays an INVITE to a destination, typically any replies / responses that are part of that dialog will go back to the originator using the Via headers.
This would be fine except if the originator doesn’t know the user name and password requested by the carrier, but Kamailio does,
Instead what we need Kamailio to do is if the response to the INVITE is a 401 Unauthorised Response, or a 407 Proxy Authentication required, intercept the request, generate the response to the authentication challenge, and send it to the carrier.
To do this we’ll need to use the UAC module in Kamailio and set some basic params:
Before we can call the t_relay() command, we need to specify a failure route, to be called if a negative response code comes back, we’ll use one called TRUNKAUTH and tell the transaction module that’s the one we’ll use by adding t_on_failure(“TRUNKAUTH”);
What we’ve done is specified to rewrite the destination URI to sip.nickvsnetworking.com, if the request type is an INVITE, it’ll load a failure route called TRUNKAUTH and proxy the request with the transaction module to sip.nickvsnetworking.com.
What we get is a 401 response back from our imaginary carrier, and included in it is a www-auth header for authentication.
To catch this we’ll create an on failure route named “TRUNKAUTH”
failure_route[TRUNKAUTH] {
xlog("trunk auth");
}
We’ll make sure the transaction hasn’t been cancelled, and if it has bail out (no point processing subsequent requests on a cancelled dialog).
failure_route[TRUNKAUTH] {
xlog("trunk auth");
if (t_is_canceled()) {
exit;
}
And determine if the response code is a 401 Unauthorised Response, or a 407 Proxy Authentication required (Authentication requests from our upstream carrier):
failure_route[TRUNKAUTH] {
xlog("trunk auth");
if (t_is_canceled()) {
exit;
}
xlog("Checking status code");
if(t_check_status("401|407")) {
xlog("status code is valid auth challenge");
}
}
Next we’ll define the username and password we want to call upon for this challenge, and generate an authentication response based on these values using the uac_auth() command,
failure_route[TRUNKAUTH] {
xlog("trunk auth");
if (t_is_canceled()) {
exit;
}
xlog("Checking status code");
if(t_check_status("401|407")) {
xlog("status code is valid auth challenge");
$avp(auser) = "test";
$avp(apass) = "test";
uac_auth();
}
}
Then finally we’ll relay that back to the carrier with our www-auth header populated with the challenge response;
These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.
The Registration-Termination Request / Answer allow a Diameter Client (S-CSCF) to indicate to the HSS (Diameter Server) that it is no longer serving that user and the registration has been terminated.
Basics:
The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:
The Registration-Termination-Request is sent by a Diameter Multimedia server to a Diameter Multimedia client in order to request the de-registration of a user.
Reference: TS 29.229
The Registration-Termination-Request commands are sent by a S-CSCF to indicate to the Diameter server that it is no longer serving a specific subscriber, and therefore this subscriber is now unregistered.
There are a variety of reasons for this, such as PERMANENT_TERMINATION, NEW_SIP_SERVER_ASSIGNED and SIP_SERVER_CHANGE.
The Diameter Server (HSS) will typically send the Diameter Client (S-CSCF) a Registration-Termination-Answer in response to indicate it has updated it’s internal database and will no longer consider the user to be registered at that S-CSCF.
Packet Capture
I’ve included a packet capture of these Diameter Commands from my lab network which you can find below.
These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.
The Diameter User-Authorization-Request and User-Authorization-Answer commands are used as the first line of authorization of a user and to determine which Serving-CSCF to forward a request to.
Basics
When a SIP Proxy (I-CSCF) receives an incoming SIP REGISTER request, it sends a User-Authorization-Request to a Diameter server to confirm if the user exists on the network, and which S-CSCF to forward the request to.
When the Diameter server receives the User-Authorization-Request it looks at the User-Name (1) AVP to determine if the Domain / Realm is served by the Diameter server and the User specified exists.
Assuming the user & domain are valid, the Diameter server sends back a User-Authorization-Answer, containing a Server-Capabilities (603) AVP with the Server-Name of the S-CSCF the user will be served by.
I always find looking at the packets puts everything in context, so here’s a packet capture of both the User-Authorization-Request and the User-Authorization-Answer.
Wireshark display of User-Authorization-Request packet
Wireshark display of User-Authorization-Answer packet
First Registration
If this is the first time this Username / Domain combination (Referred to in the RFC as an AOR – Address of Record) is seen by the Diameter server in the User-Authorization-Request it will allocate a S-CSCF address for the subscriber to use from it’s pool / internal logic.
The Diameter server will store the S-CSCF it allocated to that Username / Domain combination (AoR) for subsequent requests to ensure they’re routed to the same S-CSCF.
The Diameter server indicates this is the first time it’s seen it by adding the DIAMETER_FIRST_REGISTRATION (2001) AVP to the User-Authorization-Answer.
Subsequent Registration
If the Diameter server receives another User-Authorization-Request for the same Username / Domain (AoR) it has served before, the Diameter server returns the same S-CSCF address as it did in the first User-Authorization-Answer.
It indicates this is a subsequent registration in much the same way the first registration is indicated, by adding an DIAMETER_SUBSEQUENT_REGISTRATION (2002) AVP to the User-Authorization-Answer.
User-Authorization-Type (623) AVP
An optional User-Authorization-Type (623) AVP is available to indicate the reason for the User-Authorization-Request. The possible values / reasons are:
Creating / Updating / Renewing a SIP Registration (REGISTRATION (0))
Establishing Server Capabilities & Registering (CAPABILITIES (2))
Terminating a SIP Registration (DEREGISTRATION (1))
If the User-Authorization-Type is set to DEREGISTRATION (1) then the Diameter server returns the S-CSCF address in the User-Authorization-Answer and then removes the S-SCSF address it had associated with the AoR from it’s own records.
These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.
The Server-Assignment-Request/Answer commands are used so a SIP Server can indicate to a Diameter server that it is serving a subscriber and pull the profile information of the subscriber.
Basics:
The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:
The main functions of the Diameter SAR command are to inform the Diameter server of the URI of the SIP server allocated to the user, and to store or clear it from the Diameter server.
Additionally, the Diameter client can request to download the user profile or part of it.
The Server-Assignment-Request/Answer commands are sent by a S-CSCF to indicate to the Diameter server that it is now serving a specific subscriber, (This information can then be queried using the Location-Info-Request commands) and get the subscriber’s profile, which contains the details and identities of the subscriber.
Typically upon completion of a successful SIP REGISTER dialog (Multimedia-Authentication Request), the SIP Server (S-CSCF) sends the Diameter server a Server-Assignment-Request containing the SIP Username / Domain (referred to as an Address on Record (SIP-AOR) in the RFC) and the SIP Server (S-CSCF)’s SIP-Server-URI.
The Diameter server looks at the SIP-AOR and ensures there are not currently any active SIP-Server-URIs associated with that AoR. If there are not any currently active it then stores the SIP-AOR and the SIP-Server-URI of the SIP Server (S-CSCF) serving that user & sends back a Server-Assignment-Answer.
For most request the Subscriber’s profile is also transfered to the S-SCSF in the Server-Assignment-Answer command.
SIP-Server-Assignment-Type AVP
The same Server-Assignment-Request command can be used to register, re-register, remove registration bindings and pull the user profile, through the information in the SIP-Server-Assignment-Type AVP (375),
Common values are:
NO_ASSIGNMENT (0) – Used to pull just the user profile
The Cx-User-Data profile contains the subscriber’s profile from the Diameter server in an XML formatted dataset, that is contained as part of the Server-Assignment-Answer in the Cx-User-Data AVP (606).
The profile his tells the S-CSCF what services are offered to the subscriber, such as the allowed SIP Methods (ie INVITE, MESSAGE, etc), and how to handle calls to the user when the user is not registered (ie send calls to voicemail if the user is not there).
There’s a lot to cover on the user profile which we’ll touch on in a later post.
These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.
The Location-Information-Request/Answer commands are used so a SIP Server query a Diameter to find which P-CSCF a Subscriber is being served by
Basics:
The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:
The Location-Info-Request is sent by a Diameter Multimedia client to a Diameter Multimedia server in order to request name of the server that is currently serving the user.Reference: 29.229-
The Location-Info-Request is sent by a Diameter Multimedia client to a Diameter Multimedia server in order to request name of the server that is currently serving the user.
Reference: TS 29.229
The Location-Info-Request commands is sent by an I-CSCF to the HSS to find out from the Diameter server the FQDN of the S-CSCF serving that user.
The Public-Identity AVP (601) contains the Public Identity of the user being sought.
Here you can see the I-CSCF querying the HSS via Diameter to find the S-CSCF for public identity 12722123
The Diameter server sends back the Location-Info-Response containing the Server-Name AVP (602) with the FQDN of the S-CSCF.
Packet Capture
I’ve included a packet capture of these Diameter Commands from my lab network which you can find below.
So we’ve got a functional network, but let’s dive deeper into what we can do to connect it with other networks and how things work in “the real world”.
Media Handling – OsmoMGW
The Audio/Voice (media stream) data on a call between subscribers does not go directly between the subscribers and instead needs to be proxed relayed. The reason for this is because there’s no direct link from one BTS to another BTS (even if they are served by the same BSC) and as our subscribers can move from cell to cell while on a call – which may mean moving from one BSC to another depending on where they’re heading – we need to have a single point for the audio to remain.
To handle this a Media Gateway is used, a single point for call audio to be “anchored” – meaning regardless of which BTS/BSC is serving the subscribers on either end of the call, the media will be sent by both parties to a single destination (The Media Gateway), and that destination will send the audio to the other party.
The Media gateway relays / proxies the Media Stream – the RTP packets containing the call audio. OsmoMSC provides the SDP payload containing the codecs and RTP details for the session via MGCP (Media Gateway Control Protocol) to the OsmoMGW which relays the audio.
In terms of running osmo-mgw we installed it earlier,
The only parameter you really need to change is the rtp bind-ip,
On the MGW you can also limit and restrict the codecs supported and also allow or disallow transcoding.
MNCC-SAP & Call Routing
In it’s default mode, the OsmoMSC will look at the destination a call is being sent to, and if the destination is a subscriber on the network (in it’s VLR), will route the call to that subscriber
This on-net only mode is great but it puts our network on an island – cut off from the outside world.
Calls between MSCs, to the PSTN and users everywhere else are not possible in this scenario.
3GPP defined “MNCC-SAP” (Mobile Network Call Control – Service Access Point) a protocol for handling calls to/from destinations outside of the local MSC.
When in MNCC-SAP mode all calls (even on-net calls between subscribers on the same MSC) are routed to the external MNCC-SAP, and left up to it to determine how to route the call.
Configuring Osmo-MSC to talk MNCC
As we just covered by default Osmo-MSC only switches calls internally between subscribers, so we’ll need to turn off this behaviour and isntead reconfigure it to talk MNCC-SAP.
To do this we’ll telnet / VTY into Osmo-MSC;
root@gsm-bts:/etc/osmocom# telnet localhost 4254
Welcome to the OsmoMSC VTY interface
OsmoMSC - Osmocom Circuit-Switched Core Network implementation
OsmoMSC> enable
OsmoMSC# configure terminal
OsmoMSC(config)# msc
OsmoMSC(config-msc)# mncc external /tmp/msc_mncc
OsmoMSC(config-msc)# end
OsmoMSC# cop run st
Configuration saved to /etc/osmocom/osmo-msc.cfg
After making this change we have to restart OsmoMSC;
systemctl restart osmo-msc
Now OsmoMSC will not switch calls locally, but instead when a mobile originated call comes to the MSC, it will signal to the external MNCC via the file sock at /tmp/msc_mncc,
MNCC-SAP sounds great but platform X only speaks SIP
Enter the Osmo-SIP-Connector, which takes the MNCC-SAP messages and converts them to SIP.
From here you can tie the call control functions of the MNC into any SIP software such as Kamailio, FreeSwitch, Asterisk, etc, to handle call routing, number translation, application services like voicemail and conferencing, etc, etc.
On my to-do list is to make a call between one subscriber on GSM and one on VoLTE, I’ll cover that in a subsequent post.
So anywho, let’s get Osmo-SIP-Connector setup, I’m running it on the same box as the MSC on 10.0.1.201, My softphone client is running on 10.0.1.252
root@gsm-bts:/etc/osmocom# apt-get install osmo-sip-connector
root@gsm-bts:/etc/osmocom# telnet localhost 4256
Welcome to the OsmoSIPcon VTY interface
OsmoSIPcon> enable
OsmoSIPcon# configure t
OsmoSIPcon(config)# mncc
OsmoSIPcon(config-mncc)# socket-path /tmp/msc_mncc
OsmoSIPcon(config-mncc)# exit
OsmoSIPcon(config)# sip
OsmoSIPcon(config-sip)# local 10.0.1.201 5060
OsmoSIPcon(config-sip)# remote 10.0.1.252 5060
OsmoSIPcon(config-sip)# end
OsmoSIPcon# cop run st
Configuration saved to /etc/osmocom/osmo-sip-connector.cfg
Now any Mobile Originated calls will result in a SIP INVITE being sent to 10.0.1.252 port 5060 (using UDP).
Any SIP INVITES where the request URI is a valid MSISDN @ 10.0.1.201 from 10.0.1.252 will be routed to the correct subscriber for that MSISDN.
A small note – The GSM codec is (unsurprisingly) used as the codec for GSM calls by default.
Some handsets support different codecs, but many off-the-shelf IP phones don’t include GSM support, so you may find you’re required to transcode between codecs if there is no support for the other codecs.
So now we’re able to define our call routing logic in something that speaks SIP and connect calls between multiple MSCs, VoLTE / IMS networks and fixed networks, all based on what we do with the SIP.
Local Call, Local Switch
If two subscribers on the same BSC call each other, the RTP / call audio will route to the MGW where it’s anchored.
This makes sense from a mobility standpoint, but adds load to the MGW and relies on a quality A interface connection, which may be an issue depending on what backhaul options you have.
Local Call, Local Switch is a 3GPP spec to allow the RTP / call audio to act as the RTP proxy instead of the MGW.
I wrote about using Ansible to automate Kamailio config management, Ansible is great at managing VMs or bare metal deployments, but for Containers using Docker to build and manage the deployments is where it’s at.
I’m going to assume you’ve got Docker in place, if not there’s heaps of info online about getting started with Docker.
The Dockerfile
The Kamailio team publish a Docker image for use, there’s no master branch at the moment, so you’ve got to specify the version; in this case kamailio:5.3.3-stretch.
Once we’ve got that we can start on the Dockerfile,
For this example I’m going to include
#Kamailio Test Stuff
FROM kamailio/kamailio:5.3.3-stretch
#Copy the config file onto the Filesystem of the Docker instance
COPY kamailio.cfg /etc/kamailio/
#Print out the current IP Address info
RUN ip add
#Expose port 5060 (SIP) for TCP and UDP
EXPOSE 5060
EXPOSE 5060/udp
Once the dockerfile is created we can build an image,
docker image build -t kamtest:0.1 .
And then run it,
docker run kamtest:0.1
Boom, now Kamailio is running, with the config file I pushed to it from my Dockerfile directory,
Now I can setup a Softphone on my local machine and point it to the IP of the Docker instance and away we go,
Where the real power here comes in is that I can run that docker run command another 10 times, and have another 10 Kamailio instannces running.
Tie this in with Kubernetes or a similar platform and you’ve got a way to scale and manage upgrades unlike anything you’d get on Bare Metal or VMs.
I had a few headaches getting the example P-CSCF example configs from the Kamailio team to run, recent improvements with the IPsec support and code evolution meant that the example config just didn’t run.
So, after finally working out the changes I needed to make to get Kamailio to function as a P-CSCF, I took the plunge and made my first pull request on the Kamailio project.
A lot of people think there’s a one-to-one relationship between a registration Address on Record, and a username.
That doesn’t have to be the case, there are some platforms that only allow a single registration for a single username, but the RFC itself allows multiple registrations for a single username.
REGISTER requests add, remove, and query bindings.
A REGISTER request can add a new binding between an address-of-record and one or more contact addresses.
Registration on behalf of a particular address-of-record can be performed by a suitably authorized third party.
A client can also remove previous bindings or query to determine which bindings are currently in place for an address-of-record.
Let’s say you’ve got a SIP phone on your desk at the office and at home.
What we could do is create a different username and password for home & work, and then setup some time based forward rules to ring the office from 9-5 and home outside of that.
You could register both with the same username and password, and then unplug the one at home before you leave to work, get to work, plug in your office phone, unplug it before you leave to go home, and when you get home plug back in your home phone, or if multi-device registration is supported, register both and have incoming calls ring on both.
Admittedly, platforms that support this are the exception, not the rule, but the RFC does allow it.
The other little known feature in SIP Registration is that you can query the SIP Registrar to get the list of Addresses on Record.
So there you go, factoids about SIP REGISTER method!
Today I thought I’d cover a simple use case – running an HTTP get from Kamailio and doing something with the output.
The http_client does what it sounds – Acts as an HTTP client to send HTTP GET and POST requests.
The use cases for this become clear quite quickly, you could use http_client to request credit from an accounting server via it’s API, get the latest rate to a destination from a supplier, pull weather data, etc, etc.
Let’s take a very simple example, we’ll load http_client by adding a loadmodule line:
...
loadmodule "http_client.so"
...
Next I’ve put together a very simple request_route block that requests a HTTP file from a web server and sends the response to a SIP client:
####### Routing Logic ########
/* Main SIP request routing logic
* - processing of any incoming SIP request starts with this route
* - note: this is the same as route { ... } */
request_route {
xlog("Got request");
http_client_query("https://nickvsnetworking.com/utils/curl.html", "", "$var(result)");
xlog("Result is $var(result)");
sl_reply("200", "Result from HTTP server was $var(result)");
}
Using the http_client_query() function we’re able to query a HTTP server,
We’ll query the URL https://nickvsnetworking.com/utils/curl.html and store the output to a variable called result.
If you visit the URL you’ll just get a message that says “Hello there“, that’s what Kamailio will get when it runs the http_client function.
Next we print the result using an xlog() function so we can see the result in syslog,
Finally we send a stateless reply with the result code 200 and the body set to the result we got back from the server.
We can make this a bit more advanced, using HTTP Post we can send user variables and get back responses.
The http_client module is based on the ubiquitous cURL tool, that many users will already be familiar with.
It’s probably pretty evident to most why you’d want to use TLS these days,
SIP Secure – aka sips has been around for a long time and is supported by most SIP endpoints now.
Kamailio supports TLS, and it’s setup is very straightforward.
I’ve got a private key and certificate file for the domain nickvsnetworking.com so I’ll use those to secure my Kamailio instance by using TLS.
I’ll start by copying both the certificate (in my case it’s cert.pem) and the private key (privkey.pem) into the Kamailio directory. (If you’ve already got the private key and certificate on your server for another application – say a web server, you can just reference that location so long as the permissions are in place for Kamailio to access)
Next up I’ll open my Kamailio config (kamailio.cfg), I’ll be working with an existing config and just adding the TLS capabilities, so I’ll add this like to the config:
!define WITH_TLS
That’s pretty much the end of the configuration in kamailio.cfg, if we have a look at what’s in place we can see that the TLS module loads it’s parameters from a separate file;
After restarting Kamailio subscribers can now contact us via TLS using sips.
You may wish to disable TCP & UDP transport in favor of only TLS.
A note about CAs…
If you’re planning on rolling out SIP over TLS (sips) to existing IP phones it’s worth looking at what Certificate Authorities (CAs) are recognised by the IP phones.
As TLS relies on a trust model where a CA acts kind of like a guarantor to the validity of the certificate, if the IP phone doesn’t recognise the CA, it may see the certificate as Invalid.
Some IP phones for example won’t recognize Let’s Encrypt certificates as valid, while others may not recognize any of the newer CAs.
If you’re running a Debian system, the Kamailio team provide nightly development builds as Debian packages that can be installed on Debian or Ubuntu systems using the apt package manager.
Installing is a breeze, first we just add the GPG key for the repo:
Despite the fact it’s 2020 there’s still a lot of folks in the world manually configuring boxes,
Ansible is a topic I could talk all day about, but in essence it’s kind of an automation framework, tell Ansible what to do one and it can spin you up two boxes, or two thousand boxes and manage the config on them.
I talked about DMQ, the Distributed Message Queue in a Kamailio Bytes post some time ago, and so as an example I’ll share an example playbook to Install Kamailio the lazy way from the Repos, and load the DMQ config with the IP Address and DMQ Address pulled from variables based on the host itself.
There’s a huge number of posts on installing and the basics of Ansible online, if you’re not familiar with Ansible already I’d suggest starting by learning the basics and then rejoining us.
The Hosts
Depending on if your hosts are running on bare metal, VMWare VMs or cloud based, I’m going to assume you’re working with a Debian system.
I’ve already got 3 servers ready to go, they’ve got sequential IP Addresses so I’ve added the range to my /etc/ansible/hosts file:
I’ve created the group kamailio and put the IP Address range 10.0.1.193 to 10.0.1.195 in there.
You will probably need to add the authentication info, such as passwords, private keys and privilege escalation details, but I’m going to assume you’ve already done that and you can run the ping module on each one:
ansible kamailio -m ping
Assuming that comes back OK and you can get into each one let’s move onto the Playbook.
The Playbook
There’s a few tasks we’ll get out of the way before we configure Kamailio,
The first of which is adding the Debian repo and the keys,
Next we’ll load a Kamailio config from a template that fills in our IP Address and Kamailio version, then we’ll install Kamailio,
Rather than talk you through each of the plays here’s a copy of my playbook:
---
- name: Configure Kamailio
hosts: kamailio
become: yes
vars:
kamailio_version: "53"
debian_sources_dir: "/etc/apt/sources.list.d"
tasks:
- name: Add keys for Kamailio repo
apt_key:
url: http://deb.kamailio.org/kamailiodebkey.gpg
state: present
- name: Add repo to sources.list
apt_repository:
repo: deb http://deb.kamailio.org/kamailio{{kamailio_version}} {{hostvars[inventory_hostname]['ansible_lsb']['codename']}} main
#The full list of Debian repos can be found at http://deb.kamailio.org/
#The version is based off the versions listed there and the release is based on the codename of the Debian / Ubuntu release.
state: present
- name: Copy Config Template
#Copies config from the template, fills in variables and uplaods to the server
template:
src: kamailio.cfg.j2
dest: /etc/kamailio/kamailio.cfg
owner: root
group: root
backup: yes
register: config_changed
- name: Install Kamailio
#Updates cache (apt-get update) and then installs Kamailio
apt:
name: kamailio
update_cache: yes
state: present
register: kamailio_installed_firstrun
- name: Restart Kamailio if config changed
service:
name: kamailio
state: restarted
when: config_changed.changed
- name: Start Kamailio if installed for the first time
service:
name: kamailio
state: started
when: kamailio_installed_firstrun.changed
Should be pretty straight forward to anyone who’s used Ansible before, but the real magic happens in the templatemodule. Let’s take a look;
Kamailio config in Jinja2 template
Pusing out static config is one thing, but things like IP Addresses, FQDNs and SSL certs may differ from machine to machine, so instead of just pushing one config, I’ve created a config and added some variables in Jinja2 format to the config, that will be filled with the value on the target when pushed out.
In the templatemodule of the playbook you can see I’ve specified the file kamailio.cfg.j2 this is just a regular Kamailio config file but I’ve added some variables, let’s look at how that’s done.
On the machine 10.0.1.194 we want it to listen on 10.0.1.194, we could put list 0.0.0.0 but this can lead to security concerns, so instead let’s specify the IP in the Jinja config,
By putting ansible_default_ipv4.address in two sets of curly brackets, this tells Ansible to fill in thes values from the template with the Ansible IPv4 Address of the target machine.
Let’s take a look on the 10.0.1.194’s actual kamailio.cfg file:
Let’s take another example,
To keep DMQ humming it makes sense to have different DMQ domains for different versions of Kamailio, so in the Kamailio config file template I’ve called the variable kamailio_versionin the DMQ address,
This means on a Kamailio 5.2 version this URL look like this on the boxes’ config:
Running it is just a simple matter of calling ansible-playbook and pointing it at the playbook we created, here’s how it looks setting up the 3 hosts from their vanilla state:
The great thing about Kamailio is it’s omnipotent – This means it will detect if it needs to do each of the tasks specified in the playbook.
So if we run this again it won’t try and add the repo, GPG keys, install Kamailio and load the template, it’ll look and see each of those steps have already been done and skip each of them.
But what if someone makes some local changes on one of the boxes, let’s look at what happens:
Likewise now if we decide to change our config we only need to update the template file and Ansible will push it out to all our machines, I’ve added a comment into the Kamailio template, so let’s run it again and see the config pushed out to all the Kamailio instances and them restarting.