As we get closer to the much-anticipated release of OpenSIPS 3.1 LTS, a new major feature has been merged into the master branch: full support for standards-based SIP Push Notifications (short: “SIP PN”), a.k.a. RFC 8599!
In this two-part series, I’m going to present two sides of the story: first, the IETF document itself: the problems it tackles and how it does it (part 1), followed by an OpenSIPS implementation write-up: the way we shaped it and how you can enable it (part 2).
RFC 8599 Explained
Throughout the last decade, mobile phone manufacturers have constantly striven to extend phone battery life as much as possible. And apart from improving the manufacturing process, keeping their Operating Systems and Applications under tight control so that battery waste is kept to a minimum is an equally effective way to achieve the same goal. And one of the first class of applications that they began to restrict were VoIP apps: with protocols such as SIP that required the phone to send periodic network packets to the VoIP service in order to remain registered, such applications were the first to be punished (a.k.a. moved in the background, without the possibility of waking up by themselves).
A backgrounded VoIP application typically cannot receive a call, nor can it register to the VoIP service by itself. However, we can awake it, on demand, by triggering a Push Notification (PN). This is the main idea behind RFC 8599: a standardized way for PN service providers to interoperate using SIP, despite having completely different PN services and APIs within each of their yards.
The entire document can be boiled down to 3 main ideas:
PN for Registration Refreshing
The 1st essential idea within the specification is that SIP User Agents are considered to be registered as long as their binding expiration interval is still due.
So no matter if the NAT binding in front of them has expired, or if their TCP connection is dead, a SIP PN proxy must not delete the binding. This isn’t quite what we’ve been used to up until now, because the accepted belief was that a user location binding should be deleted once its TCP connection goes down. In fact, this is actually a feature request on the OpenSIPS development tracker.
So how does the spec solve the problem of the TCP connection going down? Well… it doesn’t! Because we can force the device to re-register at any time, thus obtaining a fresh connection! For this, we just need to send a Push Notification to the device, so the VoIP app wakes up and re-registers. Now you have a refreshed SIP binding with a healthy TCP connection. Or, at least for the next 1 minute, provided that our user doesn’t get bored with their phone…
To conclude this chapter, as a SIP PN capable proxy, we must not let our user location bindings expire. For this, the proxy will periodically generate Push Notifications for the device, well within time (the recommendation is 120 seconds before binding expiration), in order to get it to re-register. This way, the device is always considered to be online and can receive calls.
But, speaking of calls…
PN for Dialog Establishment
What if the SIP registration is barely through its expiration interval but the app is closed (thus unreachable) and we have a call for this user? Welcome to the 2nd important idea behind the document: a SIP PN proxy must send a Push Notification to a PN-capable device and get it to re-register before routing a call to it.
Below is the SIP flow for the PN-equipped registration, followed by the incoming call:
Taking the above step-by-step:
- the device fetches its PN identifier (PRID: Push Resource IDentifier) from its service provider and includes it in the SIP REGISTER, so the PN proxy learns it dynamically. This will come in handy later.
- as an INVITE arrives for this device, the proxy places it in a waiting queue
- the proxy then sends a Push Notification to the device PRID, to get it to re-register and create a new TCP connection and/or open up the NAT pinhole
- as soon as the device re-registers, the INVITE is removed from the waiting queue and forwarded to it
Sounds complicated? Well, we’re just getting started…
PN for a Mid-Dialog Request
What if the SIP registration is barely through its expiration interval and although we woke up the app when we routed a call to it, the user spent enough time on the call that the app was put in background and we must now route a BYE (hang-up) from the callee side to our app? This whole battery-saving thing is turning into a real chore…
Enter the 3rd most important part of the spec: reachability issues during mid-dialog request routing are also solved by sending a PN beforehand! However, the SIP packets will require some key additions before the PN proxy is able to pinpoint the PN coordinates of the target device, as we’re about to find out.
First, here is the SIP flow we’re going to discuss:
Key differences from the “PN at initial INVITE” logic:
- during registration, the proxy advertises a Proxy-Unique Registration Reference (PURR) to the device, in the 200 OK reply. You can think of the PURR as the OpenSIPS “Contact ID”, if you are familiar with the concept.
- if a device expects to be awoken with a PN before a mid-dialog request can be sent to it, it must include its own PURR in the Contact header of the initial INVITE or the 200 OK, depending on the direction. Alice’s Contact becomes Bob’s mid-dialog BYE Request-URI and vice-versa, remember?
- the opposite-end device will now have no choice but to include this tag reference (PURR) in the Request-URI of all its mid-dialog requests. By strictly using the PURR, the proxy must be able to pinpoint the remote Contact (including its PN coordinates!), after which it will trigger the PN, wait for the registration and resume processing the mid-dialog request. Just like with the initial INVITE.
For mid-dialog requests, the PN coordinates of each device are anonymized, so the proxy must look up each Contact in order to obtain them. However, at this stage of SIP processing, the PN proxy is supposed to perform end-to-end loose routing, so essential look-up data is missing! To alleviate this issue, the proxy generates a PURR tag for each contact as it registers, which will eventually become a Contact URI parameter (“;pn-purr”). As such, the PURR will be easily visible in any mid-dialog Request-URI (or Route header field!). Using the PURR as a lookup key, a Contact can be searched and have its PN coordinates retrieved.
When implementing all of the above complexity in OpenSIPS 3.1, we had an end-goal of keeping the development of a SIP PN registrar as seamless as possible: quick to set up and easy to maintain afterwards. So almost all RFC 8599 logic is built into the registrar set of modules — you just need to enable it.
But we will talk in more detail about this in the next post. Stay safe!