Posts Tagged ‘opensips’

OpenSIPS dispatching algorithms

Tuesday, February 20th, 2024

Just some explanation of dispatcher module argorithms.

hash over callid – ensures that all requests within a dialog goes to same box
hash over from uri – ensures that all requests from same user goes to same box
hash over to uri – ensures that registrations of an AoR goes to same box
hash over request-uri – ensures that requests to same destination are processes by same box
#hash over config variable – for different needs

OpenSIPS: monitoring with Zabbix: HTTP Agent, JSONPath

Thursday, March 24th, 2022

This guide will help you to understand how OpenSIPS can be monitored with Zabbix, sharing its statistic data via HTTP interface in JSON format. This article assumes using OpenSIPS 3.2 and Zabbix 6.

Enable embedded HTTP server and HTTP support for Management Interface in your OpenSIPS:

loadmodule "httpd.so"
modparam("httpd", "ip", "192.168.88.244")
modparam("httpd", "port", 8888)

# :8888/mi
loadmodule "mi_http.so"
modparam("mi_http", "root", "mi")

Now we may try to send a JSON-RPC OpenSIPS MI command from the command-line, using curl (official example at the very bottom of the page):

/usr/bin/curl -X POST 192.168.88.244:8888/mi -H 'Content-Type: application/json' -d '{"jsonrpc": "2.0", "id": "1", "method": "uptime"}'

The official documentation does not have other more complicated examples, e.g. “get_statistics sl:”, that’s why I decided to write this article, maybe it’s more about JSON, JSONPath and working with all this in Zabbix.

As an example, we’ll get stateless replier module statistics, like shown by invoking a CLI command “opensips-cli -x mi get_statistics sl:” .

/usr/bin/curl -X POST 192.168.88.244:8888/mi -H 'Content-Type: application/json' -d '{"jsonrpc": "2.0", "id": "1", "method": "get_statistics", "params": {"statistics": ["sl:"]}}'

I’ll also duplicate the command with a screenshot (to be sure that you see it in a right way, because markup may cut some special characters):

Tip: if you need statistics from several groups, for example several modules (not only SL), the request will be as follows:

/usr/bin/curl -X POST 192.168.88.244:8888/mi -H 'Content-Type: application/json' -d '{"jsonrpc": "2.0", "id": "1", "method": "get_statistics", "params": {"statistics": ["sl:", "tm:"]}}'

OpenSIPS will answer and you’ll see SL module statistics after sending this request with CURL.

But the responce is a one-liner, so we need to convert this one line to JSON format and then to create a JSONPath, to be able to extract the value we need.

Let’s monitor not all SL module statistics received from OpenSIPS, but the number of 2xx replies only.

I use https://www.jsonformatter.io/ for JSON formatting and https://jsonpath.com/ for creating JSONPath. After formatting and specifying JSONPath we can configure Zabbix.


Now it’s time to add items to your OpenSIPS host in Zabbix. Item parameters:

Type: HTTP Agent
Type of information: Numeric (unsigned)
Request type: POST
Request body type: JSON data
Request body: {"jsonrpc": "2.0", "id": "1", "method": "get_statistics", "params": {"statistics": ["sl:"]}}
Retrieve mode: Body
Convert to JSON (enabled)

Screenshots as usual:


Item Preprocessing parameters:

1st step - JSONPath
Parameters: $.body.result.["sl:2xx_replies"]
Type of information: Numeric (unsigned)

Screen – item Preprocessing and successful testing:


Now we add a graph with this item (I hope you know how to create graphs in Zabbix) and it’s time to test. I will generate thousands of OPTIONS requests to my OpenSIPS with the sipp tool (I also recommend this sipp cheatsheet).

Download OPTIONS.xml scenario file, and generate 10000 OPTIONS requests with call rate of 10 (I assume that your OpenSIPS, like mine, answers “200 OK”):

sipp 192.168.88.244 -sf OPTIONS.xml -l 10000 -m 10000 -r 10

Finally, here is the graph showing your OpenSIPS SL module 2xx processed replies statistics:

This is how you can monitor OpenSIPS with Zabbix using JSON.

Have fun!

RTPEngine set weight

Monday, August 23rd, 2021

Undocumented feature –
how to configure weight for a rtpengine set (default value is 1):

modparam("rtpengine", "rtpengine_sock", "udp:localhost:12222=2")

http://lists.opensips.org/pipermail/users/2021-August/045084.html

OpenSIPS: INVITE filtering

Tuesday, October 27th, 2020

A small snippet for passing only valid INVITEs from the Internet to your OpenSIPS server: allowing calls from VoIP ISPs and registered users only.

# antiflood
if(!is_myself("$si") && $Rp == 5060)
{
  if(!is_registered("location", "$fu") && !check_source_address("1")) 
  {
    exit;
  }
}

In this example we store ISPs IP addresses in the ‘address’ table of the permissions module, in group 1, which is seen from the output of the corresponding fifo command:

[root@voip-srv ~]# opensipsctl fifo address_dump
part:: default
dest:: grp=1 ip=193.201.229.35 mask=32 port=0 proto=any pattern= context_info=VoIP ISP Multifon
dest:: grp=1 ip=81.211.59.102 mask=32 port=0 proto=any pattern= context_info=VoIP ISP ekt.ip.Beeline
dest:: grp=1 ip=212.119.246.230 mask=32 port=0 proto=any pattern= context_info=VoIP ISP ip.Beeline

OpenSIPS 2.4: DB/script clusterer configuration

Monday, September 7th, 2020

No DB, node 1:

modparam("clusterer", "current_id", 1)
modparam("clusterer", "db_mode", 0)
modparam("clusterer", "seed_fallback_interval", 10) # Only relevant for seed node
modparam("clusterer", "current_info","cluster_id=1,url=bin:10.145.213.63:5555,flags=seed")
modparam("clusterer", "neighbor_info","cluster_id=1,node_id=2,url=bin:10.145.213.155:5555")

No DB, node 2:

modparam("clusterer", "current_id", 2)
modparam("clusterer", "db_mode", 0)
modparam("clusterer", "seed_fallback_interval", 10) # Only relevant for seed node
modparam("clusterer", "current_info", "cluster_id=1,url=bin:10.145.213.155:5555")
modparam("clusterer", "neighbor_info", "cluster_id=1,node_id=1,url=bin:10.145.213.63:5555")

DB configuration, node 1:

modparam("clusterer", "current_id", 1)
modparam("clusterer", "db_mode", 1)
modparam("clusterer", "db_url", "mysql://opensips:MeGaPaSs@10.145.213.200/opensips")

DB configuration, node 2:

modparam("clusterer", "current_id", 2)
modparam("clusterer", "db_mode", 1)
modparam("clusterer", "db_url", "mysql://opensips:MeGaPaSs@10.145.213.200/opensips")

Clusterer table:

MariaDB [dbsrv]> select * from clusterer\G
*************************** 1. row ***************************
             id: 1
     cluster_id: 1
        node_id: 1
            url: bin:10.145.213.63:5555
          state: 1
no_ping_retries: 3
       priority: 50
       sip_addr: 
          flags: seed
    description: USRLOC_Cluster_node_1
*************************** 2. row ***************************
             id: 2
     cluster_id: 1
        node_id: 2
            url: bin:10.145.213.155:5555
          state: 1
no_ping_retries: 3
       priority: 50
       sip_addr: 
          flags: 
    description: USRLOC_Cluster_node_2

OpenSIPS: Adding CALLERID (display-name) to calls from registered users

Thursday, November 21st, 2019

This is about adding a display name to the calls from registered users. An analogue of Asterisk’s Set(CALLERID(name)=John Doe).

The idea was to move SIP accounts from Asterisk to OpenSIPS.

In case of using Asterisk we would configure something like:

[user222]
context=o.local
secret=4EwIWikV
callerid=Alexey Kazantsev <222>

How to achieve the same with OpenSIPS? This is the solution:

  • add desired display-names to ‘rpid’ columns of the ‘subscriber‘ table:
Adding desired callerid to ‘rpid’ column of the SIP-account.
modparam("auth_db", "load_credentials", "$avp(display)=rpid")
  • add ‘attr_avp‘ parameter to ‘registrar’ module settings:
modparam("registrar", "attr_avp", "$avp(display)")
  • and finally some magic in the script, to add the display-name to the INVITE request from the registered user, going through our OpenSIPS SBC:
# call from registered user -> add callerid
# and forward to mediaserver for call recording, etc.
if(is_registered("location"))
{
# replace only display and do not touch uri
uac_replace_from("$avp(display)","");
rewritehostport("10.145.213.63:5067");
route(relay);
}

How it looks like?

This is the INVITE coming to OpenSIPS:

2019/11/21 14:16:55.247856 10.145.213.64:5061 -> 10.145.213.63:5060
INVITE sip:111@taxsee.com SIP/2.0
Via: SIP/2.0/UDP 10.145.213.64:5061;branch=z9hG4bK-3822b894
From: <sip:222@taxsee.com>;tag=48b014547f398294o1

And this is the same INVITE leaving OpenSIPS, being modified:

2019/11/21 14:16:55.252518 10.145.213.63:5060 -> 10.145.213.63:5067
INVITE sip:111@10.145.213.63:5067 SIP/2.0
Record-Route: <sip:10.145.213.63;lr;ftag=48b014547f398294o1>
Via: SIP/2.0/UDP 10.145.213.63:5060;branch=z9hG4bK77e6.9bb3aa72.0
Via: SIP/2.0/UDP 10.145.213.64:5061;branch=z9hG4bK-3822b894
From: "Alexey Kazantsev" <sip:222@taxsee.com>;tag=48b014547f398294o1

The information stored in the ‘rpid’ column (in our example, or some custom in your architecture) is fetched to AVP at each REGISTER/save, so you do not need to reload anything to take changes in effect.

The callerid info is seen in console output via ‘opensipsctl fifo ul_dump’ command:

	AOR:: 222@taxsee.com
		Contact:: sip:222@10.145.213.64:5061 Q=
			ContactID:: 3039507536010050217
			Expires:: 42
			Callid:: 9fdd26c2-6de37105@10.145.213.64
			Cseq:: 35746
			User-agent:: Cisco/SPA303-7.6.2c
			State:: CS_NEW
			Flags:: 0
			Cflags:: 
			Socket:: udp:10.145.213.63:5060
			Methods:: 5247
			Attr:: Alexey Kazantsev

Inserting Asterisk in your call flow

Wednesday, November 6th, 2019

Let’s imagine that we’ve upgraded our VoIP network which was formerly based on geographically distributed Asterisks.

We configured OpenSIPS servers as registrars, connected them together in a full-sharing usrloc cluster and now we need to route calls between endpoints not directly, but through Asterisks – to handle our calls in a familiar way (CDR records, call recording via MixMonitor, some AGI scripts, etc).

We have to create something like that:

This is a code snippet of OpenSIPS with IP address 10.145.213.63:

	# initial INVITE
	if(is_method("INVITE") && !has_totag())
	{
		t_on_failure("1");

		# call from registered user ->
                # forward to mediaserver for call recording, etc.
		if(is_registered("location"))
		{
			$ru="sip:" + $oU + "@" + "10.145.213.63:5067";
			route(relay);
		}

		# call from Asterisk? -> change domain part before doing lookup
		if($sp=="5067")
		{
			$rd="taxsee.com";
		}

		$var(lookup_flags) = "m";
		if(cluster_check_addr("1", "$si")) {
			xlog("si: $si . $rm from cluster, doing local lookup only\n");
		} else {
			xlog("si: $si . $rm from outside, doing global lookup\n");
			$var(lookup_flags) = $var(lookup_flags) + "g";
		}

		if(!lookup("location", "$var(lookup_flags)"))
		{
			t_reply("404", "Not Found");
			exit;
		}

		if(has_body("application/sdp"))
		{
			rtpengine_offer("RTP/AVP replace-origin replace-session-connection ICE=remove");
		}

	} # initial INVITE end

	route(relay);

A SIP peer to this OpenSIPS in Asterisk sip.conf looks like this:


[opensips]
type=peer
context=office
host=10.145.213.63
port=5060

And a dialplan for CDR/MixMonitor/etc:

context office
{
	_XXX =>
	{
		NoOp(imagine this is CDR, MixMonitor, AGI);
		Dial(SIP/opensips/${EXTEN});
		Hangup();
	};
};

UPD: assuming your SIP acoounts DB has moved from Asterisk to OpenSIPS cluster, its desirable not just processing REGISTER requests and doing authentication, but also adding caller ids to your SIP accounts.

OpenSIPS 2.4 usrloc full-sharing cluster

Friday, October 25th, 2019

UPD: 2019-november-5. An official tutorial is available now! And some discussion on the mail list.

Draft/some notes…

2 OpenSIPS nodes without MongoDB

  • clusterer.so module – one node is configured as seed (in case of using MongoDB for usrloc, both nodes were seed)
    • if a non-seed node restarts, it gets usrloc from seed one via bin interface
    • if a seed node is restarted, we have to get all usrloc records from non-seed one using ‘opensipsctl fifo ul_cluster_sync’ command

Key settings:

loadmodule “usrloc.so”
modparam(“usrloc”, “db_url”, “mysql://opensips:pass@10.145.213.63/opensips”)
modparam(“usrloc”, “nat_bflag”, “NAT”)
modparam(“usrloc”, “working_mode_preset”, “full-sharing-cluster”)
modparam(“usrloc”, “location_cluster”, 1)
modparam(“usrloc”, “use_domain”, 1)

loadmodule “clusterer.so” # requires proto_bin.so
modparam(“clusterer”, “current_id”, 1)
modparam(“clusterer”, “db_mode”, 1)
modparam(“clusterer”, “db_url”, “mysql://opensips:pass@10.145.213.63/opensips”)


    # initial INVITE
    if(is_method("INVITE")) {
	t_on_failure("1");
	$var(lookup_flags) = "m";
	if (cluster_check_addr("1", "$si")) {
		xlog("si: $si . $rm from cluster, doing local lookup only\n");
	} else {
		xlog("si: $si . $rm from outside, doing global lookup\n");
		$var(lookup_flags) = $var(lookup_flags) + "g";
	}
	if (!lookup("location", "$var(lookup_flags)")) {
		t_reply("404", "Not Found");
		exit;
	}
	if (has_body("application/sdp")) {
	    rtpengine_offer("RTP/AVP replace-origin replace-session-connection ICE=remove");
	}

    }	# initial invite END

UPD: some useful notes from Liviu Chircu: http://lists.opensips.org/pipermail/users/2019-October/041819.html

Pay attention: according to his advice, we don’t need any data in ‘sip_addr’ columns when creating a full-sharing usrloc cluster. In case of federated cluster we have to fill them with IP addresses on which OpenSIPS nodes are listening.

OpenSIPS: filter REGISTER requests based on username

Thursday, July 11th, 2019

Formerly we’ve learned how to restrict access with permission.so module based on source IP addresses. Today I’ll show how to restrict access to your OpenSIPS based on usernames, being registering.

loadmodule "permissions.so" # no deps

...

if (is_method("REGISTER")) {
if(!allow_register("register")) {
sl_send_reply(403, "Forbidden by permissions");
exit;
}

“Deny all, but …” policy – we will allow registrations of explicitly defined usernames and drop anybody else.

register.deny file:

ALL : ALL


register.allow file:

# this allows lexus, lexus2, lexus3 to register
"^sip:lexus[23]?@alexeyka.zantsev.com" : ALL

# regexp seems to be CORRECT, but for some reason lexus2 and lexus3 can not register
# "^sip:lexus[\d]?@alexeyka.zantsev.com" : ALL

Have a look how it’s working! A good guy is being registered successfully:

good guy

While a bad guy had been kicked:

bad guy

Another solution using regex.so module from Pavel Eremin. The pros of this method is that it allows editing a txt file with usernames defined and reload regex.so module via MI interface (no restart needed).

And even one more from Răzvan Crainea.

OpenSIPS: ratelimit with dynamically changeable value

Monday, May 13th, 2019

This note will instruct you how to protect each DID number connected to your OpenSIPS from SIP DDoS, limiting not the total amount of INVITE requests going to your OpenSIPS server, but only INVITEs to some certain RURI.

Check it out, I hope you like it!

This is useful when you have a plenty of SIP numbers (DIDs) connected to your server and each one accepts inbound calls, e.g. a call centre or a taxi ordering service, etc. And you have to check each destination and drop too much requests, without degradation of any other incoming calls.

This is a nice solution to prevent the situation seen on the graph in the previous post.

PS: clustering support is not described here.

    loadmodule "ratelimit.so"		     # no deps
    modparam("ratelimit", "window_size", 2)  # ban timeout, sec

Add start limit value to the startup_route:

    startup_route {
    	cache_store("local", "inc:rl", "8");
    }

Somewhere in the initial INVITE section:

# AntiDDoS for each inbound call
if($si !~ "^10\..*") {
    cache_fetch("local", "inc:rl", $var(rl));
        # $var(rl) invites/sec going to each $rU.
        # SBT is the most precise policy.
        if (!rl_check("pipe_$rU", "$(var(rl){s.int})", "SBT")) {          
            sl_send_reply("503", "Service Unavailable. AntiDDoS");
            xlog("L_INFO", "call $ci from $fU@$si:$sp to $oU@$Ri drp by rl");
            exit;
        };
};

Live statistics:

voip-sipgw01 opensips # opensipsctl fifo rl_list
PIPE::  id=pipe_9618688830 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_9020578345 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_9611157347 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_79190224444 algorithm=SBT limit=8 counter=0
...
PIPE::  drop_rate=581

Change the limit on the fly up to 10 INVITES to each $rU:

opensipsctl fifo cache_store local inc:rl 10