Kamailio by example - Data types: HTable

5 minute read


htable

:information_source: Note: Read the official docs to get the most updated information.

htable $sht(htname=>key) is an Hash Table stored in shared memory, implemented by the module htable. A typical use case for the SIP server is to implement a cache system.

An iten is a pair of key, value (string,integer/string). The key can contain other PVs which is evaluated at runtime and the values can be loaded at startup from a database table.

The definition of a hash table. The value of the parameter may have the following format: "htname=>size=_number_;autoexpire=_number_;dbtable=_string_"

loadmodule "htable.so"

modparam("htable", "htable", "cps=>size=3;initval=0;autoexpire=30")

request_route {
  // $sht(cps=>test) is the logical link to the Htable called cps with a key named $si
  // we are increasing the source IP's counter by 1
  $sht(cps=>$si) = $sht(cps=>$si) + 1;

  if($sht(cps=>$si) > 3){
    xlog("$si is back again, rate limiting them...");
    sl_send_reply("403", "CPS exceeded");
    exit;
  }

  xlog("$si is $sht(cps=>$si) ");
  sl_send_reply("403", "CPS exceeded");

}

Itens in a hash table are single values: assignemetns overwrite the previous value (to delete use $null).

Locking

Updates done in one step are safe to be done without locking, but if you need to do multiple operations, you should use it:

var(a)=1;
lock(key);
$sht(id=>key) = $sht(id=>key) + var(a);
unlock(key);

Multiple values per key

The module also provides a way to store multiple values for a single key. This is emulated by storing individual keys as key_name[n], where n is incremented for each key. The total number of keys is stored in a dedicated key, by default: key_name::size.

Size

The number of itens is limited by the shared memory.

If there’s a large number of itens, it’s recommended to increase the size, to make the search faster (lower collision rate in the same slot).

How to test

#!KAMAILIO

auto_aliases=no
listen=10.0.0.1

loadmodule "xlog"
loadmodule "pv"
loadmodule "sl"
loadmodule "ctl"
loadmodule "htable"

modparam("htable", "htable", "cps=>size=3;initval=0;autoexpire=60")

request_route {
  // $sht(cps=>test) is the logical link to the Htable called cps with a key named $si
  // we are increasing the source IP's counter by 1
  $sht(cps=>$si) = $sht(cps=>$si) + 1;

  if($sht(cps=>$si) > 3){
    xlog("$si exceeded the CPS of 3 ($sht(cps=>$si))\n");
    sl_send_reply("403", "Forbidden CPS exceeded");
    exit;
  }

  // debug print the whole table
  sht_print();

  // iterate over the table
  sht_iterator_start("i1", "cps");
  while(sht_iterator_next("i1")) {
      xlog("cps[$shtitkey(i1)] is: $shtitval(i1)\n");
  }
  sht_iterator_end("i1");

  xlog("CPS[$si] increased to $sht(cps=>$si)\n\n");
  sl_send_reply("200", "Ok Sir");

}

event_route[htable:expired:cps] {
  xlog("cps record expired $shtrecord(key) => $shtrecord(value)\n");
}
  ## add temporary local ips to interface eth0
~ $ sudo ip addr add 10.0.0.1 dev eth0
~ $ sudo ip addr add 10.0.0.2 dev eth0
~ $ sudo ip addr add 10.0.0.3 dev eth0
~ $ sudo ip addr add 10.0.0.4 dev eth0
~ $ sudo ip addr add 10.0.0.5 dev eth0
~ $ sudo ip addr add 10.0.0.6 dev eth0
~ $ sudo ip addr add 10.0.0.7 dev eth0
~ $ sudo ip addr add 10.0.0.8 dev eth0
~ $ sudo ip addr add 10.0.0.9 dev eth0

  ## run a kamailio docker image with the kamailio.cfg above
~ $ sudo docker run -it --rm --name kam --net host -v "$(pwd):/etc/kamailio/" ghcr.io/kamailio/kamailio:5.6.4-bullseye
  ## run a sip client to answer the request
~ $ docker run -ti --rm --net host --name pjsua marcelofpfelix/pjsua --log-level=3 --app-log-level=3 --no-stderr --color --light-bg --snd-auto-close=0 --no-vad --auto-answer=200 --max-calls=4 --duration=90 --id='sip:pjsua@pjsua:5060;transport=udp' --use-timer=1 --clock-rate 44100 --snd-clock-rate 44100 --null-audio --rtcp-mux --add-codec=PCMU/8000/1 --no-tcp --auto-update-nat=1 --disable-stun --local-port=5001  --bound-addr=10.0.0.2 --rtp-port=59634

  ## send options from different private ip
~ $ sipexer -laddr 10.0.0.1:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.2:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.3:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.4:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.5:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.6:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.7:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.8:15060 10.0.0.1
~ $ sipexer -laddr 10.0.0.9:15060 10.0.0.1

 1(8) ERROR: htable [ht_api.c:963]: ht_dbg(): htable[cps] hid: 7286744 exp: 60>
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[0] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.6
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767768 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[1] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.7
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767769 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[2] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.4
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767770 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[3] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.5
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767771 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[4] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.2
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767772 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[5] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.3
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767773 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[6] -- <1>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.9
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767782 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:968]: ht_dbg(): htable[7] -- <2>
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.1
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767775 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1
 1(8) ERROR: htable [ht_api.c:972]: ht_dbg(): 	cell: 10.0.0.8
 1(8) ERROR: htable [ht_api.c:973]: ht_dbg(): 	hid: 1662767783 msize: 81 flags: 0 expire: 1685290242
 1(8) ERROR: htable [ht_api.c:978]: ht_dbg(): 	v-i:1

 1(8) ERROR: <script>: cps[10.0.0.6] is: 1
 1(8) ERROR: <script>: cps[10.0.0.7] is: 1
 1(8) ERROR: <script>: cps[10.0.0.4] is: 1
 1(8) ERROR: <script>: cps[10.0.0.5] is: 1
 1(8) ERROR: <script>: cps[10.0.0.2] is: 1
 1(8) ERROR: <script>: cps[10.0.0.3] is: 1
 1(8) ERROR: <script>: cps[10.0.0.9] is: 1
 1(8) ERROR: <script>: cps[10.0.0.1] is: 1
 1(8) ERROR: <script>: cps[10.0.0.8] is: 1
 1(8) ERROR: <script>: CPS[10.0.0.9] increased to 1

## sending 4 options we can see the value increase resulting in the 403 response
~ $ sipexer -laddr 10.0.0.5:15060 10.0.0.1
 4(11) ERROR: <script>: CPS[10.0.0.5] increased to 3
 5(12) ERROR: <script>: 10.0.0.5 exceeded the CPS of 3 (4)
## and the expiration of the record
 9(16) ERROR: <script>: cps record expired 10.0.0.5 => 4

We can see in this example that the hash table has 8 slots, and the slot 7 has 2 itens.

kamcmd

The kamcmd can be use to view the tables and content.

~ $ docker exec -it kam bash
/ # kamcmd htable.stats
{
	name: cps
	slots: 8
	all: 9
	min: 1
	max: 2
}
/ # kamcmd htable.dump cps
# (...)
{
	entry: 7
	size: 2
	slot: {
		{
			name: 10.0.0.1
			value: 1
			type: int
		}
		{
			name: 10.0.0.8
			value: 1
			type: int
		}
	}
}

Resources

Updated:

Leave a Comment