Kamailio by example - Data types: HTable
htable
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
}
}
}
Leave a Comment