Accelerate black list loading with nftables backend

Issue #151 open
Former user created an issue

I have a black list of tens of thousands addresses on my server. Every time it reboots, sshguard configures them one by one via nft add element comand, which takes up to half an hour.

I tried switching to batch configuration with nft -f, and it takes less than a minute to complete. My approach is to cache block commands in a tmpfile, and flush to nftables after a timeout.

I think it would be better to use batch config only during black list loading, and use nft add element afterwards. This may need to introduce new backend commands to indicate the start and end of initialization.

Some changes to sshg-fw-nft-sets in test:

fw_block() {
    prot="$(proto $2)"
    if [ ! -s ${file} ]; then
        cat > ${file} << EOF
table ${prot} ${NFT_TABLE} {
    set ${NFT_SET} {
        type ipv${2}_addr
        flags interval
        elements = {
    echo "            $1/$3," >> ${file}

fw_block_flush() {
    for prot in ip ip6; do
        if [ -s ${file} ]; then
            echo -e "        }\n    }\n}" >> ${file}
            ${CMD_NFT} -f ${file}
            rm ${file}
while :; do
    read -t 3 -r cmd address addrtype cidr
    if [ "$ret" -gt 128 ]; then
    elif [ $ret -eq 0 ]; then
        if [ $cmd != "block" ]; then
        case $cmd in
                fw_block "$address" "$addrtype" "$cidr";;
                fw_release "$address" "$addrtype" "$cidr";;
                die 65 "Invalid command";;

Comments (4)

  1. Kevin Zheng
    • changed status to open

    Would it help if all the backends gained a "batch add" command? That way SSHGuard can issue a single "batch add" command at startup when it re-blacklists everything, then goes back to blocking addresses one by one.

    Alternatively, do you know if there's a better way to control nft so that it isn't so slow?

  2. Xiao Liang

    Do you mean a single batch add command with huge arguments? It could be fine, but I would prefer enclosing block commands with a batch-start and batch-end pair. A benefit is that backends which don’t (or not necessary to) support batch can simply ignore them.

    It seems that nft add element reads the set from kernel to determine its type, while the batch form can provide this information. I don’t know if there’re other ways to fix it.

  3. Darrell Enns

    I’ve also encountered issues with slow blacklist loading on nftables. I think the current method of doing (for each list entry) a shell script call which in turn calls the nft cli tool is quite inefficient. Adding a batch backend call that is used when loading the blacklist would certainly help.

    The simplest way would probably be to have a batch add command that passes the list of entries via stdin (stdin avoids any issues with arguments being too long). The same interface could be used for other backends. In the nftables backend script, the “nft add element” command can be used with a comma separated list of elements to add, which reduces the number of “nft” process calls.

    If further performance improvement is necessary, the backend could be implemented backend in C (or another compiled language), rather than as a shell script. It could use libnftnl ( to talk to the kernel directly, so there are no additional process launched. Or, to go one step further, the backend interface could be via shared libraries that are loaded by sshguard at startup. Then there’s no launching additional processes needed at all.

  4. Kevin Zheng

    Thanks for your comments. We would welcome a smarter nftables backend. It’s not convenient for me to write since I’m mostly on *BSD machines, but if some is able to write and test such a nftables backend it would be useful.

  5. Log in to comment