Login | Register

Documentation

Documentation -> Tutorials -> Diameter Client/Server

This page has been visited 307 times.

How to build and process Diameter requests using OpenSIPS scripting

by Liviu Chircu


1.  Setting up the freeDiameter libraries

This tutorial has been written for a Xubuntu 22.04 LTS, which comes with freeDiameter library v1.2.1 packages. Other distros, such as Debian 10, are also known to offer standard package-based support for freeDiameter v1.2.1, so they are expected to be compatible just as well.

sudo apt -y install libfreediameter-dev

2.  Installing the "aaa_diameter" OpenSIPS module

If you are building from source:

include_modules=aaa_diameter make install


Otherwise, if you are installing from packages, make sure to grab the opensips-diameter-module:

sudo apt -y install opensips-diameter-module

3.  Defining a Generic Diameter Request + Answer

While the freeDiameter library is very robust, this also means it will be very nitpicky with its "known" set of Diameter Applications and Commands. The set of currently known apps and commands (along with each command's precise AVP structure!) make up the library's dictionary, in freeDiameter terminology.


This means that before we can build and send a generic request, we must make sure to define the request in the freeDiameter dictionary. You can achieve this by attaching an ;extra-avps-file:<path-to-dictionary.opensips> option to the aaa_url modparam of aaa_diameter. In order to define Apps and Command AVP structure, we have extended the classic, AAA dictionary.opensips AVP definitions file to support this. See dm_send_request for full examples.


For our case, we only need the following in dictionary.opensips:


ATTRIBUTE Exponent          429 integer32
ATTRIBUTE Value-Digits      447 integer64

ATTRIBUTE Cost-Unit 424 grouped
{
	Value-Digits | REQUIRED | 1
	Exponent | OPTIONAL | 1
}

ATTRIBUTE Currency-Code     425 unsigned32

ATTRIBUTE Unit-Value  445 grouped
{
	Value-Digits | REQUIRED | 1
	Exponent | OPTIONAL | 1
}

ATTRIBUTE Cost-Information  423 grouped
{
	Unit-Value | REQUIRED | 1
	Currency-Code | REQUIRED | 1
	Cost-Unit | OPTIONAL | 1
}

APPLICATION 42 My Diameter Application

REQUEST 92001 My-Custom-Request
{
	Origin-Host | REQUIRED | 1
	Origin-Realm | REQUIRED | 1
	Destination-Realm | REQUIRED | 1
	Transaction-Id | REQUIRED | 1
	Sip-From-Tag | REQUIRED | 1
	Sip-To-Tag | REQUIRED | 1
	Acct-Session-Id | REQUIRED | 1
	Sip-Call-Duration | REQUIRED | 1
	Sip-Call-Setuptime | REQUIRED | 1
	Sip-Call-Created | REQUIRED | 1
	Sip-Call-MSDuration | REQUIRED | 1
	call_cost | REQUIRED | 1
	Cost-Information | OPTIONAL | 1
}

ANSWER 92001 My-Custom-Answer
{
	Origin-Host | REQUIRED | 1
	Origin-Realm | REQUIRED | 1
	Destination-Realm | REQUIRED | 1
	Transaction-Id | REQUIRED | 1
	Result-Code | REQUIRED | 1
}

4.  opensips.cfg: Sending Generic Diameter Requests

OpenSIPS 3.2 and above releases offer the option of building generic Diameter requests with full control over the AVPs of the request, as well as full access to the content of the response. Below is a short tutorial on how to send a Diameter request.


After making sure that aaa_diameter is loaded, start by building the request's AVP list as a JSON array:


log_stdout = yes
...
loadmodule "aaa_diameter.so"
modparam("aaa_diameter", "fd_log_level", 0)
modparam("aaa_diameter", "realm", "diameter.test")
modparam("aaa_diameter", "peer_identity", "server")
...


# Building an sending an My-Custom-Request (92001) for the
# My Diameter Application (42)
$var(payload) = "[
	{ \"Origin-Host\": \"client.diameter.test\" },
	{ \"Origin-Realm\": \"diameter.test\" },
	{ \"Destination-Realm\": \"diameter.test\" },
	{ \"Sip-From-Tag\": \"dc93-4fba-91db\" },
	{ \"Sip-To-Tag\": \"ae12-47d6-816a\" },
	{ \"Acct-Session-Id\": \"a59c-dff0d9efd167\" },
	{ \"Sip-Call-Duration\": 6 },
	{ \"Sip-Call-Setuptime\": 1 },
	{ \"Sip-Call-Created\": 1652372541 },
	{ \"Sip-Call-MSDuration\": 5850 },
	{ \"out_gw\": \"GW-774\" },
	{ \"cost\": \"10.84\" },
	{ \"Cost-Information\": [
		{\"Unit-Value\": [{\"Value-Digits\": 1000}]},
		{\"Currency-Code\": 35}
		]}
]";


... and, finally, send the request out (in a blocking fashion, for async support, see async dm_send_request()):


dm_send_request(42, 92001, $var(payload), $var(rpl_avps));


In this example, the Diameter Application ID is 42, and the Command's Code is 92001. Finally, the response AVPs are available under $var(rpl_avps). Just use the OpenSIPS JSON module in order to parse & access them:


$json(rpl) := $var(rpl_avps);
xlog("Reply AVPs: $json(rpl)\n");
$var(i) = 0;
while ($json(rpl[$var(i)])) {
  xlog("   AVP: $json(rpl[$var(i)])\n");
}

5.  opensips.cfg: Processing Generic Diameter Requests

Starting with OpenSIPS 3.5, there is the option of acting as a Diameter Server and process incoming Diameter requests, as well as to provide the appropriate AVPs for the Diameter answer, which may also be an error, of course. Here are the steps to achieve this:


First, make sure the event_route module is loaded. It is required in order to gain access to the event_route, which will be triggered with a E_DM_REQUEST event on each incoming Diameter request:


log_stdout = yes
...
loadmodule "aaa_diameter.so"
modparam("aaa_diameter", "fd_log_level", 0)
modparam("aaa_diameter", "realm", "diameter.test")
modparam("aaa_diameter", "peer_identity", "server")

loadmodule "event_route.so"
...
event_route [E_DM_REQUEST]
{
    xlog("Req: $param(sess_id) / $param(app_id) / $param(cmd_code)\n");
    xlog("AVPs: $param(avps_json)\n");
}


Notice there are several pieces of information provided through the event's parameters:

  • app_id (integer) - the Diameter Application Identifier
  • cmd_code (integer) - the Diameter Command Code
  • sess_id (string) - the value of either the Session-Id AVP, Transaction-Id AVP or a NULL value if neither of these transaction-identifying AVPs is present in the Diameter request
  • avps_json (string) - a JSON Array containing the AVPs of the request


Next, the request must be processed according to the application specifications. Effectively, the result will be a set of AVPs to be included in the answer. Make sure to pack them as a JSON Array (similar to the input of dm_send_request()), before finally calling dm_send_answer():


  ...
  $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");

Page last modified on March 07, 2024, at 05:12 PM