Tips & Tricks: Using serial forking based on q-value with Sip:provider

Sipwise Sip:Provider (aka NGCP) allows you to register multiple devices under the same subscriber. By the default, the maximum number of the device you can register is 5, and this value is configurable via config.yml.

If a customer registers multiple devices, NGCP – once receives a call for that user – just send the call to all the registered devices, in parallel. All the devices will ring at the same time. This is called Parallel Forking, and this is the default behaviour. But what NGCP can also do is the so-called Serial Forking, which means let ring one device first, then after a timeout let ring the next device, and so on and so forth. The Serial Forking feature has been introducing since version mr3.3.1, and it could be quite interesting to have it, but maybe not all of the NGCP users know how it works.

Serial Forking

Serial Forking just calls the registered devices in serial, one after the another. But how ? How can I tell the system which one I want to call as first, and which one as second ?

Serial Forking is based on SIP Contact’s parameter called q-value, which is basically a priority number, set by the clients during their Registration.

The q value is a floating point number in a range 0 to 1.0 specify as the parameter in the Contact header field. The higher the q value number, the more priority that device has. Contacts with q value 1.0 have maximum priority, so such contacts will be always tried first in serial forking. Contacts with q value 0 have the lowest priority and they will be tried after all other contacts with higher priority.

Here an example of Contact with q value set to 0.9:

Contact: <sip:johdoe@sip.domain.com:5060;transport=udp>;expires=3600;q=0.9

So, q-value is set by the client during the Registration process.

In most of the cases clients don’t set q value and do not allow you to set it, so NGCP just set a default value of ‘q=-1’ in the database, which means basically it’s not going to use it and it’s not going to perform any serial forking.
As you may understand, there is a big problem at this point.

What’s the problem with q-value ?

The majority of SIP Phones out there don’t allow you to set q-value! Which means, you can NOT use serial forking in NGCP! This is a pity, but frankly, there is nothing NGCP can do to avoid that, this is a Client feature, and SIP clients don’t have it.

How to use Serial Forking in NGCP

But with some tricks, we can handle this, anyway…

What we are suggesting here is a way to set q-value on your client and use Serial Forking. Clients don’t allow us to set q-value at all, but there is another header that almost all clients allow you to set, and this is the User-Agent header field.

So the idea is to set the q-value in the User-Agent header field of your phones, then parse it on NGCP and use it as real q-value for your Serial Forking! Let’s see how to do it.

Setting q-value in User-Agent header

I will take as an example the SIP Phone CISCO SPA502g. But this is possible with all the CISCO SPA50X and with other brands.

If you access the phone web interface, just click on ‘Admin Login’ and then ‘Advanced’ on the top-right.

Now go into ‘SIP’ section and look for the field ‘SIP Reg User Agent Name:‘.

Just set there the following string, ‘$VERSION;q=0.9‘. $VERSION is the internal variable used by default as standard User Agent name, and it looks like ‘Cisco/SPA502G-7.4.9c’, but with our additional parameter it will look like ‘Cisco/SPA502G-7.4.9c;q=0.9‘.

Handling q-value on NCGCP

Now, on NGCP we are going to add the following section in our /etc/ngcp-config/templates/etc/kamailio/lb/kamailio.cfg.customtt.tt2 file:


xlog("L_NOTICE", "New request - M=$rm R=$ru F=$fu T=$tu IP=$pr:$si:$sp ID=$cin");
setflag(FLAG_OUTBOUND);
}


### Patch ### Read q-value from User-Agent header and use it.
if(is_method("REGISTER") && is_present_hf("Contact"))
{
$var(qval)=$(hdr(User-Agent){param.value,q});
if($var(qval) != $null)
{
xlog("L_NOTICE", "q-value set by the client is q=$var(qval) - R=$ru ID=$cin");
$var(newct) = $ct + ';q=' + $var(qval);
remove_hf("Contact");
append_hf("Contact: $(var(newct))rn");
}
}
### end Patch ###

then apply the changes.

This part will read the q value in the User-Agent header – if exist – and append the value in the contact header.
So, for example, subscriber John Doe has two phones, CISCO phone 1 with q=0.9 in his User-Agent header (source IP 1.1.1.1), and CISCO phone 2 with set q=0.1 (source IP 2.2.2.2).
John will register both devices, and the situation will be the following:


mysql> select username,domain,contact,q from kamailio.location where username='johndoe';
+----------+-------------+-------------------------------+-------+
| username | domain      | contact                       | q     |
+----------+-------------+-------------------------------+-------+
| johndoe  | sip.dom.com | sip:johndoe@1.1.1.1:5060      | 0.90  |
| johndoe  | sip.dom.com | sip:johndoe@2.2.2.2:5060      | 0.10  |
+----------+-------------+-------------------------------+-------+

Now, in order to enable Serial Forking for subscriber John Doe, we just need to go into Subscriber’s Preferences, section ‘Internals’ and enable “serial_forking_by_q_value”.

Every call – now – to John Doe will let ring CISCO Phone 1 first, and then phone 2, in serial.

Conclusion

If you are able to set a new SIP header field on your client, it would be much better, so you can use a dedicated header for that, example “X-q-value: q-value header;q=xx”.

You just need to parse header ‘X-q-value’ instead of ‘User-Agent’ in the Kamailio patch above.

Also, another solution could be to set a static registration (if you know the devices IPs) and then set the q-value manually in the DB and then restart Kamailio proxy to load the change on Kamailio memory as well.