SIP flood vs OpenSIPS armed with,, ipset and iptables

Preface: the PIKE module itself blocks SIP requests (just stops sending any replies) in case of flood. This article is about going on – adding flooding IP addresses to ipset for further rejecting any traffic to the OpenSIPS server using iptables.

1. Create an ipset with auto removing addresses after 120 seconds and ability to add comments.

ipset create SIPFLOOD hash:ip timeout 120 comment

2. An iptables rule, which will drop incoming traffic from src IP addresses from created ipset table:

iptables -A INPUT -m set --match-set SIPFLOOD src -j DROP

3. Allow OpenSIPS’ run-user (usually ‘opensips’) executing ‘ipset’ command without a password (add this line to /etc/sudoers using ‘visudo’ command):

opensips ALL= NOPASSWD: /sbin/ipset

4. OpenSIPS configuration.

Part of modules section of config:

#### exec
loadmodule ""

#### antiflood module
loadmodule ""
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 10)
modparam("pike", "remove_latency", 120)

Part of OpenSIPS script, assuming that somebody sends us too much OPTIONS requests:

if(is_method("OPTIONS")) {

    switch($retcode) {
        case -2:    # detected once - simply drop the request
        case -1:    # detected again - ban the IP and drop request
            exec("/usr/bin/sudo ipset -exist add SIPFLOOD $si");

    sl_send_reply("200", "OK");

5. You may test all this with ‘sipp’ tool.

This is for generating 10 requests (-r) in 2 seconds (-rp 2000) and exiting sipp after sending 10 requests (-m):

sipp -r 10 -rp 2000 -m 10 -sf OPTIONS.xml

This – for generating 70 requests (-r) in 2 seconds (-rp 2000) and exiting sipp after sending 70 requests (-m):

sipp -r 70 -rp 2000 -m 70 -sf OPTIONS.xml

The OPTIONS.xml is as follows:

iptables: a rule with expiration

If you need an automated way of deleting iptables rules after some time, use this:

iptables -A INPUT -s -j DROP && { echo "iptables -D INPUT -s -j DROP" | at now + 1 min; }

This rule will be deleted in a 1 minute.

OpenBSD PF: limit incoming connections per time period

In iptables there is a nice module called hashlimit.

Being in love with OpenBSD and PF, I decided to find if this wonderful firewall has the same feature.

As a minimal example, you can use this rule to allow =< 2 SSH connections per 60 seconds:

pass in on $ext_if proto tcp from any to any port 22 keep state (max-src-conn-rate 2/60)

Note that you have to use parentheses, even using just one option (max-src-conn-rate), otherwise you’ll get an error while parsing the ruleset.
Keep in mind that one of keep state, modulate state, or synproxy state must be specified explicitly to apply this option to a rule.

For more information read the documentation for pf.conf syntax.

OpenBSD: lack of RAM, reordering libraries at boot time and pkg_add errors

Freshly installed (in VirtualBox) OpenBSD 6.2 spent too much time during boot, on the ‘reordering libraries’ step. Several minutes, not less.
I havent’ seen such a behavior in prior releases.

As usual, thanks to guys from
And here are some explanations from Theo de Raadt:

But the problem was in lack of free memory: the VM had only 64 mb (default value in VirtualBox setting for OBSD) and it was not enough.

After adding more memory the boot process became quicker.

pkg_add(1) and pkg_info(1) havent’ worked properly either, until I added extra memory to the configuration.

Lighttpd: mod_access

lighttpd_logo I faced a problem when it was needed to allow access to certain url (x.x.x.x/zabbix/) for 2 fixed IP-addresses and one /16 subnet, and deny to anybody else.

The old examples from the official documentation worked not so perfect as I wanted
But the users helped me in the same topic.

This is how it’s done:

$HTTP["url"] =~ "^/zabbix/" {
    $HTTP["remoteip"] == "" {
    else $HTTP["remoteip"] == "" {
    else $HTTP["remoteip"] == "" {
    else $HTTP["remoteip"] != "" {  # (dummy match everything)
        url.access-deny = ( "" )

Now anybody accessing /zabbix/ will get “403” error except , and

OpenSIPS battle: REGISTER requests vs permissions module

Sutuation: you have to check the source address of REGISTER messages, going to your OpenSIPS server and decide wether to allow them or to deny.

Use permissions module for this.

You can use it in two variants:

1. with OpenSIPS’ text config files register.allow and register.deny (similar to Unix hosts.allow and hosts.deny).
In this case you should use module’s function ‘allow_register

Example of blocking REGISTERs from IP address:

register.deny file:

ALL : "^sip:.*10\.145\.13\.49"
ALL : "^sip:.*0*10\.145\.0*13\.0*49"   # this is to prevent bypassing
                                       # by the insertion of one or more '0' in the IP address

register.allow file is empty (allow everything except those in .deny file).

OpenSIPS script snippet:

	if ( is_method("REGISTER") ) {
		if (allow_register("register")) {
		} else {
			sl_send_reply("403", "Forbidden registration from your IP v2");

But this method has one big disadvantage – you need to restart OpenSIPS each time you edit register.allow/register.deny.
OpenSIPS ‘permissions’ module has a MI function ‘address_reload‘ but it reloads the table (see below), not the allow/deny files.
So, it’s more cool to use the second variant, go on reading!..

2. with DB table ‘address‘.
In this case you should use modules’ function ‘check_address

– register.allow and register.deny files are empty.
– add entries to ‘address’ table. In our case we’re using not real SQL DB but dbtext. So, this is how ‘/etc/opensips/dbtext/address’ file looks like:

voip-pbx-sbc ~ # cat /etc/opensips/dbtext/address 
id(int,auto) grp(int) ip(string) mask(int) port(int) proto(string) pattern(string,null) context_info(string,null)

WARNING: every time you add any new txt table (not sure about real SQL, but sure in case of using dbtext), do not forget to add it’s version to another txt-table ‘version’ (I think in case of real SQL it is done automatically, but with dbtext we have to do it manually):

voip-pbx-sbc ~ # cat /etc/opensips/dbtext/version 
table_name(string) table_version(int) 

Firstly, I haven’t done it, and that’s why OpenSIPS could not start and I had this message in the system log:

ERROR:core:db_check_table_version: invalid version 0 for table address found, expected 5

So, the script snippet with the ‘check_address’ function:

	if ( is_method("REGISTER") ) {

		if(check_address("0","$si","0","any")) {
		} else {
			sl_send_reply("403", "Forbidden registration from your IP v2");


And here’s the magic! You may add IP-addresses or subnets to your DB or dbtext file and then run a MI command ‘address_reload‘ without restarting your high-loaded OpenSIPS.

Now the policy is “if address is in the table – allow it, otherwise block”. Look at the images below.

IP is not in the table – REGISTER is forbidden:

IP has been added to dbtext table and table reloaded – registrations passed successfully:

You can also look the table’s contents with MI commands ‘opensipsctl fifo address_dump‘ and ‘opensipsctl fifo subnet_dump‘.

UPD: OpenSIPS core developer’s answer to my question .

Asterisk: count active calls on certain peers

Let’s imagine that you have a number of peers – both internal users and trunks with VoIP providers.
And you need to count active calls on trunks only. Or even on trunks with some concrete provider, but not the total ‘core show calls’. As an example, you may need to pass that integer to Zabbix.

root@pbx:~# asterisk -C /etc/asterisk/asterisk.conf -rx 'sip show inuse' 
Setting max files open to 1000
* Peer name               In use          Limit           
104                       0/0/0           10              
107                       0/0/0           10              
100                       0/0/0           10                            
voip-isp-london1          1/0/0           6      
voip-isp-london2          2/1/0           6      

In fact, all magic is done with grep and especially awk tool. Firstly, you need to grep ‘voip-isp’, then extract the second column (is done with awk ‘{print $2}’ ) and then extract the first digit from three ones (e.g. 2 from 2/1/0).
At this point, you’ll get a list of integers, one per line, corresponding to number of active calls on each grepped trunk.
Now it’s time to summarize them, and the best way to do it is also awk! (is done with awk -F\/ ‘{sum += $1} END {print sum}’ )

root@pbx:~# asterisk -C /etc/asterisk/asterisk.conf -rx 'sip show inuse' | grep voip | awk '{print $2}' | awk -F\/ '{sum += $1} END {print sum}'

PBX backup script



TAR="/bin/tar -czf"
DATE=$(date +%Y-%m-%d)
CDRDAY=$(date +%d)

# function for backup routine
bkuper ()
	# 1. cd to dir
	mkdir BACKUP

	# 2. copy mission-critical shit
	for i in /etc/asterisk/ /etc/lighttpd/lighttpd.conf /etc/odbc.ini /etc/odbcinst.ini /var/scripts/ ;
		/bin/cp -r $i BACKUP/

	# 3. CDR SQL dump on 28 day monthly
	if [ $CDRDAY == 28 ]
		$SQLDUMP -u root -pRoOtPaSsWoRd db_asterisk_cdr > BACKUP/db_asterisk_cdr.$DATE.sql

	# 4. archivate

	# 5. delete shit
	/bin/rm -rf BACKUP/

# fire!
if [ -d $BACKUP_DIR ]
	/bin/mkdir $BACKUP_DIR

# remove older than 30 days old backups
find $BACKUP_DIR -type f -name "*z" -mtime +30 -execdir rm -f {} \;

exit $?

Debian 9, MariaDB, ODBC

Debian switched from MySQL to MariaDB since release 9.
And as for now (2017-august-11) it seems that there is no ODBC package for MariaDB connection.

You need to download connector manually from MariaDB website. I tried 3.0.1 (beta) and it didn’t work for me, so I used latest stable (as for now, 2.0.15).

Just download gzipped tar file (not source), unpack it and place to /usr/lib/x86_64-linux-gnu/odbc/

After that your odbcinst.ini should look like this:

# not sure if we need 'Setup' at all. As I understood, it's for GUI tool and as you see, 
# I steel have a wrong lib here :) but now everything works

If you’re in a hurry, you may configure your server right now. But there’s a second part of the story.

As it was my first practice with Debian 9, MariaDB and ODBC connection for it, I haven’t know exactly what packages do I need. It seemed to be clear that some package names will be different in contrast to Debian 8/MySQL+ODBC.

That’s why I installed odbc-mdbtools, thinking that “MDB” means “MariaDB” :-)
Nothing worked, and “isql -v %connector name% %DB user% %DB pass%” command showed that “File not found”. I was confused!

It was my fault that I haven’t read the package description, relying on package name only.

And then, after some hours of research (including my monologue at Linuxquestions) I configured MariaDB + ODBC.

Debian 9: webserver notes

Debian 9.1 Stretch (stable).

If your Apache2 does not execute PHP code (even when PHP is already installed) check if a package ‘libapache2-mod-php7.0‘ is installed. Seems to be easy and obvious, but I faced this problem with Debian 9.