1 # banIP shared function library/include
2 # Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
3 # This is free software, licensed under the GNU General Public License v3.
5 # (s)hellcheck exceptions
6 # shellcheck disable=all
11 export PATH
="/usr/sbin:/usr/bin:/sbin:/bin"
14 ban_backupdir
="/tmp/banIP-backup"
15 ban_reportdir
="/tmp/banIP-report"
16 ban_feedfile
="/etc/banip/banip.feeds"
17 ban_allowlist
="/etc/banip/banip.allowlist"
18 ban_blocklist
="/etc/banip/banip.blocklist"
19 ban_mailtemplate
="/etc/banip/banip.tpl"
20 ban_pidfile
="/var/run/banip.pid"
21 ban_rtfile
="/var/run/banip_runtime.json"
22 ban_lock
="/var/run/banip.lock"
24 ban_logreadcmd
="$(command -v logread)"
25 ban_logcmd
="$(command -v logger)"
26 ban_ubuscmd
="$(command -v ubus)"
27 ban_nftcmd
="$(command -v nft)"
28 ban_fw4cmd
="$(command -v fw4)"
29 ban_awkcmd
="$(command -v awk)"
30 ban_grepcmd
="$(command -v grep)"
31 ban_lookupcmd
="$(command -v nslookup)"
32 ban_mailcmd
="$(command -v msmtp)"
33 ban_mailsender
="no-reply@banIP"
35 ban_mailtopic
="banIP notification"
36 ban_mailprofile
="ban_notify"
37 ban_mailnotifcation
="0"
38 ban_reportelements
="1"
39 ban_nftloglevel
="warn"
40 ban_nftpriority
="-200"
41 ban_nftpolicy
="memory"
59 ban_blockforwardwan
=""
60 ban_blockforwardlan
=""
76 # gather system information
81 ban_memory
="$("${ban_awkcmd}" '/^MemAvailable/{printf "%s
",int($2/1000)}' "/proc
/meminfo
" 2>/dev/null)"
82 ban_ver
="$(${ban_ubuscmd} -S call rpc-sys packagelist 2>/dev/null | jsonfilter -ql1 -e '@.packages.banip')"
83 ban_sysver
="$(${ban_ubuscmd} -S call system board 2>/dev/null | jsonfilter -ql1 -e '@.model' -e '@.release.description' |
84 "${ban_awkcmd}" 'BEGIN{RS="";FS="\n"}{printf "%s
, %s
",$1,$2}')"
85 if [ -z "${ban_cores}" ]; then
86 cpu
="$("${ban_grepcmd}" -c '^processor' /proc/cpuinfo 2>/dev/null)"
87 core
="$("${ban_grepcmd}" -cm1 '^core id' /proc/cpuinfo 2>/dev/null)"
88 [ "${cpu}" = "0" ] && cpu
="1"
89 [ "${core}" = "0" ] && core
="1"
90 ban_cores
="$((cpu * core))"
93 f_log
"debug" "f_system ::: system: ${ban_sysver:-"n/a"}, version: ${ban_ver:-"n/a"}, memory: ${ban_memory:-"0"}, cpu_cores: ${ban_cores}"
101 if [ ! -d "${dir}" ]; then
104 f_log
"debug" "f_mkdir ::: created directory: ${dir}"
113 if [ ! -f "${file}" ]; then
115 f_log
"debug" "f_mkfile ::: created file: ${file}"
119 # create temporary files and directories
122 f_mkdir
"${ban_basedir}"
123 ban_tmpdir
="$(mktemp -p "${ban_basedir}" -d)"
124 ban_tmpfile
="$(mktemp -p "${ban_tmpdir}" -tu)"
126 f_log
"debug" "f_tmp ::: base_dir: ${ban_basedir:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}"
134 if [ -d "${dir}" ]; then
136 f_log
"debug" "f_rmdir ::: deleted directory: ${dir}"
145 [ "${char}" = "1" ] && printf "%s" "✔" ||
printf "%s" "✘"
153 string
="${string#"${string%%[![:space:]]*}"}"
154 string
="${string%"${string##*[![:space:]]}"}"
155 printf "%s" "${string}"
161 local class
="${1}" log_msg
="${2}"
163 if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" = "1" ]; }; then
164 if [ -x "${ban_logcmd}" ]; then
165 "${ban_logcmd}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
167 printf "%s
%s
%s
\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
170 if [ "${class}" = "err
" ]; then
171 "${ban_nftcmd}" delete table inet banIP >/dev/null 2>&1
172 if [ "${ban_enabled}" = "1" ]; then
174 [ "${ban_mailnotification}" = "1" ] && [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ] && f_mail
176 f_genstatus "disabled
"
178 f_rmdir "${ban_tmpdir}"
188 unset ban_dev ban_ifv4 ban_ifv6 ban_feed ban_blockinput ban_blockforwardwan ban_blockforwardlan ban_logterm ban_country ban_asn
193 eval "${option}=\"${value}\""
200 eval "${option}=\"$(printf "%s" "${ban_dev}")${value} \""
203 eval "${option}=\"$(printf "%s" "${ban_ifv4}")${value} \""
206 eval "${option}=\"$(printf "%s" "${ban_ifv6}")${value} \""
209 eval "${option}=\"$(printf "%s" "${ban_feed}")${value} \""
212 eval "${option}=\"$(printf "%s" "${ban_blockinput}")${value} \""
214 "ban_blockforwardwan
")
215 eval "${option}=\"$(printf "%s" "${ban_blockforwardwan}")${value} \""
217 "ban_blockforwardlan
")
218 eval "${option}=\"$(printf "%s" "${ban_blockforwardlan}")${value} \""
221 eval "${option}=\"$(printf "%s" "${ban_logterm}")${value}\\|
\""
224 eval "${option}=\"$(printf "%s" "${ban_country}")${value} \""
227 eval "${option}=\"$(printf "%s" "${ban_asn}")${value} \""
234 [ "${ban_action}" = "boot" ] && [ -z "${ban_trigger}" ] && sleep ${ban_triggerdelay}
237 # prepare fetch utility
240 local ut utils packages insecure
242 if [ -z "${ban_fetchcmd}" ] ||
[ ! -x "${ban_fetchcmd}" ]; then
243 packages
="$(${ban_ubuscmd} -S call rpc-sys packagelist 2>/dev/null)"
244 [ -z "${packages}" ] && f_log
"err" "local opkg package repository is not available, please set the download utility 'ban_fetchcmd' manually"
245 utils
="aria2c curl wget uclient-fetch"
246 for ut
in ${utils}; do
247 if { [ "${ut}" = "uclient-fetch" ] && printf "%s" "${packages}" | "${ban_grepcmd}" -q '"libustream-'; } ||
248 { [ "${ut}" = "wget" ] && printf "%s" "${packages}" | "${ban_grepcmd}" -q '"wget-ssl'; } ||
249 [ "${ut}" = "curl" ] ||
[ "${ut}" = "aria2c" ]; then
250 ban_fetchcmd
="$(command -v "${ut}")"
251 if [ -x "${ban_fetchcmd}" ]; then
252 uci_set banip global ban_fetchcmd
"${ban_fetchcmd##*/}"
259 [ ! -x "${ban_fetchcmd}" ] && f_log
"err" "download utility with SSL support not found"
260 case "${ban_fetchcmd##*/}" in
262 [ "${ban_fetchinsecure}" = "1" ] && insecure
="--check-certificate=false"
263 ban_fetchparm
="${ban_fetchparm:-"${insecure} --timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o"}"
266 [ "${ban_fetchinsecure}" = "1" ] && insecure
="--insecure"
267 ban_fetchparm
="${ban_fetchparm:-"${insecure} --connect-timeout 20 --fail --silent --show-error --location -o"}"
270 [ "${ban_fetchinsecure}" = "1" ] && insecure
="--no-check-certificate"
271 ban_fetchparm
="${ban_fetchparm:-"${insecure} --timeout=20 -O"}"
274 [ "${ban_fetchinsecure}" = "1" ] && insecure
="--no-check-certificate"
275 ban_fetchparm
="${ban_fetchparm:-"${insecure} --no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
279 f_log
"debug" "f_fetch ::: fetch_cmd: ${ban_fetchcmd:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}"
287 ppid
="$(cat "${ban_pidfile}" 2>/dev/null)"
288 [ -n "${ppid}" ] && pids
="$(pgrep -P "${ppid}" 2>/dev/null)" ||
return 0
289 for pid
in ${pids}; do
290 kill -INT "${pid}" >/dev
/null
2>&1
295 # get nft/monitor actuals
300 if "${ban_nftcmd}" -t list
set inet banIP allowlistvMAC
>/dev
/null
2>&1; then
305 if pgrep
-f "logread" -P "$(cat "${ban_pidfile}" 2>/dev/null)" >/dev
/null
2>&1; then
306 monitor
="$(f_char "1")"
308 monitor
="$(f_char "0")"
310 printf "%s" "nft: ${nft}, monitor: ${monitor}"
316 local iface update
="0"
318 if [ "${ban_autodetect}" = "1" ]; then
319 if [ -z "${ban_ifv4}" ]; then
321 network_find_wan iface
322 if [ -n "${iface}" ] && "${ban_ubuscmd}" -t 10 wait_for network.interface."${iface}" >/dev
/null
2>&1; then
325 uci_set banip global ban_protov4
"1"
326 uci_add_list banip global ban_ifv4
"${iface}"
327 f_log
"info" "added IPv4 interface '${iface}' to config"
330 if [ -z "${ban_ifv6}" ]; then
332 network_find_wan6 iface
333 if [ -n "${iface}" ] && "${ban_ubuscmd}" -t 10 wait_for network.interface."${iface}" >/dev
/null
2>&1; then
336 uci_set banip global ban_protov6
"1"
337 uci_add_list banip global ban_ifv6
"${iface}"
338 f_log
"info" "added IPv6 interface '${iface}' to config"
342 if [ -n "$(uci -q changes "banip
")" ]; then
346 ban_ifv4
="${ban_ifv4%%?}"
347 ban_ifv6
="${ban_ifv6%%?}"
348 for iface
in ${ban_ifv4} ${ban_ifv6}; do
349 if ! "${ban_ubuscmd}" -t 10 wait_for network.interface.
"${iface}" >/dev
/null
2>&1; then
350 f_log
"err" "wan interface '${iface}' is not available, please check your configuration"
354 [ -z "${ban_ifv4}" ] && [ -z "${ban_ifv6}" ] && f_log
"err" "wan interfaces not found, please check your configuration"
356 f_log
"debug" "f_getif ::: auto/update: ${ban_autodetect}/${update}, interfaces (4/6): ${ban_ifv4}/${ban_ifv6}, protocols (4/6): ${ban_protov4}/${ban_protov6}"
362 local dev iface update
="0" cnt
="0" cnt_max
="30"
364 if [ "${ban_autodetect}" = "1" ]; then
365 while [ "${cnt}" -lt "${cnt_max}" ] && [ -z "${ban_dev}" ]; do
367 for iface
in ${ban_ifv4} ${ban_ifv6}; do
368 network_get_device dev
"${iface}"
369 if [ -n "${dev}" ]; then
370 if printf "%s" "${dev}" |
"${ban_grepcmd}" -qE "pppoe|6in4"; then
373 if ! printf " %s " "${ban_dev}" | "${ban_grepcmd}" -q " ${dev} "; then
374 ban_dev
="${ban_dev}${dev} "
375 uci_add_list banip global ban_dev
"${dev}"
376 f_log
"info" "added device '${dev}' to config"
384 if [ -n "$(uci -q changes "banip
")" ]; then
388 ban_dev
="${ban_dev%%?}"
389 [ -z "${ban_dev}" ] && f_log
"err" "wan devices not found, please check your configuration"
391 f_log
"debug" "f_getdev ::: auto/update: ${ban_autodetect}/${update}, devices: ${ban_dev}, cnt: ${cnt}"
397 local sub iface ip update
="0"
399 if [ "${ban_autoallowlist}" = "1" ]; then
400 for iface
in ${ban_ifv4} ${ban_ifv6}; do
402 network_get_subnet sub
"${iface}"
403 if [ -n "${sub}" ] && ! printf " %s " "${ban_sub}" | "${ban_grepcmd}" -q " ${sub} "; then
404 ban_sub
="${ban_sub}${sub} "
406 network_get_subnet6 sub
"${iface}"
407 if [ -n "${sub}" ] && ! printf " %s " "${ban_sub}" | "${ban_grepcmd}" -q " ${sub} "; then
408 ban_sub
="${ban_sub}${sub} "
411 for ip
in ${ban_sub}; do
412 if ! "${ban_grepcmd}" -q "${ip}" "${ban_allowlist}"; then
414 printf "%-42s%s\n" "${ip}" "# subnet added on $(date "+%Y-
%m-
%d
%H
:%M
:%S
")" >>"${ban_allowlist}"
415 f_log
"info" "added subnet '${ip}' to local allowlist"
418 ban_sub
="${ban_sub%%?}"
421 f_log
"debug" "f_getsub ::: auto/update: ${ban_autoallowlist}/${update}, subnet(s): ${ban_sub:-"-"}"
429 [ -s "${file}" ] && printf "%s" "elements={ $(cat "${file}") };"
432 # build initial nft file with base table, chains and rules
435 local feed_log feed_rc
file="${1}"
438 # nft header (tables and chains)
440 printf "%s\n\n" "#!/usr/sbin/nft -f"
441 if "${ban_nftcmd}" -t list
set inet banIP allowlistvMAC
>/dev
/null
2>&1; then
442 printf "%s\n" "delete table inet banIP"
444 printf "%s\n" "add table inet banIP"
445 printf "%s\n" "add chain inet banIP wan-input { type filter hook input priority ${ban_nftpriority}; policy accept; }"
446 printf "%s\n" "add chain inet banIP wan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
447 printf "%s\n" "add chain inet banIP lan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
449 # default wan-input rules
451 printf "%s\n" "add rule inet banIP wan-input ct state established,related counter accept"
452 printf "%s\n" "add rule inet banIP wan-input iifname != { ${ban_dev// /, } } counter accept"
453 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv4 udp sport 67-68 udp dport 67-68 counter accept"
454 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 udp sport 547 udp dport 546 counter accept"
455 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv4 icmp type { echo-request } limit rate 1000/second counter accept"
456 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { echo-request } limit rate 1000/second counter accept"
457 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} limit rate 1000/second ip6 hoplimit 1 counter accept"
458 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} limit rate 1000/second ip6 hoplimit 255 counter accept"
460 # default wan-forward rules
462 printf "%s\n" "add rule inet banIP wan-forward ct state established,related counter accept"
463 printf "%s\n" "add rule inet banIP wan-forward iifname != { ${ban_dev// /, } } counter accept"
465 # default lan-forward rules
467 printf "%s\n" "add rule inet banIP lan-forward ct state established,related counter accept"
468 printf "%s\n" "add rule inet banIP lan-forward oifname != { ${ban_dev// /, } } counter accept"
471 # load initial banIP table within nft (atomic load)
473 feed_log
="$("${ban_nftcmd}" -f "${file}" 2>&1)"
476 f_log
"debug" "f_nftinit ::: devices: ${ban_dev}, priority: ${ban_nftpriority}, policy: ${ban_nftpolicy}, loglevel: ${ban_nftloglevel}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
483 local log_input log_forwardwan log_forwardlan start_ts end_ts tmp_raw tmp_load tmp_file split_file ruleset_raw handle
484 local cnt_set cnt_dl restore_rc feed_direction feed_rc feed_log feed
="${1}" proto="${2}" feed_url="${3}" feed_rule="${4}" feed_flag="${5}"
486 start_ts
="$(date +%s)"
487 feed
="${feed}v${proto}"
488 tmp_load
="${ban_tmpfile}.${feed}.load"
489 tmp_raw
="${ban_tmpfile}.${feed}.raw"
490 tmp_split
="${ban_tmpfile}.${feed}.split"
491 tmp_file
="${ban_tmpfile}.${feed}.file"
492 tmp_flush
="${ban_tmpfile}.${feed}.flush"
493 tmp_nft
="${ban_tmpfile}.${feed}.nft"
495 [ "${ban_loginput}" = "1" ] && log_input="log level ${ban_nftloglevel} prefix \"banIP/inp-wan/drp/${feed}: \""
496 [ "${ban_logforwardwan}" = "1" ] && log_forwardwan="log level ${ban_nftloglevel} prefix \"banIP/fwd-wan/drp/${feed}: \""
497 [ "${ban_logforwardlan}" = "1" ] && log_forwardlan="log level ${ban_nftloglevel} prefix \"banIP/fwd-lan/rej/${feed}: \""
499 # set source block direction
501 if printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}"; then
502 feed_direction
="input"
504 if printf "%s" "${ban_blockforwardwan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
505 feed_direction
="${feed_direction} forwardwan"
507 if printf "%s" "${ban_blockforwardlan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
508 feed_direction
="${feed_direction} forwardlan"
511 # chain/rule maintenance
513 if [ "${ban_action}" = "reload" ] && "${ban_nftcmd}" -t list set inet banIP "${feed}" >/dev
/null
2>&1; then
514 ruleset_raw
="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
516 printf "%s\n" "flush set inet banIP ${feed}"
517 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"wan-input
\"][@.
expr[0].match.right
=\"@
${feed}\"].handle
")"
518 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
519 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"wan-forward
\"][@.
expr[0].match.right
=\"@
${feed}\"].handle
")"
520 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-forward handle ${handle}"
521 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"lan-forward
\"][@.
expr[0].match.right
=\"@
${feed}\"].handle
")"
522 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
526 # restore local backups during init
528 if { [ "${ban_action}" != "reload" ] || [ "${feed_url}" = "local" ]; } && [ "${feed%v*}" != "allowlist" ] && [ "${feed%v*}" != "blocklist" ]; then
529 f_restore
"${feed}" "${feed_url}" "${tmp_load}"
531 feed_rc
="${restore_rc}"
536 if [ "${feed%v*}" = "allowlist" ]; then
538 printf "%s\n\n" "#!/usr/sbin/nft -f"
539 [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
540 if [ "${proto}" = "MAC" ]; then
541 "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${ban_allowlist}" >"${tmp_file}"
542 printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
543 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} counter accept"
544 elif [ "${proto}" = "4" ]; then
545 "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s, ",$1}' "${ban_allowlist}" >"${tmp_file}"
546 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
547 if [ -z "${feed_direction##*input*}" ]; then
548 if [ "${ban_allowlistonly}" = "1" ]; then
549 printf "%s\n" "add rule inet banIP wan-input ip saddr != @${feed} ${log_input} counter drop"
551 printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} counter accept"
554 if [ -z "${feed_direction##*forwardwan*}" ]; then
555 if [ "${ban_allowlistonly}" = "1" ]; then
556 printf "%s\n" "add rule inet banIP wan-forward ip saddr != @${feed} ${log_forwardwan} counter drop"
558 printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} counter accept"
561 if [ -z "${feed_direction##*forwardlan*}" ]; then
562 if [ "${ban_allowlistonly}" = "1" ]; then
563 printf "%s\n" "add rule inet banIP lan-forward ip daddr != @${feed} ${log_forwardlan} counter reject with icmp type admin-prohibited"
565 printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} counter accept"
568 elif [ "${proto}" = "6" ]; then
569 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_allowlist}" |
570 "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s, ",tolower($1)}' >"${tmp_file}"
571 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
572 if [ -z "${feed_direction##*input*}" ]; then
573 if [ "${ban_allowlistonly}" = "1" ]; then
574 printf "%s\n" "add rule inet banIP wan-input ip6 saddr != @${feed} ${log_input} counter drop"
576 printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} counter accept"
579 if [ -z "${feed_direction##*forwardwan*}" ]; then
580 if [ "${ban_allowlistonly}" = "1" ]; then
581 printf "%s\n" "add rule inet banIP wan-forward ip6 saddr != @${feed} ${log_forwardwan} counter drop"
583 printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} counter accept"
586 if [ -z "${feed_direction##*forwardlan*}" ]; then
587 if [ "${ban_allowlistonly}" = "1" ]; then
588 printf "%s\n" "add rule inet banIP lan-forward ip6 daddr != @${feed} ${log_forwardlan} counter reject with icmpv6 type admin-prohibited"
590 printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} counter accept"
596 elif [ "${feed%v*}" = "blocklist" ]; then
598 printf "%s\n\n" "#!/usr/sbin/nft -f"
599 [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
600 if [ "${proto}" = "MAC" ]; then
601 "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${ban_blocklist}" >"${tmp_file}"
602 printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
603 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} ${log_forwardlan} counter reject"
604 elif [ "${proto}" = "4" ]; then
605 if [ "${ban_deduplicate}" = "1" ]; then
606 "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s,\n",$1}' "${ban_blocklist}" >"${tmp_raw}"
607 "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
608 "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
609 cat "${tmp_raw}" 2>/dev
/null
>"${ban_blocklist}"
611 "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s,\n",$1}' "${ban_blocklist}" >"${tmp_split}"
613 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
614 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval, timeout; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
615 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${log_input} counter drop"
616 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} ${log_forwardwan} counter drop"
617 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} ${log_forwardlan} counter reject with icmp type admin-prohibited"
618 elif [ "${proto}" = "6" ]; then
619 if [ "${ban_deduplicate}" = "1" ]; then
620 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
621 "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s,\n",tolower($1)}' >"${tmp_raw}"
622 "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
623 "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
624 cat "${tmp_raw}" 2>/dev
/null
>"${ban_blocklist}"
626 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
627 "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s,\n",tolower($1)}' >"${tmp_split}"
629 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
630 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval, timeout; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
631 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${log_input} counter drop"
632 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} ${log_forwardwan} counter drop"
633 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} ${log_forwardlan} counter reject with icmpv6 type admin-prohibited"
637 # handle external downloads
639 elif [ "${restore_rc}" != "0" ] && [ "${feed_url}" != "local" ]; then
640 # handle country downloads
642 if [ "${feed%v*}" = "country" ]; then
643 for country
in ${ban_country}; do
644 feed_log
="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}${country}-aggregated.zone
" 2>&1)"
646 [ "${feed_rc}" = "0" ] && cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
650 # handle asn downloads
652 elif [ "${feed%v*}" = "asn" ]; then
653 for asn
in ${ban_asn}; do
654 feed_log
="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}AS${asn}" 2>&1)"
656 [ "${feed_rc}" = "0" ] && cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
660 # handle compressed downloads
662 elif [ -n "${feed_flag}" ]; then
663 case "${feed_flag}" in
665 feed_log
="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}" 2>&1)"
667 if [ "${feed_rc}" = "0" ]; then
668 zcat
"${tmp_raw}" 2>/dev
/null
>"${tmp_load}"
675 # handle normal downloads
678 feed_log
="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_load}" "${feed_url}" 2>&1)"
685 if [ "${restore_rc}" != "0" ] && [ "${feed_rc}" = "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
686 f_backup
"${feed}" "${tmp_load}"
688 elif [ -z "${restore_rc}" ] && [ "${feed_rc}" != "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
689 f_restore
"${feed}" "${feed_url}" "${tmp_load}" "${feed_rc}"
693 # build nft file with set and rules for regular downloads
695 if [ "${feed_rc}" = "0" ] && [ ! -s "${tmp_nft}" ]; then
698 if [ "${ban_deduplicate}" = "1" ] && [ "${feed_url}" != "local" ]; then
699 "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_raw}"
700 "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null | tee -a "${ban_tmpfile}.deduplicate" >"${tmp_split}"
702 "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_split}"
707 if [ "${feed_rc}" = "0" ]; then
708 if [ -n "${ban_splitsize//[![:digit]]/}" ] && [ "${ban_splitsize//[![:digit]]/}" -gt "0" ]; then
709 if ! "${ban_awkcmd}" "NR%${ban_splitsize//[![:digit]]/}==1{file=\"${tmp_file}.\"++i;}{ORS=\" \";print > file}" "${tmp_split}" 2>/dev
/null
; then
710 rm -f "${tmp_file}".
*
711 f_log
"info" "failed to split ${feed} set to size '${ban_splitsize//[![:digit]]/}'"
714 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}.1"
718 rm -f "${tmp_raw}" "${tmp_load}"
719 if [ "${feed_rc}" = "0" ] && [ "${proto}" = "4" ]; then
721 # nft header (IPv4 set)
723 printf "%s\n\n" "#!/usr/sbin/nft -f"
724 [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
725 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}.1") }"
727 # input and forward rules
729 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${log_input} counter drop"
730 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} ${log_forwardwan} counter drop"
731 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} ${log_forwardlan} counter reject with icmp type admin-prohibited"
733 elif [ "${feed_rc}" = "0" ] && [ "${proto}" = "6" ]; then
735 # nft header (IPv6 set)
737 printf "%s\n\n" "#!/usr/sbin/nft -f"
738 [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
739 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}.1") }"
741 # input and forward rules
743 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${log_input} counter drop"
744 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} ${log_forwardwan} counter drop"
745 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} ${log_forwardlan} counter reject with icmpv6 type admin-prohibited"
750 # load generated nft file in banIP table
752 if [ "${feed_rc}" = "0" ]; then
753 cnt_dl
="$("${ban_awkcmd}" 'END{printf "%d
",NR}' "${tmp_split}" 2>/dev/null)"
754 if [ "${cnt_dl:-"0"}" -gt "0" ] || [ "${feed_url}" = "local" ] || [ "${feed%v*}" = "allowlist" ] || [ "${feed%v*}" = "blocklist" ]; then
755 feed_log
="$("${ban_nftcmd}" -f "${tmp_nft}" 2>&1)"
757 # load additional split files
759 if [ "${feed_rc}" = "0" ]; then
760 for split_file
in "${tmp_file}".
*; do
761 [ ! -f "${split_file}" ] && break
762 if [ "${split_file##*.}" = "1" ]; then
763 rm -f "${split_file}"
766 if ! "${ban_nftcmd}" add element inet banIP "${feed}" "{ $(cat "${split_file}") }" >/dev
/null
2>&1; then
767 f_log
"info" "failed to add split file '${split_file##*.}' to ${feed} set"
769 rm -f "${split_file}"
771 if [ "${ban_debug}" = "1" ] && [ "${ban_reportelements}" = "1" ]; then
772 cnt_set
="$("${ban_nftcmd}" -j list set inet banIP "${feed}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)"
776 f_log
"info" "empty feed ${feed} will be skipped"
779 rm -f "${tmp_split}" "${tmp_nft}"
782 f_log
"debug" "f_down ::: name: ${feed}, cnt_dl: ${cnt_dl:-"-"}, cnt_set: ${cnt_set:-"-"}, split_size: ${ban_splitsize:-"-"}, time: $((end_ts - start_ts)), rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
788 local backup_rc feed
="${1}" feed_file
="${2}"
790 gzip -cf "${feed_file}" >"${ban_backupdir}/banIP.${feed}.gz"
793 f_log
"debug" "f_backup ::: name: ${feed}, source: ${feed_file##*/}, target: banIP.${feed}.gz, rc: ${backup_rc}"
800 local tmp_feed restore_rc
="1" feed
="${1}" feed_url="${2}" feed_file="${3}" feed_rc="${4:-"0"}"
802 [ "${feed_rc}" != "0" ] && restore_rc
="${feed_rc}"
803 [ "${feed_url}" = "local" ] && tmp_feed="${feed%v*}v4" || tmp_feed="${feed}"
804 if [ -f "${ban_backupdir}/banIP.${tmp_feed}.gz" ]; then
805 zcat
"${ban_backupdir}/banIP.${tmp_feed}.gz" 2>/dev/null >"${feed_file}"
809 f_log
"debug" "f_restore ::: name: ${feed}, source: banIP.${tmp_feed}.gz, target: ${feed_file##*/}, in_rc: ${feed_rc}, rc: ${restore_rc}"
813 # remove disabled feeds
816 local tmp_del ruleset_raw table_sets handle
set del_set feed_log feed_rc
818 tmp_del
="${ban_tmpfile}.final.delete"
819 ruleset_raw
="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
820 table_sets
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -qe '@.nftables[@.set.table="banIP
"].set.name')"
822 printf "%s\n\n" "#!/usr/sbin/nft -f"
823 for set in ${table_sets}; do
824 if ! printf "%s" "allowlist blocklist ${ban_feed}" | "${ban_grepcmd}" -q "${set%v*}"; then
825 del_set
="${del_set}${set}, "
826 rm -f "${ban_backupdir}/banIP.${set}.gz"
827 printf "%s\n" "flush set inet banIP ${set}"
828 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"wan-input
\"][@.
expr[0].match.right
=\"@
${set}\"].handle
")"
829 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
830 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"wan-forward
\"][@.
expr[0].match.right
=\"@
${set}\"].handle
")"
831 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-forward handle ${handle}"
832 handle
="$(printf "%s
\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables
[@.rule.table
=\"banIP
\"&&@.rule.chain
=\"lan-forward
\"][@.
expr[0].match.right
=\"@
${set}\"].handle
")"
833 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
834 printf "%s\n\n" "delete set inet banIP ${set}"
839 if [ -n "${del_set}" ]; then
840 del_set
="${del_set%%??}"
841 feed_log
="$("${ban_nftcmd}" -f "${tmp_del}" 2>&1)"
846 f_log
"debug" "f_rmset ::: sets: ${del_set:-"-"}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
849 # generate status information
852 local object duration
set table_sets cnt_elements
="0" split="0" status
="${1}"
854 [ -z "${ban_dev}" ] && f_conf
855 if [ "${status}" = "active" ]; then
856 if [ -n "${ban_starttime}" ]; then
857 ban_endtime
="$(date "+%s
")"
858 duration
="$(((ban_endtime - ban_starttime) / 60))m $(((ban_endtime - ban_starttime) % 60))s"
860 table_sets
="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null | jsonfilter -qe '@.nftables[@.set.table="banIP
"].set.name')"
861 if [ "${ban_reportelements}" = "1" ]; then
862 for set in ${table_sets}; do
863 cnt_elements
="$((cnt_elements + $("${ban_nftcmd}" -j list set inet banIP "${set}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)))"
866 runtime
="action: ${ban_action:-"-"}, duration: ${duration:-"-"}, date: $(date "+%Y-
%m-
%d
%H
:%M
:%S
")"
868 [ ${ban_splitsize:-"0"} -gt "0" ] && split="1"
872 json_load_file
"${ban_rtfile}" >/dev
/null
2>&1
873 json_add_string
"status" "${status}"
874 json_add_string
"version" "${ban_ver}"
875 json_add_string
"element_count" "${cnt_elements}"
876 json_add_array
"active_feeds"
877 if [ "${status}" != "active" ]; then
879 json_add_string
"feed" "-"
882 for object
in ${table_sets}; do
884 json_add_string
"feed" "${object}"
889 json_add_array
"active_devices"
890 if [ "${status}" != "active" ]; then
892 json_add_string
"device" "-"
895 for object
in ${ban_dev}; do
897 json_add_string
"device" "${object}"
900 for object
in ${ban_ifv4} ${ban_ifv6}; do
902 json_add_string
"interface" "${object}"
907 json_add_array
"active_subnets"
908 if [ "${status}" != "active" ]; then
910 json_add_string
"subnet" "-"
913 for object
in ${ban_sub}; do
915 json_add_string
"subnet" "${object}"
920 json_add_string
"nft_info" "priority: ${ban_nftpriority}, policy: ${ban_nftpolicy}, loglevel: ${ban_nftloglevel}, expiry: ${ban_nftexpiry:-"-"}"
921 json_add_string
"run_info" "base: ${ban_basedir}, backup: ${ban_backupdir}, report: ${ban_reportdir}, feed: ${ban_feedfile}"
922 json_add_string
"run_flags" "auto: $(f_char ${ban_autodetect}), proto (4/6): $(f_char ${ban_protov4})/$(f_char ${ban_protov6}), log (wan-inp/wan-fwd/lan-fwd): $(f_char ${ban_loginput})/$(f_char ${ban_logforwardwan})/$(f_char ${ban_logforwardlan}), dedup: $(f_char ${ban_deduplicate}), split: $(f_char ${split}), allowed only: $(f_char ${ban_allowlistonly})"
923 json_add_string
"last_run" "${runtime:-"-"}"
924 json_add_string
"system_info" "cores: ${ban_cores}, memory: ${ban_memory}, device: ${ban_sysver}"
925 json_dump
>"${ban_rtfile}"
928 # get status information
931 local key keylist
type value index_key1 index_key2 index_value1 index_value2 actual
="${1}"
933 [ -z "${ban_dev}" ] && f_conf
934 json_load_file
"${ban_rtfile}" >/dev
/null
2>&1
935 if json_get_keys keylist
; then
936 printf "%s\n" "::: banIP runtime information"
937 for key
in ${keylist}; do
938 json_get_var value
"${key}" >/dev
/null
2>&1
939 if [ "${key}" = "status" ]; then
940 value
="${value} ($(f_actual))"
941 elif [ "${key}" = "active_devices" ]; then
942 json_select
"${key}" >/dev
/null
2>&1
944 while json_get_type
type "${index}" && [ "${type}" = "object" ]; do
945 json_get_keys index_key1
"${index}" >/dev
/null
2>&1
946 json_get_keys index_key2
"$((index + 1))" >/dev
/null
2>&1
947 json_get_values index_value1
"${index}" >/dev
/null
2>&1
948 if [ "${index}" = "1" ] && [ "${index_key1// /}" = "device" ] && [ "${index_key2// /}" = "interface" ]; then
949 json_get_values index_value2
"$((index + 1))" >/dev
/null
2>&1
950 value
="${index_value1} ::: ${index_value2}"
951 index
="$((index + 1))"
952 elif [ "${index}" = "1" ]; then
953 value
="${index_value1}"
954 elif [ "${index}" != "1" ] && [ "${index_key1// /}" = "device" ] && [ "${index_key2// /}" = "interface" ]; then
955 json_get_values index_value2
"$((index + 1))" >/dev
/null
2>&1
956 value
="${value}, ${index_value1} ::: ${index_value2}"
957 index
="$((index + 1))"
958 elif [ "${index}" != "1" ]; then
959 value
="${value}, ${index_value1}"
961 index
="$((index + 1))"
964 elif [ "${key%_*}" = "active" ]; then
965 json_select
"${key}" >/dev
/null
2>&1
967 while json_get_type
type "${index}" && [ "${type}" = "object" ]; do
968 json_get_values index_value1
"${index}" >/dev
/null
2>&1
969 if [ "${index}" = "1" ]; then
970 value
="${index_value1}"
972 value
="${value}, ${index_value1}"
974 index
="$((index + 1))"
978 value
="$(printf "%s
" "${value}" |
979 awk '{NR=1;max=118;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{printf"%-24s%s
\n","",substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}')"
980 printf " + %-17s : %s\n" "${key}" "${value:-"-"}"
983 printf "%s\n" "::: no banIP runtime information available"
990 local cnt list domain lookup ip start_time end_time duration cnt_domain
="0" cnt_ip
="0" feed
="${1}"
992 start_time
="$(date "+%s
")"
993 if [ "${feed}" = "allowlist" ]; then
994 list
="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s
",tolower($1)}' "${ban_allowlist}" 2>/dev/null)"
995 elif [ "${feed}" = "blocklist" ]; then
996 list
="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s
",tolower($1)}' "${ban_blocklist}" 2>/dev/null)"
999 for domain
in ${list}; do
1000 lookup
="$("${ban_lookupcmd}" "${domain}" ${ban_resolver} 2>/dev/null | "${ban_awkcmd}" '/^Address[ 0-9]*: /{if(!seen[$NF]++)printf "%s ",$NF}' 2>/dev/null)"
1001 for ip in ${lookup}; do
1002 if [ "${ip%%.*}" = "0" ] || [ -z "${ip%%::*}" ]; then
1005 if { [ "${feed}" = "allowlist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_allowlist}"; } ||
1006 { [ "${feed}" = "blocklist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_blocklist}"; }; then
1007 cnt_ip="$((cnt_ip + 1))"
1008 if [ "${ip##*:}" = "${ip}" ]; then
1009 if ! "${ban_nftcmd}" add element inet banIP "${feed}v4" "{ ${ip} }" >/dev/null 2>&1; then
1010 f_log "info" "failed to add IP '${ip}' (${domain}) to ${feed}v4
set"
1014 if ! "${ban_nftcmd}" add element inet banIP "${feed}v6" "{ ${ip} }" >/dev/null 2>&1; then
1015 f_log "info
" "failed to add IP
'${ip}' (${domain}) to ${feed}v6 set"
1019 if [ "${feed}" = "allowlist" ] && [ "${ban_autoallowlist}" = "1" ]; then
1020 printf "%-42s%s\n" "${ip}" "# '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_allowlist}"
1021 elif [ "${feed}" = "blocklist" ] && [ "${ban_autoblocklist}" = "1" ]; then
1022 printf "%-42s%s\n" "${ip}" "# '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
1027 cnt_domain="$((cnt_domain + 1))"
1029 end_time="$(date "+%s")"
1030 duration="$(((end_time - start_time) / 60))m $(((end_time - start_time) % 60))s"
1032 f_log "debug" "f_lookup ::: name: ${feed}, cnt_domain: ${cnt_domain}, cnt_ip: ${cnt_ip}, duration: ${duration}"
1038 local report_jsn report_txt set tmp_val ruleset_raw table_sets set_cnt set_input set_forwardwan set_forwardlan set_cntinput set_cntforwardwan set_cntforwardlan output="${1}"
1039 local detail set_details jsnval timestamp autoadd_allow autoadd_block sum_sets sum_setinput sum_setforwardwan sum_setforwardlan sum_setelements sum_cntinput sum_cntforwardwan sum_cntforwardlan
1041 [ -z "${ban_dev}" ] && f_conf
1042 f_mkdir "${ban_reportdir}"
1043 report_jsn="${ban_reportdir}/ban_report.jsn"
1044 report_txt="${ban_reportdir}/ban_report.txt"
1046 # json output preparation
1048 ruleset_raw="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
1049 table_sets="$(printf "%s" "${ruleset_raw}" | jsonfilter -qe '@.nftables
[@.
set.table
="banIP"].
set.name
')"
1052 sum_setforwardwan="0"
1053 sum_setforwardlan="0"
1056 sum_cntforwardwan="0"
1057 sum_cntforwardlan="0"
1058 timestamp="$(date "+%Y-%m-%d %H:%M:%S")"
1062 printf "\t%s\n" '"sets": {'
1063 for set in ${table_sets}; do
1064 set_cntinput="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-input\"][@.expr[0].match.right=\"@${set}\"].expr[*].counter.packets")"
1065 set_cntforwardwan="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-forward\"][@.expr[0].match.right=\"@${set}\"].expr[*].counter.packets")"
1066 set_cntforwardlan="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"lan-forward\"][@.expr[0].match.right=\"@${set}\"].expr[*].counter.packets")"
1067 if [ "${ban_reportelements}" = "1" ]; then
1068 set_cnt="$("${ban_nftcmd}" -j list set inet banIP "${set}" 2>/dev/null | jsonfilter -qe '@.nftables
[*].
set.elem
[*]' | wc -l 2>/dev/null)"
1069 sum_setelements="$((sum_setelements + set_cnt))"
1072 sum_setelements="n/a"
1074 if [ -n "${set_cntinput}" ]; then
1076 sum_setinput="$((sum_setinput + 1))"
1077 sum_cntinput="$((sum_cntinput + set_cntinput))"
1082 if [ -n "${set_cntforwardwan}" ]; then
1084 sum_setforwardwan="$((sum_setforwardwan + 1))"
1085 sum_cntforwardwan="$((sum_cntforwardwan + set_cntforwardwan))"
1088 set_cntforwardwan=""
1090 if [ -n "${set_cntforwardlan}" ]; then
1092 sum_setforwardlan="$((sum_setforwardlan + 1))"
1093 sum_cntforwardlan="$((sum_cntforwardlan + set_cntforwardlan))"
1096 set_cntforwardlan=""
1098 [ "${sum_sets}" -gt "0" ] && printf "%s\n" ","
1099 printf "\t\t%s\n" "\"${set}\": {"
1100 printf "\t\t\t%s\n" "\"cnt_elements\": \"${set_cnt}\","
1101 printf "\t\t\t%s\n" "\"cnt_input\": \"${set_cntinput}\","
1102 printf "\t\t\t%s\n" "\"input\": \"${set_input}\","
1103 printf "\t\t\t%s\n" "\"cnt_forwardwan\": \"${set_cntforwardwan}\","
1104 printf "\t\t\t%s\n" "\"wan_forward\": \"${set_forwardwan}\","
1105 printf "\t\t\t%s\n" "\"cnt_forwardlan\": \"${set_cntforwardlan}\","
1106 printf "\t\t\t%s\n" "\"lan_forward\": \"${set_forwardlan}\""
1108 sum_sets="$((sum_sets + 1))"
1110 printf "\n\t%s\n" "},"
1111 printf "\t%s\n" "\"timestamp\": \"${timestamp}\","
1112 printf "\t%s\n" "\"autoadd_allow\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_allowlist}")\","
1113 printf "\t%s\n" "\"autoadd_block\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_blocklist}")\","
1114 printf "\t%s\n" "\"sum_sets\": \"${sum_sets}\","
1115 printf "\t%s\n" "\"sum_setinput\": \"${sum_setinput}\","
1116 printf "\t%s\n" "\"sum_setforwardwan\": \"${sum_setforwardwan}\","
1117 printf "\t%s\n" "\"sum_setforwardlan\": \"${sum_setforwardlan}\","
1118 printf "\t%s\n" "\"sum_setelements\": \"${sum_setelements}\","
1119 printf "\t%s\n" "\"sum_cntinput\": \"${sum_cntinput}\","
1120 printf "\t%s\n" "\"sum_cntforwardwan\": \"${sum_cntforwardwan}\","
1121 printf "\t%s\n" "\"sum_cntforwardlan\": \"${sum_cntforwardlan}\""
1125 # text output preparation
1127 if [ "${output}" != "json" ] && [ -s "${report_jsn}" ]; then
1130 if json_load_file "${report_jsn}" >/dev/null 2>&1; then
1131 json_get_var timestamp "timestamp" >/dev/null 2>&1
1132 json_get_var autoadd_allow "autoadd_allow" >/dev/null 2>&1
1133 json_get_var autoadd_block "autoadd_block" >/dev/null 2>&1
1134 json_get_var sum_sets "sum_sets" >/dev/null 2>&1
1135 json_get_var sum_setinput "sum_setinput" >/dev/null 2>&1
1136 json_get_var sum_setforwardwan "sum_setforwardwan" >/dev/null 2>&1
1137 json_get_var sum_setforwardlan "sum_setforwardlan" >/dev/null 2>&1
1138 json_get_var sum_setelements "sum_setelements" >/dev/null 2>&1
1139 json_get_var sum_cntinput "sum_cntinput" >/dev/null 2>&1
1140 json_get_var sum_cntforwardwan "sum_cntforwardwan" >/dev/null 2>&1
1141 json_get_var sum_cntforwardlan "sum_cntforwardlan" >/dev/null 2>&1
1143 printf "%s\n%s\n%s\n" ":::" "::: banIP Set Statistics" ":::"
1144 printf "%s\n" " Timestamp: ${timestamp}"
1145 printf "%s\n" " ------------------------------"
1146 printf "%s\n" " auto-added to allowlist today: ${autoadd_allow}"
1147 printf "%s\n\n" " auto-added to blocklist today: ${autoadd_block}"
1148 json_select "sets" >/dev/null 2>&1
1149 json_get_keys table_sets >/dev/null 2>&1
1150 if [ -n "${table_sets}" ]; then
1151 printf "%-25s%-15s%-24s%-24s%s\n" " Set" "| Elements" "| WAN-Input (packets)" "| WAN-Forward (packets)" "| LAN-Forward (packets)"
1152 printf "%s\n" " ---------------------+--------------+-----------------------+-----------------------+------------------------"
1153 for set in ${table_sets}; do
1154 printf " %-21s" "${set}"
1155 json_select "${set}"
1156 json_get_keys set_details
1157 for detail in ${set_details}; do
1158 json_get_var jsnval "${detail}" >/dev/null 2>&1
1161 printf "%-15s" "| ${jsnval}"
1163 "cnt_input" | "cnt_forwardwan" | "cnt_forwardlan")
1164 [ -n "${jsnval}" ] && tmp_val=": ${jsnval}"
1167 printf "%-24s" "| ${jsnval}${tmp_val}"
1175 printf "%s\n" " ---------------------+--------------+-----------------------+-----------------------+------------------------"
1176 printf "%-25s%-15s%-24s%-24s%s\n" " ${sum_sets}" "| ${sum_setelements}" "| ${sum_setinput} (${sum_cntinput})" "| ${sum_setforwardwan} (${sum_cntforwardwan})" "| ${sum_setforwardlan} (${sum_cntforwardlan})"
1182 # output channel (text|json|mail)
1186 [ -s "${report_txt}" ] && cat "${report_txt}"
1189 [ -s "${report_jsn}" ] && cat "${report_jsn}"
1192 [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ] && f_mail
1195 rm -f "${report_txt}"
1201 local table_sets ip proto run_search search="${1}"
1203 if [ -n "${search}" ]; then
1204 ip="$(printf "%s" "${search}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{printf "%s",RT}')"
1205 [ -n "${ip}" ] && proto="v4
"
1206 if [ -z "${proto}" ]; then
1207 ip="$
(printf "%s" "${search}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?){3,7}[A-Fa-f0-9]{1,4}"}{printf "%s",RT
}')"
1208 [ -n "${ip}" ] && proto="v6"
1211 if [ -n "${proto}" ]; then
1212 table_sets="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null | jsonfilter -qe "@.nftables[@.set.table=\"banIP\"&&@.set.type=\"ip${proto}_addr\"].set.name")"
1214 printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::"
1217 printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
1218 printf "%s\n" " Looking for IP '${ip}' on $(date "+%Y-%m-%d %H:%M:%S")"
1219 printf "%s\n" " ---"
1221 run_search="/var/run/banIP.search"
1222 for set in ${table_sets}; do
1224 if "${ban_nftcmd}" get element inet banIP "${set}" "{ ${ip} }" >/dev/null 2>&1; then
1225 printf "%s\n" " IP found in Set '${set}'"
1229 hold="$((cnt % ban_cores))"
1230 [ "${hold}" = "0" ] && wait
1234 if [ ! -f "${run_search}" ]; then
1235 printf "%s\n" " IP not found"
1237 rm -f "${run_search}"
1244 local set_elements set="${1}"
1246 [ -n "${set}" ] && set_elements="$("${ban_nftcmd}" -j list set inet banIP "${set}" 2>/dev/null | jsonfilter -qe '@.nftables
[*].
set.elem
[*]')"
1248 if [ -z "${set}" ] || [ -z "${set_elements}" ]; then
1249 printf "%s\n%s\n%s\n" ":::" "::: no valid survey input" ":::"
1252 printf "%s\n%s\n%s\n" ":::" "::: banIP Survey" ":::"
1253 printf "%s\n" " List the elements of Set '${set}' on $(date "+%Y-%m-%d %H:%M:%S")"
1254 printf "%s\n" " ---"
1255 printf "%s\n" "${set_elements}"
1263 # load mail template
1265 [ -r "${ban_mailtemplate}" ] && . "${ban_mailtemplate}" || f_log "info" "the mail template is missing"
1266 [ -z "${mail_text}" ] && f_log "info" "the 'mail_text
' template variable is empty"
1267 [ "${ban_debug}" = "1" ] && msmtp_debug="--debug"
1271 ban_mailhead="From: ${ban_mailsender}\nTo: ${ban_mailreceiver}\nSubject: ${ban_mailtopic}\nReply-to: ${ban_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
1272 if printf "%b" "${ban_mailhead}${mail_text}" | "${ban_mailcmd}" --timeout=10 ${msmtp_debug} -a "${ban_mailprofile}" "${ban_mailreceiver}" >/dev/null 2>&1; then
1273 f_log "info" "status mail was sent successfully"
1275 f_log "info" "failed to send status mail (${?})"
1278 f_log "debug" "f_mail ::: notification: ${ban_mailnotification}, template: ${ban_mailtemplate}, profile: ${ban_mailprofile}, receiver: ${ban_mailreceiver}, rc: ${?}"
1281 # check banIP availability and initial sourcing
1284 if [ "${ban_action}" != "stop" ]; then
1285 if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]; then
1286 . "/lib/functions.sh"
1287 . "/lib/functions/network.sh"
1288 . "/usr/share/libubox/jshn.sh"
1290 f_log "err" "system libraries not found"
1292 [ ! -d "/etc/banip" ] && f_log "err" "banIP config directory not found, please re-install the package"
1293 [ ! -r "/etc/banip/banip.feeds" ] && f_log "err" "banIP feed file not found, please re-install the package"
1294 [ ! -r "/etc/config/banip" ] && f_log "err" "banIP config not found, please re-install the package"
1295 [ "$(uci_get banip global ban_enabled)" = "0" ] && f_log "err" "banIP is currently disabled, please set the config option 'ban_enabled
' to '1' to use this service"