Diameter Server Support for IMS Flows in OpenSIPS 3.5

IMS Network Architecture, source: researchgate.net

Intro

The OpenSIPS 3.5 release introduces support for OpenSIPS acting as a Diameter server, with the possibility of scripting generic Diameter Server interactions. This means that you will be able to add, tweak or remove the support for various Diameter requests by only re-deploying your opensips.cfg set of scripts (yes, opensips.cfg has include_file support) and restarting the OpenSIPS service.

In this post, we take a look at the required opensips.cfg scripting in order to achieve server-side support for a single command of the 3GPP’s IMS Diameter Cx/Dx interface (Diameter Application ID: 16777216), specifically the Push-Profile-Request (PPR) command, with code 305. The Cx/Dx interface in IMS is represented by Diameter protocol interactions (both Client->Server and Server->Client!) exclusively, between the HSS (Home Subscriber Server) and the S-CSCF (“Serving” Call Session Control Function), typically. The latest version of the spec at the time of writing is Release 17, covered by two ETSI-approved 3GPP documents: TS-129-228 and TS-129-229.

Push-Profile-Request

One of the first mentions of “Push-Profile-Request” when querying the webs points to the Diameter RFC 4740 § 8.11, where the command is listed with code 288. However, note that this command is part of the base “Diameter SIP Application”, with Diameter Application ID 6, per RFC 4740 § 13.1. In this blog, we will refer to 3GPP’s Push-Profile-Request specification and AVP structure, instead of its initial variant, dating all the back to the Diameter RFC (2006).

“The Push-Profile-Request (PPR) command, indicated by the Command-Code field set to 305 and the ‘R’ bit set in the Command Flags field, is sent by a Diameter Multimedia server to a Diameter Multimedia client in order to update the subscription data and for SIP Digest authentication the authentication data of a multimedia user in the Diameter Multimedia client whenever a modification has occurred in the subscription data or digest password that constitutes the data used by the client.

Extending the libfreeDiameter Dictionary

Before libfreeDiameter can successfully parse a custom Diameter request and pass it to the OpenSIPS application, it must match its AVP payload with a known Diameter Request format from its dictionary, which is loaded on startup.

So, the first step is to take a look at the Request format:

Message Format
< Push-Profile-Request > ::= < Diameter Header: 305, REQ, PXY, 16777216 >
< Session-Id >
[ DRMP ]
{ Vendor-Specific-Application-Id }
{ Auth-Session-State }
{ Origin-Host }
{ Origin-Realm }
{ Destination-Host }
{ Destination-Realm }
{ User-Name }
*[ Supported-Features ]
[ User-Data ]
[ Charging-Information ]
[ SIP-Auth-Data-Item ]
[ Allowed-WAF-WWSF-Identities ]
*[ AVP ]
*[ Proxy-Info ]
*[ Route-Record ]

(the AVPs marked with <> represent header AVPs (they always lead the AVP list and are mandatory). The ones marked with {} are mandatory, while the ones with [] are optional. Finally, the * denotes AVPs which may have 0 or more occurrences)

Let’s convert this into the equivalent dictionary.opensips format. Note that you must explicitly define any AVP which is not present in neither the standard dict_sip.fdx nor the dict_dcca_3gpp.fdx freeDiameter extensions (both extensions are available via the freediameter-extensions DEB package).

APPLICATION 16777216 Diameter Application for Cx Interface

REQUEST 305 Push-Profile-Request
{
Session-ID | FIXED_HEAD | 1
DRMP | OPTIONAL | 1
Vendor-Specific-Application-Id | REQUIRED | 1
Auth-Session-State | REQUIRED | 1
Origin-Host | REQUIRED | 1
Origin-Realm | REQUIRED | 1
Destination-Host | REQUIRED | 1
Destination-Realm | REQUIRED | 1
User-Name | REQUIRED | 1
Supported-Features | OPTIONAL | 100
User-Data | OPTIONAL | 1
Charging-Information | OPTIONAL | 1
SIP-Auth-Data-Item | OPTIONAL | 1
Allowed-WAF-WWSF-Identities | OPTIONAL | 1
}

Next, the Answer format:

< Push-Profile-Answer > ::= < Diameter Header: 305, PXY, 16777216 >
< Session-Id >
[ DRMP ]
{ Vendor-Specific-Application-Id }
[ Result-Code ]
[ Experimental-Result ]
{ Auth-Session-State }
{ Origin-Host }
{ Origin-Realm }
*[ Supported-Features ]
*[ AVP ]
[ Failed-AVP ]
*[ Proxy-Info ]
*[ Route-Record ]

… which we also append to our dictionary.opensips:

ANSWER 305 Push-Profile-Answer
{
Session-ID | FIXED_HEAD | 1
DRMP | OPTIONAL | 1
Vendor-Specific-Application-Id | REQUIRED | 1
Result-Code | OPTIONAL | 1
Experimental-Result | OPTIONAL | 1
Auth-Session-State | REQUIRED | 1
Origin-Host | REQUIRED | 1
Origin-Realm | REQUIRED | 1
Supported-Features | OPTIONAL | 100
Failed-AVP | OPTIONAL | 1
}

Integrating with opensips.cfg

At this point, the OpenSIPS built-in Diameter Server is ready to process 3GPP Push-Profile-Requests (305). For detailed explanations on the opensips.cfg part, please see the OpenSIPS Diameter Client/Server wiki tutorial. For our purpose, here are the relevant sections:

...
loadmodule "event_route.so"
loadmodule "json.so"
...

route [DM_CXDX_PPR]
{
$json(req_avps) := $param(2);

# 1. process the AVPs, e.g. update cache with new charging information

# 2. successful reply case
$var(ans_avps) = "[
{ \"Vendor-Specific-Application-Id\": [{
\"Vendor-Id\": 0
}] },

{ \"Result-Code\": 2001 },
{ \"Auth-Session-State\": 0 },
{ \"Origin-Host\": \"opensips.diameter.test\" },
{ \"Origin-Realm\": \"diameter.test\" }
]";

if (!dm_send_answer($var(ans_avps)))
xlog("ERROR - failed to send Diameter answer\n");
}

event_route [E_DM_REQUEST]
{
# error reply AVPs
$var(err_avps) = "[
{ \"Vendor-Specific-Application-Id\": [{
\"Vendor-Id\": 0
}] },

{ \"Result-Code\": 3001 },
{ \"Auth-Session-State\": 0 },
{ \"Origin-Host\": \"opensips.diameter.test\" },
{ \"Origin-Realm\": \"diameter.test\" }
]";

if ($param(app_id) != 16777216) {
xlog("ERROR: Unsupported Diameter request: $param(app_id) / $param(cmd_code) / $param(sess_id)\n");
dm_send_answer($var(err_avps), true);
exit;
}

switch ($param(cmd_code)) {
case 305:
route(DM_CXDX_PPR, $param(sess_id), $param(avps_json));
break;

default:
xlog("ERROR: Unsupported Diameter Cx/Dx command: $param(app_id) / $param(cmd_code) / $param(sess_id)\n");
dm_send_answer($var(err_avps), true);
exit;
}
}

And that concludes the tutorial! To find out more about the IMS related changes coming to OpenSIPS 3.5, as well as various other use cases shared by VoIP developers and integrators from the community, do consider booking some “business travel” days for the OpenSIPS Summit in Valencia, May 14-17!

Leave a comment