Merge pull request #21024 from commodo/python-updates1
[feed/packages.git] / net / banip / files / banip-functions.sh
1 # banIP shared function library/include - ban incoming and outgoing IPs via named nftables Sets
2 # Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
3 # This is free software, licensed under the GNU General Public License v3.
4
5 # (s)hellcheck exceptions
6 # shellcheck disable=all
7
8 # set initial defaults
9 #
10 export LC_ALL=C
11 export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
12
13 ban_basedir="/tmp"
14 ban_backupdir="/tmp/banIP-backup"
15 ban_reportdir="/tmp/banIP-report"
16 ban_feedfile="/etc/banip/banip.feeds"
17 ban_customfeedfile="/etc/banip/banip.custom.feeds"
18 ban_allowlist="/etc/banip/banip.allowlist"
19 ban_blocklist="/etc/banip/banip.blocklist"
20 ban_mailtemplate="/etc/banip/banip.tpl"
21 ban_pidfile="/var/run/banip.pid"
22 ban_rtfile="/var/run/banip_runtime.json"
23 ban_lock="/var/run/banip.lock"
24 ban_fetchcmd=""
25 ban_logreadcmd="$(command -v logread)"
26 ban_logcmd="$(command -v logger)"
27 ban_ubuscmd="$(command -v ubus)"
28 ban_nftcmd="$(command -v nft)"
29 ban_fw4cmd="$(command -v fw4)"
30 ban_awkcmd="$(command -v awk)"
31 ban_grepcmd="$(command -v grep)"
32 ban_sedcmd="$(command -v sed)"
33 ban_catcmd="$(command -v cat)"
34 ban_zcatcmd="$(command -v zcat)"
35 ban_lookupcmd="$(command -v nslookup)"
36 ban_mailcmd="$(command -v msmtp)"
37 ban_mailsender="no-reply@banIP"
38 ban_mailreceiver=""
39 ban_mailtopic="banIP notification"
40 ban_mailprofile="ban_notify"
41 ban_mailnotifcation="0"
42 ban_reportelements="1"
43 ban_nftloglevel="warn"
44 ban_nftpriority="-200"
45 ban_nftpolicy="memory"
46 ban_nftexpiry=""
47 ban_loglimit="100"
48 ban_logcount="1"
49 ban_logterm=""
50 ban_country=""
51 ban_asn=""
52 ban_loginput="1"
53 ban_logforwardwan="1"
54 ban_logforwardlan="0"
55 ban_allowurl=""
56 ban_allowlistonly="0"
57 ban_autoallowlist="1"
58 ban_autoallowuplink="subnet"
59 ban_autoblocklist="1"
60 ban_deduplicate="1"
61 ban_splitsize="0"
62 ban_autodetect="1"
63 ban_feed=""
64 ban_blockpolicy=""
65 ban_blockinput=""
66 ban_blockforwardwan=""
67 ban_blockforwardlan=""
68 ban_protov4="0"
69 ban_protov6="0"
70 ban_ifv4=""
71 ban_ifv6=""
72 ban_dev=""
73 ban_uplink=""
74 ban_fetchinsecure=""
75 ban_fetchretry="5"
76 ban_cores=""
77 ban_memory=""
78 ban_packages=""
79 ban_trigger=""
80 ban_triggerdelay="10"
81 ban_resolver=""
82 ban_enabled="0"
83 ban_debug="0"
84
85 # gather system information
86 #
87 f_system() {
88 local cpu core
89
90 if [ -z "${ban_dev}" ]; then
91 ban_debug="$(uci_get banip global ban_debug)"
92 ban_cores="$(uci_get banip global ban_cores)"
93 fi
94 ban_packages="$(${ban_ubuscmd} -S call rpc-sys packagelist '{ "all": true }' 2>/dev/null)"
95 ban_memory="$("${ban_awkcmd}" '/^MemAvailable/{printf "%s",int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
96 ban_ver="$(printf "%s" "${ban_packages}" | jsonfilter -ql1 -e '@.packages.banip')"
97 ban_sysver="$(${ban_ubuscmd} -S call system board 2>/dev/null | jsonfilter -ql1 -e '@.model' -e '@.release.description' |
98 "${ban_awkcmd}" 'BEGIN{RS="";FS="\n"}{printf "%s, %s",$1,$2}')"
99 if [ -z "${ban_cores}" ]; then
100 cpu="$("${ban_grepcmd}" -c '^processor' /proc/cpuinfo 2>/dev/null)"
101 core="$("${ban_grepcmd}" -cm1 '^core id' /proc/cpuinfo 2>/dev/null)"
102 [ "${cpu}" = "0" ] && cpu="1"
103 [ "${core}" = "0" ] && core="1"
104 ban_cores="$((cpu * core))"
105 fi
106 }
107
108 # create directories
109 #
110 f_mkdir() {
111 local dir="${1}"
112
113 if [ ! -d "${dir}" ]; then
114 rm -f "${dir}"
115 mkdir -p "${dir}"
116 f_log "debug" "f_mkdir ::: directory: ${dir}"
117 fi
118 }
119
120 # create files
121 #
122 f_mkfile() {
123 local file="${1}"
124
125 if [ ! -f "${file}" ]; then
126 : >"${file}"
127 f_log "debug" "f_mkfile ::: file: ${file}"
128 fi
129 }
130
131 # create temporary files and directories
132 #
133 f_tmp() {
134 f_mkdir "${ban_basedir}"
135 ban_tmpdir="$(mktemp -p "${ban_basedir}" -d)"
136 ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
137
138 f_log "debug" "f_tmp ::: base_dir: ${ban_basedir:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}"
139 }
140
141 # remove directories
142 #
143 f_rmdir() {
144 local dir="${1}"
145
146 if [ -d "${dir}" ]; then
147 rm -rf "${dir}"
148 f_log "debug" "f_rmdir ::: directory: ${dir}"
149 fi
150 }
151
152 # convert chars
153 #
154 f_char() {
155 local char="${1}"
156
157 if [ "${char}" = "1" ]; then
158 printf "%s" "✔"
159 elif [ "${char}" = "0" ] || [ -z "${char}" ]; then
160 printf "%s" "✘"
161 else
162 printf "%s" "${char}"
163 fi
164 }
165
166 # trim strings
167 #
168 f_trim() {
169 local string="${1}"
170
171 string="${string#"${string%%[![:space:]]*}"}"
172 string="${string%"${string##*[![:space:]]}"}"
173 printf "%s" "${string}"
174 }
175
176 # remove log monitor
177 #
178 f_rmpid() {
179 local ppid pid pids
180
181 ppid="$("${ban_catcmd}" "${ban_pidfile}" 2>/dev/null)"
182 [ -n "${ppid}" ] && pids="$(pgrep -P "${ppid}" 2>/dev/null)"
183 for pid in ${pids}; do
184 kill -INT "${pid}" >/dev/null 2>&1
185 done
186 : >"${ban_pidfile}"
187 }
188
189 # write log messages
190 #
191 f_log() {
192 local class="${1}" log_msg="${2}"
193
194 if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" = "1" ]; }; then
195 if [ -x "${ban_logcmd}" ]; then
196 "${ban_logcmd}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
197 else
198 printf "%s %s %s\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
199 fi
200 fi
201 if [ "${class}" = "err" ]; then
202 "${ban_nftcmd}" delete table inet banIP >/dev/null 2>&1
203 if [ "${ban_enabled}" = "1" ]; then
204 f_genstatus "error"
205 [ "${ban_mailnotification}" = "1" ] && [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ] && f_mail
206 else
207 f_genstatus "disabled"
208 fi
209 f_rmdir "${ban_tmpdir}"
210 f_rmpid
211 rm -rf "${ban_lock}"
212 exit 1
213 fi
214 }
215
216 # load config
217 #
218 f_conf() {
219 unset ban_dev ban_ifv4 ban_ifv6 ban_feed ban_allowurl ban_blockinput ban_blockforwardwan ban_blockforwardlan ban_logterm ban_country ban_asn
220 config_cb() {
221 option_cb() {
222 local option="${1}"
223 local value="${2}"
224 eval "${option}=\"${value}\""
225 }
226 list_cb() {
227 local option="${1}"
228 local value="${2}"
229 case "${option}" in
230 "ban_dev")
231 eval "${option}=\"$(printf "%s" "${ban_dev}")${value} \""
232 ;;
233 "ban_ifv4")
234 eval "${option}=\"$(printf "%s" "${ban_ifv4}")${value} \""
235 ;;
236 "ban_ifv6")
237 eval "${option}=\"$(printf "%s" "${ban_ifv6}")${value} \""
238 ;;
239 "ban_feed")
240 eval "${option}=\"$(printf "%s" "${ban_feed}")${value} \""
241 ;;
242 "ban_allowurl")
243 eval "${option}=\"$(printf "%s" "${ban_allowurl}")${value} \""
244 ;;
245 "ban_blockinput")
246 eval "${option}=\"$(printf "%s" "${ban_blockinput}")${value} \""
247 ;;
248 "ban_blockforwardwan")
249 eval "${option}=\"$(printf "%s" "${ban_blockforwardwan}")${value} \""
250 ;;
251 "ban_blockforwardlan")
252 eval "${option}=\"$(printf "%s" "${ban_blockforwardlan}")${value} \""
253 ;;
254 "ban_logterm")
255 eval "${option}=\"$(printf "%s" "${ban_logterm}")${value}\\|\""
256 ;;
257 "ban_country")
258 eval "${option}=\"$(printf "%s" "${ban_country}")${value} \""
259 ;;
260 "ban_asn")
261 eval "${option}=\"$(printf "%s" "${ban_asn}")${value} \""
262 ;;
263 esac
264 }
265 }
266 config_load banip
267
268 [ "${ban_action}" = "boot" ] && [ -z "${ban_trigger}" ] && sleep ${ban_triggerdelay}
269 }
270
271 # get nft/monitor actuals
272 #
273 f_actual() {
274 local nft monitor
275
276 if "${ban_nftcmd}" -t list set inet banIP allowlistvMAC >/dev/null 2>&1; then
277 nft="$(f_char "1")"
278 else
279 nft="$(f_char "0")"
280 fi
281 if pgrep -f "${ban_logreadcmd##*/}" -P "$("${ban_catcmd}" "${ban_pidfile}" 2>/dev/null)" >/dev/null 2>&1; then
282 monitor="$(f_char "1")"
283 else
284 monitor="$(f_char "0")"
285 fi
286 printf "%s" "nft: ${nft}, monitor: ${monitor}"
287 }
288
289 # get fetch utility
290 #
291 f_getfetch() {
292 local item utils insecure update="0"
293
294 if { [ "${ban_fetchcmd}" = "uclient-fetch" ] && printf "%s" "${ban_packages}" | "${ban_grepcmd}" -q '"libustream-'; } ||
295 { [ "${ban_fetchcmd}" = "wget" ] && printf "%s" "${ban_packages}" | "${ban_grepcmd}" -q '"wget-ssl'; } ||
296 [ "${ban_fetchcmd}" = "curl" ] || [ "${ban_fetchcmd}" = "aria2c" ]; then
297 ban_fetchcmd="$(command -v "${ban_fetchcmd}")"
298 else
299 ban_fetchcmd=""
300 fi
301
302 if [ "${ban_autodetect}" = "1" ] && [ ! -x "${ban_fetchcmd}" ]; then
303 utils="aria2c curl wget uclient-fetch"
304 for item in ${utils}; do
305 if { [ "${item}" = "uclient-fetch" ] && printf "%s" "${ban_packages}" | "${ban_grepcmd}" -q '"libustream-'; } ||
306 { [ "${item}" = "wget" ] && printf "%s" "${ban_packages}" | "${ban_grepcmd}" -q '"wget-ssl'; } ||
307 [ "${item}" = "curl" ] || [ "${item}" = "aria2c" ]; then
308 ban_fetchcmd="$(command -v "${item}")"
309 if [ -x "${ban_fetchcmd}" ]; then
310 update="1"
311 uci_set banip global ban_fetchcmd "${item}"
312 uci_commit "banip"
313 break
314 fi
315 fi
316 done
317 fi
318
319 [ ! -x "${ban_fetchcmd}" ] && f_log "err" "no download utility with SSL support"
320 case "${ban_fetchcmd##*/}" in
321 "aria2c")
322 [ "${ban_fetchinsecure}" = "1" ] && insecure="--check-certificate=false"
323 ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 --retry-wait=10 --max-tries=${ban_fetchretry} --max-file-not-found=${ban_fetchretry} --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o"}"
324 ;;
325 "curl")
326 [ "${ban_fetchinsecure}" = "1" ] && insecure="--insecure"
327 ban_fetchparm="${ban_fetchparm:-"${insecure} --connect-timeout 20 --retry-delay 10 --retry ${ban_fetchretry} --retry-all-errors --fail --silent --show-error --location -o"}"
328 ;;
329 "uclient-fetch")
330 [ "${ban_fetchinsecure}" = "1" ] && insecure="--no-check-certificate"
331 ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 -O"}"
332 ;;
333 "wget")
334 [ "${ban_fetchinsecure}" = "1" ] && insecure="--no-check-certificate"
335 ban_fetchparm="${ban_fetchparm:-"${insecure} --no-cache --no-cookies --timeout=20 --waitretry=10 --tries=${ban_fetchretry} --retry-connrefused --max-redirect=0 -O"}"
336 ;;
337 esac
338
339 f_log "debug" "f_getfetch ::: auto/update: ${ban_autodetect}/${update}, cmd: ${ban_fetchcmd:-"-"}, parm: ${ban_fetchparm:-"-"}"
340 }
341
342 # get wan interfaces
343 #
344 f_getif() {
345 local iface update="0"
346
347 if [ "${ban_autodetect}" = "1" ]; then
348 if [ -z "${ban_ifv4}" ]; then
349 network_flush_cache
350 network_find_wan iface
351 if [ -n "${iface}" ] && "${ban_ubuscmd}" -t 10 wait_for network.interface."${iface}" >/dev/null 2>&1; then
352 ban_protov4="1"
353 ban_ifv4="${iface}"
354 uci_set banip global ban_protov4 "1"
355 uci_add_list banip global ban_ifv4 "${iface}"
356 f_log "info" "add IPv4 interface '${iface}' to config"
357 fi
358 fi
359 if [ -z "${ban_ifv6}" ]; then
360 network_flush_cache
361 network_find_wan6 iface
362 if [ -n "${iface}" ] && "${ban_ubuscmd}" -t 10 wait_for network.interface."${iface}" >/dev/null 2>&1; then
363 ban_protov6="1"
364 ban_ifv6="${iface}"
365 uci_set banip global ban_protov6 "1"
366 uci_add_list banip global ban_ifv6 "${iface}"
367 f_log "info" "add IPv6 interface '${iface}' to config"
368 fi
369 fi
370 fi
371 if [ -n "$(uci -q changes "banip")" ]; then
372 update="1"
373 uci_commit "banip"
374 else
375 ban_ifv4="${ban_ifv4%%?}"
376 ban_ifv6="${ban_ifv6%%?}"
377 for iface in ${ban_ifv4} ${ban_ifv6}; do
378 if ! "${ban_ubuscmd}" -t 10 wait_for network.interface."${iface}" >/dev/null 2>&1; then
379 f_log "err" "no wan interface '${iface}'"
380 fi
381 done
382 fi
383 [ -z "${ban_ifv4}" ] && [ -z "${ban_ifv6}" ] && f_log "err" "no wan interfaces"
384
385 f_log "debug" "f_getif ::: auto/update: ${ban_autodetect}/${update}, interfaces (4/6): ${ban_ifv4}/${ban_ifv6}, protocols (4/6): ${ban_protov4}/${ban_protov6}"
386 }
387
388 # get wan devices
389 #
390 f_getdev() {
391 local dev iface update="0" cnt="0" cnt_max="30"
392
393 if [ "${ban_autodetect}" = "1" ]; then
394 while [ "${cnt}" -lt "${cnt_max}" ] && [ -z "${ban_dev}" ]; do
395 network_flush_cache
396 for iface in ${ban_ifv4} ${ban_ifv6}; do
397 network_get_device dev "${iface}"
398 if [ -n "${dev}" ]; then
399 if printf "%s" "${dev}" | "${ban_grepcmd}" -qE "pppoe|6in4"; then
400 dev="${iface}"
401 fi
402 if ! printf " %s " "${ban_dev}" | "${ban_grepcmd}" -q " ${dev} "; then
403 ban_dev="${ban_dev}${dev} "
404 uci_add_list banip global ban_dev "${dev}"
405 f_log "info" "add device '${dev}' to config"
406 fi
407 fi
408 done
409 cnt="$((cnt + 1))"
410 sleep 1
411 done
412 fi
413 if [ -n "$(uci -q changes "banip")" ]; then
414 update="1"
415 uci_commit "banip"
416 fi
417 ban_dev="${ban_dev%%?}"
418 [ -z "${ban_dev}" ] && f_log "err" "no wan devices"
419
420 f_log "debug" "f_getdev ::: auto/update: ${ban_autodetect}/${update}, devices: ${ban_dev}, cnt: ${cnt}"
421 }
422
423 # get local uplink
424 #
425 f_getuplink() {
426 local uplink iface ip update="0"
427
428 if [ "${ban_autoallowlist}" = "1" ] && [ "${ban_autoallowuplink}" != "disable" ]; then
429 for iface in ${ban_ifv4} ${ban_ifv6}; do
430 network_flush_cache
431 if [ "${ban_autoallowuplink}" = "subnet" ]; then
432 network_get_subnet uplink "${iface}"
433 elif [ "${ban_autoallowuplink}" = "ip" ]; then
434 network_get_ipaddr uplink "${iface}"
435 fi
436 if [ -n "${uplink}" ] && ! printf " %s " "${ban_uplink}" | "${ban_grepcmd}" -q " ${uplink} "; then
437 ban_uplink="${ban_uplink}${uplink} "
438 fi
439 if [ "${ban_autoallowuplink}" = "subnet" ]; then
440 network_get_subnet6 uplink "${iface}"
441 elif [ "${ban_autoallowuplink}" = "ip" ]; then
442 network_get_ipaddr6 uplink "${iface}"
443 fi
444 if [ -n "${uplink}" ] && ! printf " %s " "${ban_uplink}" | "${ban_grepcmd}" -q " ${uplink} "; then
445 ban_uplink="${ban_uplink}${uplink} "
446 fi
447 done
448 for ip in ${ban_uplink}; do
449 if ! "${ban_grepcmd}" -q "${ip} " "${ban_allowlist}"; then
450 if [ "${update}" = "0" ]; then
451 "${ban_sedcmd}" -i '/# uplink added on /d' "${ban_allowlist}"
452 fi
453 printf "%-42s%s\n" "${ip}" "# uplink added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_allowlist}"
454 f_log "info" "add uplink '${ip}' to local allowlist"
455 update="1"
456 fi
457 done
458 ban_uplink="${ban_uplink%%?}"
459 elif [ "${ban_autoallowlist}" = "1" ] && [ "${ban_autoallowuplink}" = "disable" ]; then
460 "${ban_sedcmd}" -i '/# uplink added on /d' "${ban_allowlist}"
461 update="1"
462 fi
463
464 f_log "debug" "f_getuplink ::: auto/update: ${ban_autoallowlist}/${update}, uplink: ${ban_uplink:-"-"}"
465 }
466
467 # get feed information
468 #
469 f_getfeed() {
470 json_init
471 if [ -s "${ban_customfeedfile}" ]; then
472 if ! json_load_file "${ban_customfeedfile}" >/dev/null 2>&1; then
473 f_log "info" "can't load banIP custom feed file"
474 if ! json_load_file "${ban_feedfile}" >/dev/null 2>&1; then
475 f_log "err" "can't load banIP feed file"
476 fi
477 fi
478 elif ! json_load_file "${ban_feedfile}" >/dev/null 2>&1; then
479 f_log "err" "can't load banIP feed file"
480 fi
481 }
482
483 # get Set elements
484 #
485 f_getelements() {
486 local file="${1}"
487
488 [ -s "${file}" ] && printf "%s" "elements={ $("${ban_catcmd}" "${file}" 2>/dev/null) };"
489 }
490
491 # build initial nft file with base table, chains and rules
492 #
493 f_nftinit() {
494 local feed_log feed_rc file="${1}"
495
496 {
497 # nft header (tables and chains)
498 #
499 printf "%s\n\n" "#!/usr/sbin/nft -f"
500 if "${ban_nftcmd}" -t list set inet banIP allowlistvMAC >/dev/null 2>&1; then
501 printf "%s\n" "delete table inet banIP"
502 fi
503 printf "%s\n" "add table inet banIP"
504 printf "%s\n" "add chain inet banIP wan-input { type filter hook input priority ${ban_nftpriority}; policy accept; }"
505 printf "%s\n" "add chain inet banIP wan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
506 printf "%s\n" "add chain inet banIP lan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
507
508 # default wan-input rules
509 #
510 printf "%s\n" "add rule inet banIP wan-input ct state established,related counter accept"
511 printf "%s\n" "add rule inet banIP wan-input iifname != { ${ban_dev// /, } } counter accept"
512 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv4 udp sport 67-68 udp dport 67-68 counter accept"
513 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 udp sport 547 udp dport 546 counter accept"
514 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv4 icmp type { echo-request } limit rate 1000/second counter accept"
515 printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { echo-request } limit rate 1000/second counter accept"
516 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"
517 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"
518
519 # default wan-forward rules
520 #
521 printf "%s\n" "add rule inet banIP wan-forward ct state established,related counter accept"
522 printf "%s\n" "add rule inet banIP wan-forward iifname != { ${ban_dev// /, } } counter accept"
523
524 # default lan-forward rules
525 #
526 printf "%s\n" "add rule inet banIP lan-forward ct state established,related counter accept"
527 printf "%s\n" "add rule inet banIP lan-forward oifname != { ${ban_dev// /, } } counter accept"
528 } >"${file}"
529
530 # load initial banIP table within nft (atomic load)
531 #
532 feed_log="$("${ban_nftcmd}" -f "${file}" 2>&1)"
533 feed_rc="${?}"
534
535 f_log "debug" "f_nftinit ::: devices: ${ban_dev}, priority: ${ban_nftpriority}, policy: ${ban_nftpolicy}, loglevel: ${ban_nftloglevel}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
536 return ${feed_rc}
537 }
538
539 # handle downloads
540 #
541 f_down() {
542 local log_input log_forwardwan log_forwardlan start_ts end_ts tmp_raw tmp_load tmp_file split_file ruleset_raw handle
543 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}"
544
545 start_ts="$(date +%s)"
546 feed="${feed}v${proto}"
547 tmp_load="${ban_tmpfile}.${feed}.load"
548 tmp_raw="${ban_tmpfile}.${feed}.raw"
549 tmp_split="${ban_tmpfile}.${feed}.split"
550 tmp_file="${ban_tmpfile}.${feed}.file"
551 tmp_flush="${ban_tmpfile}.${feed}.flush"
552 tmp_nft="${ban_tmpfile}.${feed}.nft"
553 tmp_allow="${ban_tmpfile}.${feed%v*}"
554
555 [ "${ban_loginput}" = "1" ] && log_input="log level ${ban_nftloglevel} prefix \"banIP/inp-wan/drp/${feed}: \""
556 [ "${ban_logforwardwan}" = "1" ] && log_forwardwan="log level ${ban_nftloglevel} prefix \"banIP/fwd-wan/drp/${feed}: \""
557 [ "${ban_logforwardlan}" = "1" ] && log_forwardlan="log level ${ban_nftloglevel} prefix \"banIP/fwd-lan/rej/${feed}: \""
558
559 # set feed block direction
560 #
561 if [ "${ban_blockpolicy}" = "input" ]; then
562 if ! printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}" &&
563 ! printf "%s" "${ban_blockforwardwan}" | "${ban_grepcmd}" -q "${feed%v*}" &&
564 ! printf "%s" "${ban_blockforwardlan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
565 ban_blockinput="${ban_blockinput} ${feed%v*}"
566 fi
567 elif [ "${ban_blockpolicy}" = "forwardwan" ]; then
568 if ! printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}" &&
569 ! printf "%s" "${ban_blockforwardwan}" | "${ban_grepcmd}" -q "${feed%v*}" &&
570 ! printf "%s" "${ban_blockforwardlan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
571 ban_blockforwardwan="${ban_blockforwardwan} ${feed%v*}"
572 fi
573 elif [ "${ban_blockpolicy}" = "forwardlan" ]; then
574 if ! printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}" &&
575 ! printf "%s" "${ban_blockforwardwan}" | "${ban_grepcmd}" -q "${feed%v*}" &&
576 ! printf "%s" "${ban_blockforwardlan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
577 ban_blockforwardlan="${ban_blockforwardlan} ${feed%v*}"
578 fi
579 fi
580 if printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}"; then
581 feed_direction="input"
582 fi
583 if printf "%s" "${ban_blockforwardwan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
584 feed_direction="${feed_direction} forwardwan"
585 fi
586 if printf "%s" "${ban_blockforwardlan}" | "${ban_grepcmd}" -q "${feed%v*}"; then
587 feed_direction="${feed_direction} forwardlan"
588 fi
589
590 # chain/rule maintenance
591 #
592 if [ "${ban_action}" = "reload" ] && "${ban_nftcmd}" -t list set inet banIP "${feed}" >/dev/null 2>&1; then
593 ruleset_raw="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
594 {
595 printf "%s\n" "flush set inet banIP ${feed}"
596 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-input\"][@.expr[0].match.right=\"@${feed}\"].handle")"
597 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
598 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-forward\"][@.expr[0].match.right=\"@${feed}\"].handle")"
599 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-forward handle ${handle}"
600 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"lan-forward\"][@.expr[0].match.right=\"@${feed}\"].handle")"
601 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
602 } >"${tmp_flush}"
603 fi
604
605 # restore local backups during init
606 #
607 if { [ "${ban_action}" != "reload" ] || [ "${feed_url}" = "local" ]; } && [ "${feed%v*}" != "allowlist" ] && [ "${feed%v*}" != "blocklist" ]; then
608 f_restore "${feed}" "${feed_url}" "${tmp_load}"
609 restore_rc="${?}"
610 feed_rc="${restore_rc}"
611 fi
612
613 # prepare local allowlist
614 #
615 if [ "${feed%v*}" = "allowlist" ] && [ ! -f "${tmp_allow}" ]; then
616 "${ban_catcmd}" "${ban_allowlist}" 2>/dev/null >"${tmp_allow}"
617 for feed_url in ${ban_allowurl}; do
618 feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_load}" "${feed_url}" 2>&1)"
619 feed_rc="${?}"
620 if [ "${feed_rc}" = "0" ] && [ -s "${tmp_load}" ]; then
621 "${ban_catcmd}" "${tmp_load}" 2>/dev/null >>"${tmp_allow}"
622 else
623 f_log "info" "download for feed '${feed%v*}' failed (rc: ${feed_rc:-"-"}/log: ${feed_log})"
624 fi
625 done
626 fi
627
628 # handle local feeds
629 #
630 if [ "${feed%v*}" = "allowlist" ]; then
631 {
632 printf "%s\n\n" "#!/usr/sbin/nft -f"
633 [ -s "${tmp_flush}" ] && "${ban_catcmd}" "${tmp_flush}"
634 if [ "${proto}" = "MAC" ]; then
635 "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${tmp_allow}" >"${tmp_file}"
636 printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
637 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} counter accept"
638 elif [ "${proto}" = "4" ]; then
639 "${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}' "${tmp_allow}" >"${tmp_file}"
640 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
641 if [ -z "${feed_direction##*input*}" ]; then
642 if [ "${ban_allowlistonly}" = "1" ]; then
643 printf "%s\n" "add rule inet banIP wan-input ip saddr != @${feed} ${log_input} counter drop"
644 else
645 printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} counter accept"
646 fi
647 fi
648 if [ -z "${feed_direction##*forwardwan*}" ]; then
649 if [ "${ban_allowlistonly}" = "1" ]; then
650 printf "%s\n" "add rule inet banIP wan-forward ip saddr != @${feed} ${log_forwardwan} counter drop"
651 else
652 printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} counter accept"
653 fi
654 fi
655 if [ -z "${feed_direction##*forwardlan*}" ]; then
656 if [ "${ban_allowlistonly}" = "1" ]; then
657 printf "%s\n" "add rule inet banIP lan-forward ip daddr != @${feed} ${log_forwardlan} counter reject with icmp type admin-prohibited"
658 else
659 printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} counter accept"
660 fi
661 fi
662 elif [ "${proto}" = "6" ]; then
663 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${tmp_allow}" |
664 "${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}"
665 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
666 if [ -z "${feed_direction##*input*}" ]; then
667 if [ "${ban_allowlistonly}" = "1" ]; then
668 printf "%s\n" "add rule inet banIP wan-input ip6 saddr != @${feed} ${log_input} counter drop"
669 else
670 printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} counter accept"
671 fi
672 fi
673 if [ -z "${feed_direction##*forwardwan*}" ]; then
674 if [ "${ban_allowlistonly}" = "1" ]; then
675 printf "%s\n" "add rule inet banIP wan-forward ip6 saddr != @${feed} ${log_forwardwan} counter drop"
676 else
677 printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} counter accept"
678 fi
679 fi
680 if [ -z "${feed_direction##*forwardlan*}" ]; then
681 if [ "${ban_allowlistonly}" = "1" ]; then
682 printf "%s\n" "add rule inet banIP lan-forward ip6 daddr != @${feed} ${log_forwardlan} counter reject with icmpv6 type admin-prohibited"
683 else
684 printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} counter accept"
685 fi
686 fi
687 fi
688 } >"${tmp_nft}"
689 feed_rc="0"
690 elif [ "${feed%v*}" = "blocklist" ]; then
691 {
692 printf "%s\n\n" "#!/usr/sbin/nft -f"
693 [ -s "${tmp_flush}" ] && "${ban_catcmd}" "${tmp_flush}"
694 if [ "${proto}" = "MAC" ]; then
695 "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${ban_blocklist}" >"${tmp_file}"
696 printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
697 [ -z "${feed_direction##*forwardlan*}" ] && printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} ${log_forwardlan} counter reject"
698 elif [ "${proto}" = "4" ]; then
699 if [ "${ban_deduplicate}" = "1" ]; then
700 "${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}"
701 "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
702 "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
703 "${ban_catcmd}" "${tmp_raw}" 2>/dev/null >"${ban_blocklist}"
704 else
705 "${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}"
706 fi
707 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
708 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval, timeout; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
709 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${log_input} counter drop"
710 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} ${log_forwardwan} counter drop"
711 [ -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"
712 elif [ "${proto}" = "6" ]; then
713 if [ "${ban_deduplicate}" = "1" ]; then
714 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
715 "${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}"
716 "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
717 "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
718 "${ban_catcmd}" "${tmp_raw}" 2>/dev/null >"${ban_blocklist}"
719 else
720 "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
721 "${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}"
722 fi
723 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
724 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval, timeout; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}") }"
725 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${log_input} counter drop"
726 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} ${log_forwardwan} counter drop"
727 [ -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"
728 fi
729 } >"${tmp_nft}"
730 feed_rc="0"
731
732 # handle external feeds
733 #
734 elif [ "${restore_rc}" != "0" ] && [ "${feed_url}" != "local" ]; then
735 # handle country downloads
736 #
737 if [ "${feed%v*}" = "country" ]; then
738 for country in ${ban_country}; do
739 feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}${country}-aggregated.zone" 2>&1)"
740 feed_rc="${?}"
741 [ "${feed_rc}" = "0" ] && "${ban_catcmd}" "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
742 done
743 rm -f "${tmp_raw}"
744
745 # handle asn downloads
746 #
747 elif [ "${feed%v*}" = "asn" ]; then
748 for asn in ${ban_asn}; do
749 feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}AS${asn}" 2>&1)"
750 feed_rc="${?}"
751 [ "${feed_rc}" = "0" ] && "${ban_catcmd}" "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
752 done
753 rm -f "${tmp_raw}"
754
755 # handle compressed downloads
756 #
757 elif [ -n "${feed_flag}" ]; then
758 case "${feed_flag}" in
759 "gz")
760 feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}" 2>&1)"
761 feed_rc="${?}"
762 if [ "${feed_rc}" = "0" ]; then
763 "${ban_zcatcmd}" "${tmp_raw}" 2>/dev/null >"${tmp_load}"
764 feed_rc="${?}"
765 fi
766 rm -f "${tmp_raw}"
767 ;;
768 esac
769
770 # handle normal downloads
771 #
772 else
773 feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_load}" "${feed_url}" 2>&1)"
774 feed_rc="${?}"
775 fi
776 fi
777 [ "${feed_rc}" != "0" ] && f_log "info" "download for feed '${feed}' failed (rc: ${feed_rc:-"-"}/log: ${feed_log})"
778
779 # backup/restore
780 #
781 if [ "${restore_rc}" != "0" ] && [ "${feed_rc}" = "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
782 f_backup "${feed}" "${tmp_load}"
783 feed_rc="${?}"
784 elif [ -z "${restore_rc}" ] && [ "${feed_rc}" != "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
785 f_restore "${feed}" "${feed_url}" "${tmp_load}" "${feed_rc}"
786 feed_rc="${?}"
787 fi
788
789 # build nft file with Sets and rules for regular downloads
790 #
791 if [ "${feed_rc}" = "0" ] && [ ! -s "${tmp_nft}" ]; then
792 # deduplicate Sets
793 #
794 if [ "${ban_deduplicate}" = "1" ] && [ "${feed_url}" != "local" ]; then
795 "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_raw}"
796 "${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}"
797 else
798 "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_split}"
799 fi
800 feed_rc="${?}"
801 # split Sets
802 #
803 if [ "${feed_rc}" = "0" ]; then
804 if [ -n "${ban_splitsize//[![:digit]]/}" ] && [ "${ban_splitsize//[![:digit]]/}" -gt "0" ]; then
805 if ! "${ban_awkcmd}" "NR%${ban_splitsize//[![:digit]]/}==1{file=\"${tmp_file}.\"++i;}{ORS=\" \";print > file}" "${tmp_split}" 2>/dev/null; then
806 rm -f "${tmp_file}".*
807 f_log "info" "can't split Set '${feed}' to size '${ban_splitsize//[![:digit]]/}'"
808 fi
809 else
810 "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}.1"
811 fi
812 feed_rc="${?}"
813 fi
814 rm -f "${tmp_raw}" "${tmp_load}"
815 if [ "${feed_rc}" = "0" ] && [ "${proto}" = "4" ]; then
816 {
817 # nft header (IPv4 Set)
818 #
819 printf "%s\n\n" "#!/usr/sbin/nft -f"
820 [ -s "${tmp_flush}" ] && "${ban_catcmd}" "${tmp_flush}"
821 printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}.1") }"
822
823 # input and forward rules
824 #
825 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${log_input} counter drop"
826 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip saddr @${feed} ${log_forwardwan} counter drop"
827 [ -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"
828 } >"${tmp_nft}"
829 elif [ "${feed_rc}" = "0" ] && [ "${proto}" = "6" ]; then
830 {
831 # nft header (IPv6 Set)
832 #
833 printf "%s\n\n" "#!/usr/sbin/nft -f"
834 [ -s "${tmp_flush}" ] && "${ban_catcmd}" "${tmp_flush}"
835 printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy ${ban_nftpolicy}; $(f_getelements "${tmp_file}.1") }"
836
837 # input and forward rules
838 #
839 [ -z "${feed_direction##*input*}" ] && printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${log_input} counter drop"
840 [ -z "${feed_direction##*forwardwan*}" ] && printf "%s\n" "add rule inet banIP wan-forward ip6 saddr @${feed} ${log_forwardwan} counter drop"
841 [ -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"
842 } >"${tmp_nft}"
843 fi
844 fi
845
846 # load generated nft file in banIP table
847 #
848 if [ "${feed_rc}" = "0" ]; then
849 cnt_dl="$("${ban_awkcmd}" 'END{printf "%d",NR}' "${tmp_split}" 2>/dev/null)"
850 if [ "${cnt_dl:-"0"}" -gt "0" ] || [ "${feed_url}" = "local" ] || [ "${feed%v*}" = "allowlist" ] || [ "${feed%v*}" = "blocklist" ]; then
851 feed_log="$("${ban_nftcmd}" -f "${tmp_nft}" 2>&1)"
852 feed_rc="${?}"
853
854 # load additional split files
855 #
856 if [ "${feed_rc}" = "0" ]; then
857 for split_file in "${tmp_file}".*; do
858 [ ! -f "${split_file}" ] && break
859 if [ "${split_file##*.}" = "1" ]; then
860 rm -f "${split_file}"
861 continue
862 fi
863 if ! "${ban_nftcmd}" add element inet banIP "${feed}" "{ $("${ban_catcmd}" "${split_file}") }" >/dev/null 2>&1; then
864 f_log "info" "can't add split file '${split_file##*.}' to Set '${feed}'"
865 fi
866 rm -f "${split_file}"
867 done
868 if [ "${ban_debug}" = "1" ] && [ "${ban_reportelements}" = "1" ]; then
869 cnt_set="$("${ban_nftcmd}" -j list set inet banIP "${feed}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)"
870 fi
871 fi
872 else
873 f_log "info" "skip empty feed '${feed}'"
874 fi
875 fi
876 rm -f "${tmp_split}" "${tmp_nft}"
877 end_ts="$(date +%s)"
878
879 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:-"-"}"
880 }
881
882 # backup feeds
883 #
884 f_backup() {
885 local backup_rc feed="${1}" feed_file="${2}"
886
887 gzip -cf "${feed_file}" >"${ban_backupdir}/banIP.${feed}.gz"
888 backup_rc="${?}"
889
890 f_log "debug" "f_backup ::: name: ${feed}, source: ${feed_file##*/}, target: banIP.${feed}.gz, rc: ${backup_rc}"
891 return ${backup_rc}
892 }
893
894 # restore feeds
895 #
896 f_restore() {
897 local tmp_feed restore_rc="1" feed="${1}" feed_url="${2}" feed_file="${3}" feed_rc="${4:-"0"}"
898
899 [ "${feed_rc}" != "0" ] && restore_rc="${feed_rc}"
900 [ "${feed_url}" = "local" ] && tmp_feed="${feed%v*}v4" || tmp_feed="${feed}"
901 if [ -f "${ban_backupdir}/banIP.${tmp_feed}.gz" ]; then
902 "${ban_zcatcmd}" "${ban_backupdir}/banIP.${tmp_feed}.gz" 2>/dev/null >"${feed_file}"
903 restore_rc="${?}"
904 fi
905
906 f_log "debug" "f_restore ::: name: ${feed}, source: banIP.${tmp_feed}.gz, target: ${feed_file##*/}, in_rc: ${feed_rc}, rc: ${restore_rc}"
907 return ${restore_rc}
908 }
909
910 # remove disabled Sets
911 #
912 f_rmset() {
913 local feedlist tmp_del ruleset_raw item table_sets handle del_set feed_log feed_rc
914
915 f_getfeed
916 json_get_keys feedlist
917 tmp_del="${ban_tmpfile}.final.delete"
918 ruleset_raw="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
919 table_sets="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -qe '@.nftables[@.set.table="banIP"].set.name')"
920 {
921 printf "%s\n\n" "#!/usr/sbin/nft -f"
922 for item in ${table_sets}; do
923 if ! printf "%s" "allowlist blocklist ${ban_feed}" | "${ban_grepcmd}" -q "${item%v*}" ||
924 ! printf "%s" "allowlist blocklist ${feedlist}" | "${ban_grepcmd}" -q "${item%v*}"; then
925 del_set="${del_set}${item}, "
926 rm -f "${ban_backupdir}/banIP.${item}.gz"
927 printf "%s\n" "flush set inet banIP ${item}"
928 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-input\"][@.expr[0].match.right=\"@${item}\"].handle")"
929 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
930 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-forward\"][@.expr[0].match.right=\"@${item}\"].handle")"
931 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-forward handle ${handle}"
932 handle="$(printf "%s\n" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"lan-forward\"][@.expr[0].match.right=\"@${item}\"].handle")"
933 [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
934 printf "%s\n\n" "delete set inet banIP ${item}"
935 fi
936 done
937 } >"${tmp_del}"
938
939 if [ -n "${del_set}" ]; then
940 del_set="${del_set%%??}"
941 feed_log="$("${ban_nftcmd}" -f "${tmp_del}" 2>&1)"
942 feed_rc="${?}"
943 fi
944 rm -f "${tmp_del}"
945
946 f_log "debug" "f_rmset ::: sets: ${del_set:-"-"}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
947 }
948
949 # generate status information
950 #
951 f_genstatus() {
952 local object duration item table_sets cnt_elements="0" custom_feed="0" split="0" status="${1}"
953
954 [ -z "${ban_dev}" ] && f_conf
955 if [ "${status}" = "active" ]; then
956 if [ -n "${ban_starttime}" ]; then
957 ban_endtime="$(date "+%s")"
958 duration="$(((ban_endtime - ban_starttime) / 60))m $(((ban_endtime - ban_starttime) % 60))s"
959 fi
960 table_sets="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null | jsonfilter -qe '@.nftables[@.set.table="banIP"].set.name')"
961 if [ "${ban_reportelements}" = "1" ]; then
962 for item in ${table_sets}; do
963 cnt_elements="$((cnt_elements + $("${ban_nftcmd}" -j list set inet banIP "${item}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)))"
964 done
965 fi
966 runtime="action: ${ban_action:-"-"}, duration: ${duration:-"-"}, date: $(date "+%Y-%m-%d %H:%M:%S")"
967 fi
968 [ -s "${ban_customfeedfile}" ] && custom_feed="1"
969 [ "${ban_splitsize:-"0"}" -gt "0" ] && split="1"
970
971 : >"${ban_rtfile}"
972 json_init
973 json_load_file "${ban_rtfile}" >/dev/null 2>&1
974 json_add_string "status" "${status}"
975 json_add_string "version" "${ban_ver}"
976 json_add_string "element_count" "${cnt_elements}"
977 json_add_array "active_feeds"
978 for object in ${table_sets:-"-"}; do
979 json_add_object
980 json_add_string "feed" "${object}"
981 json_close_object
982 done
983 json_close_array
984 json_add_array "active_devices"
985 for object in ${ban_dev:-"-"}; do
986 json_add_object
987 json_add_string "device" "${object}"
988 json_close_object
989 done
990 for object in ${ban_ifv4:-"-"} ${ban_ifv6:-"-"}; do
991 json_add_object
992 json_add_string "interface" "${object}"
993 json_close_object
994 done
995 json_close_array
996 json_add_array "active_uplink"
997 for object in ${ban_uplink:-"-"}; do
998 json_add_object
999 json_add_string "uplink" "${object}"
1000 json_close_object
1001 done
1002 json_close_array
1003 json_add_string "nft_info" "priority: ${ban_nftpriority}, policy: ${ban_nftpolicy}, loglevel: ${ban_nftloglevel}, expiry: ${ban_nftexpiry:-"-"}"
1004 json_add_string "run_info" "base: ${ban_basedir}, backup: ${ban_backupdir}, report: ${ban_reportdir}, custom feed: $(f_char ${custom_feed})"
1005 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})"
1006 json_add_string "last_run" "${runtime:-"-"}"
1007 json_add_string "system_info" "cores: ${ban_cores}, memory: ${ban_memory}, device: ${ban_sysver}"
1008 json_dump >"${ban_rtfile}"
1009 }
1010
1011 # get status information
1012 #
1013 f_getstatus() {
1014 local key keylist type value index_key1 index_key2 index_value1 index_value2
1015
1016 [ -z "${ban_dev}" ] && f_conf
1017 json_load_file "${ban_rtfile}" >/dev/null 2>&1
1018 if json_get_keys keylist; then
1019 printf "%s\n" "::: banIP runtime information"
1020 for key in ${keylist}; do
1021 json_get_var value "${key}" >/dev/null 2>&1
1022 if [ "${key}" = "status" ]; then
1023 value="${value} ($(f_actual))"
1024 elif [ "${key}" = "active_devices" ]; then
1025 json_select "${key}" >/dev/null 2>&1
1026 index=1
1027 while json_get_type type "${index}" && [ "${type}" = "object" ]; do
1028 json_get_keys index_key1 "${index}" >/dev/null 2>&1
1029 json_get_keys index_key2 "$((index + 1))" >/dev/null 2>&1
1030 json_get_values index_value1 "${index}" >/dev/null 2>&1
1031 if [ "${index}" = "1" ] && [ "${index_key1// /}" = "device" ] && [ "${index_key2// /}" = "interface" ]; then
1032 json_get_values index_value2 "$((index + 1))" >/dev/null 2>&1
1033 value="${index_value1} ::: ${index_value2}"
1034 index="$((index + 1))"
1035 elif [ "${index}" = "1" ]; then
1036 value="${index_value1}"
1037 elif [ "${index}" != "1" ] && [ "${index_key1// /}" = "device" ] && [ "${index_key2// /}" = "interface" ]; then
1038 json_get_values index_value2 "$((index + 1))" >/dev/null 2>&1
1039 value="${value}, ${index_value1} ::: ${index_value2}"
1040 index="$((index + 1))"
1041 elif [ "${index}" != "1" ]; then
1042 value="${value}, ${index_value1}"
1043 fi
1044 index="$((index + 1))"
1045 done
1046 json_select ".."
1047 elif [ "${key%_*}" = "active" ]; then
1048 json_select "${key}" >/dev/null 2>&1
1049 index=1
1050 while json_get_type type "${index}" && [ "${type}" = "object" ]; do
1051 json_get_values index_value1 "${index}" >/dev/null 2>&1
1052 if [ "${index}" = "1" ]; then
1053 value="${index_value1}"
1054 else
1055 value="${value}, ${index_value1}"
1056 fi
1057 index="$((index + 1))"
1058 done
1059 json_select ".."
1060 fi
1061 printf " + %-17s : %s\n" "${key}" "${value:-"-"}"
1062 done
1063 else
1064 printf "%s\n" "::: no banIP runtime information available"
1065 fi
1066 }
1067
1068 # domain lookup
1069 #
1070 f_lookup() {
1071 local cnt list domain lookup ip elementsv4 elementsv6 start_time end_time duration cnt_domain="0" cnt_ip="0" feed="${1}"
1072
1073 [ -z "${ban_dev}" ] && f_conf
1074 start_time="$(date "+%s")"
1075 if [ "${feed}" = "allowlist" ]; then
1076 list="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s ",tolower($1)}' "${ban_allowlist}" 2>/dev/null)"
1077 elif [ "${feed}" = "blocklist" ]; then
1078 list="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s ",tolower($1)}' "${ban_blocklist}" 2>/dev/null)"
1079 fi
1080
1081 for domain in ${list}; do
1082 lookup="$("${ban_lookupcmd}" "${domain}" ${ban_resolver} 2>/dev/null | "${ban_awkcmd}" '/^Address[ 0-9]*: /{if(!seen[$NF]++)printf "%s ",$NF}' 2>/dev/null)"
1083 for ip in ${lookup}; do
1084 if [ "${ip%%.*}" = "0" ] || [ -z "${ip%%::*}" ]; then
1085 continue
1086 else
1087 if { [ "${feed}" = "allowlist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_allowlist}"; } ||
1088 { [ "${feed}" = "blocklist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_blocklist}"; }; then
1089 if [ "${ip##*:}" = "${ip}" ]; then
1090 elementsv4="${elementsv4} ${ip},"
1091 else
1092 elementsv6="${elementsv6} ${ip},"
1093 fi
1094 if [ "${feed}" = "allowlist" ] && [ "${ban_autoallowlist}" = "1" ]; then
1095 printf "%-42s%s\n" "${ip}" "# '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_allowlist}"
1096 elif [ "${feed}" = "blocklist" ] && [ "${ban_autoblocklist}" = "1" ]; then
1097 printf "%-42s%s\n" "${ip}" "# '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
1098 fi
1099 cnt_ip="$((cnt_ip + 1))"
1100 fi
1101 fi
1102 done
1103 cnt_domain="$((cnt_domain + 1))"
1104 done
1105 if [ -n "${elementsv4}" ]; then
1106 if ! "${ban_nftcmd}" add element inet banIP "${feed}v4" "{ ${elementsv4} }" >/dev/null 2>&1; then
1107 f_log "info" "can't add lookup file to Set '${feed}v4'"
1108 fi
1109 fi
1110 if [ -n "${elementsv6}" ]; then
1111 if ! "${ban_nftcmd}" add element inet banIP "${feed}v6" "{ ${elementsv6} }" >/dev/null 2>&1; then
1112 f_log "info" "can't add lookup file to Set '${feed}v6'"
1113 fi
1114 fi
1115 end_time="$(date "+%s")"
1116 duration="$(((end_time - start_time) / 60))m $(((end_time - start_time) % 60))s"
1117
1118 f_log "debug" "f_lookup ::: feed: ${feed}, domains: ${cnt_domain}, IPs: ${cnt_ip}, duration: ${duration}"
1119 }
1120
1121 # table statistics
1122 #
1123 f_report() {
1124 local report_jsn report_txt tmp_val ruleset_raw item table_sets set_cnt set_input set_forwardwan set_forwardlan set_cntinput set_cntforwardwan set_cntforwardlan output="${1}"
1125 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
1126
1127 [ -z "${ban_dev}" ] && f_conf
1128 f_mkdir "${ban_reportdir}"
1129 report_jsn="${ban_reportdir}/ban_report.jsn"
1130 report_txt="${ban_reportdir}/ban_report.txt"
1131
1132 # json output preparation
1133 #
1134 ruleset_raw="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null)"
1135 table_sets="$(printf "%s" "${ruleset_raw}" | jsonfilter -qe '@.nftables[@.set.table="banIP"].set.name')"
1136 sum_sets="0"
1137 sum_setinput="0"
1138 sum_setforwardwan="0"
1139 sum_setforwardlan="0"
1140 sum_setelements="0"
1141 sum_cntinput="0"
1142 sum_cntforwardwan="0"
1143 sum_cntforwardlan="0"
1144 timestamp="$(date "+%Y-%m-%d %H:%M:%S")"
1145 : >"${report_jsn}"
1146 {
1147 printf "%s\n" "{"
1148 printf "\t%s\n" '"sets":{'
1149 for item in ${table_sets}; do
1150 set_cntinput="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-input\"][@.expr[0].match.right=\"@${item}\"].expr[*].counter.packets")"
1151 set_cntforwardwan="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"wan-forward\"][@.expr[0].match.right=\"@${item}\"].expr[*].counter.packets")"
1152 set_cntforwardlan="$(printf "%s" "${ruleset_raw}" | jsonfilter -l1 -qe "@.nftables[@.rule.table=\"banIP\"&&@.rule.chain=\"lan-forward\"][@.expr[0].match.right=\"@${item}\"].expr[*].counter.packets")"
1153 if [ "${ban_reportelements}" = "1" ]; then
1154 set_cnt="$("${ban_nftcmd}" -j list set inet banIP "${item}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)"
1155 sum_setelements="$((sum_setelements + set_cnt))"
1156 else
1157 set_cnt=""
1158 sum_setelements="n/a"
1159 fi
1160 if [ -n "${set_cntinput}" ]; then
1161 set_input="OK"
1162 sum_setinput="$((sum_setinput + 1))"
1163 sum_cntinput="$((sum_cntinput + set_cntinput))"
1164 else
1165 set_input="-"
1166 set_cntinput=""
1167 fi
1168 if [ -n "${set_cntforwardwan}" ]; then
1169 set_forwardwan="OK"
1170 sum_setforwardwan="$((sum_setforwardwan + 1))"
1171 sum_cntforwardwan="$((sum_cntforwardwan + set_cntforwardwan))"
1172 else
1173 set_forwardwan="-"
1174 set_cntforwardwan=""
1175 fi
1176 if [ -n "${set_cntforwardlan}" ]; then
1177 set_forwardlan="OK"
1178 sum_setforwardlan="$((sum_setforwardlan + 1))"
1179 sum_cntforwardlan="$((sum_cntforwardlan + set_cntforwardlan))"
1180 else
1181 set_forwardlan="-"
1182 set_cntforwardlan=""
1183 fi
1184 [ "${sum_sets}" -gt "0" ] && printf "%s\n" ","
1185 printf "\t\t%s\n" "\"${item}\":{"
1186 printf "\t\t\t%s\n" "\"cnt_elements\": \"${set_cnt}\","
1187 printf "\t\t\t%s\n" "\"cnt_input\": \"${set_cntinput}\","
1188 printf "\t\t\t%s\n" "\"input\": \"${set_input}\","
1189 printf "\t\t\t%s\n" "\"cnt_forwardwan\": \"${set_cntforwardwan}\","
1190 printf "\t\t\t%s\n" "\"wan_forward\": \"${set_forwardwan}\","
1191 printf "\t\t\t%s\n" "\"cnt_forwardlan\": \"${set_cntforwardlan}\","
1192 printf "\t\t\t%s\n" "\"lan_forward\": \"${set_forwardlan}\""
1193 printf "\t\t%s" "}"
1194 sum_sets="$((sum_sets + 1))"
1195 done
1196 printf "\n\t%s\n" "},"
1197 printf "\t%s\n" "\"timestamp\": \"${timestamp}\","
1198 printf "\t%s\n" "\"autoadd_allow\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_allowlist}")\","
1199 printf "\t%s\n" "\"autoadd_block\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_blocklist}")\","
1200 printf "\t%s\n" "\"sum_sets\": \"${sum_sets}\","
1201 printf "\t%s\n" "\"sum_setinput\": \"${sum_setinput}\","
1202 printf "\t%s\n" "\"sum_setforwardwan\": \"${sum_setforwardwan}\","
1203 printf "\t%s\n" "\"sum_setforwardlan\": \"${sum_setforwardlan}\","
1204 printf "\t%s\n" "\"sum_setelements\": \"${sum_setelements}\","
1205 printf "\t%s\n" "\"sum_cntinput\": \"${sum_cntinput}\","
1206 printf "\t%s\n" "\"sum_cntforwardwan\": \"${sum_cntforwardwan}\","
1207 printf "\t%s\n" "\"sum_cntforwardlan\": \"${sum_cntforwardlan}\""
1208 printf "%s\n" "}"
1209 } >>"${report_jsn}"
1210
1211 # text output preparation
1212 #
1213 if [ "${output}" != "json" ] && [ -s "${report_jsn}" ]; then
1214 : >"${report_txt}"
1215 json_init
1216 if json_load_file "${report_jsn}" >/dev/null 2>&1; then
1217 json_get_var timestamp "timestamp" >/dev/null 2>&1
1218 json_get_var autoadd_allow "autoadd_allow" >/dev/null 2>&1
1219 json_get_var autoadd_block "autoadd_block" >/dev/null 2>&1
1220 json_get_var sum_sets "sum_sets" >/dev/null 2>&1
1221 json_get_var sum_setinput "sum_setinput" >/dev/null 2>&1
1222 json_get_var sum_setforwardwan "sum_setforwardwan" >/dev/null 2>&1
1223 json_get_var sum_setforwardlan "sum_setforwardlan" >/dev/null 2>&1
1224 json_get_var sum_setelements "sum_setelements" >/dev/null 2>&1
1225 json_get_var sum_cntinput "sum_cntinput" >/dev/null 2>&1
1226 json_get_var sum_cntforwardwan "sum_cntforwardwan" >/dev/null 2>&1
1227 json_get_var sum_cntforwardlan "sum_cntforwardlan" >/dev/null 2>&1
1228 {
1229 printf "%s\n%s\n%s\n" ":::" "::: banIP Set Statistics" ":::"
1230 printf "%s\n" " Timestamp: ${timestamp}"
1231 printf "%s\n" " ------------------------------"
1232 printf "%s\n" " auto-added to allowlist today: ${autoadd_allow}"
1233 printf "%s\n\n" " auto-added to blocklist today: ${autoadd_block}"
1234 json_select "sets" >/dev/null 2>&1
1235 json_get_keys table_sets >/dev/null 2>&1
1236 if [ -n "${table_sets}" ]; then
1237 printf "%-25s%-15s%-24s%-24s%s\n" " Set" "| Elements" "| WAN-Input (packets)" "| WAN-Forward (packets)" "| LAN-Forward (packets)"
1238 printf "%s\n" " ---------------------+--------------+-----------------------+-----------------------+------------------------"
1239 for item in ${table_sets}; do
1240 printf " %-21s" "${item}"
1241 json_select "${item}"
1242 json_get_keys set_details
1243 for detail in ${set_details}; do
1244 json_get_var jsnval "${detail}" >/dev/null 2>&1
1245 case "${detail}" in
1246 "cnt_elements")
1247 printf "%-15s" "| ${jsnval}"
1248 ;;
1249 "cnt_input" | "cnt_forwardwan" | "cnt_forwardlan")
1250 [ -n "${jsnval}" ] && tmp_val=": ${jsnval}"
1251 ;;
1252 *)
1253 printf "%-24s" "| ${jsnval}${tmp_val}"
1254 tmp_val=""
1255 ;;
1256 esac
1257 done
1258 printf "\n"
1259 json_select ".."
1260 done
1261 printf "%s\n" " ---------------------+--------------+-----------------------+-----------------------+------------------------"
1262 printf "%-25s%-15s%-24s%-24s%s\n" " ${sum_sets}" "| ${sum_setelements}" "| ${sum_setinput} (${sum_cntinput})" "| ${sum_setforwardwan} (${sum_cntforwardwan})" "| ${sum_setforwardlan} (${sum_cntforwardlan})"
1263 fi
1264 } >>"${report_txt}"
1265 fi
1266 fi
1267
1268 # output channel (text|json|mail)
1269 #
1270 case "${output}" in
1271 "text")
1272 [ -s "${report_txt}" ] && "${ban_catcmd}" "${report_txt}"
1273 ;;
1274 "json")
1275 [ -s "${report_jsn}" ] && "${ban_catcmd}" "${report_jsn}"
1276 ;;
1277 "mail")
1278 [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ] && f_mail
1279 ;;
1280 esac
1281 rm -f "${report_txt}"
1282 }
1283
1284 # Set search
1285 #
1286 f_search() {
1287 local item table_sets ip proto hold cnt result_flag="/var/run/banIP.search" input="${1}"
1288
1289 if [ -n "${input}" ]; then
1290 ip="$(printf "%s" "${input}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{printf "%s",RT}')"
1291 [ -n "${ip}" ] && proto="v4"
1292 if [ -z "${proto}" ]; then
1293 ip="$(printf "%s" "${input}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?){3,7}[A-Fa-f0-9]{1,4}"}{printf "%s",RT}')"
1294 [ -n "${ip}" ] && proto="v6"
1295 fi
1296 fi
1297 if [ -n "${proto}" ]; then
1298 table_sets="$("${ban_nftcmd}" -tj list ruleset 2>/dev/null | jsonfilter -qe "@.nftables[@.set.table=\"banIP\"&&@.set.type=\"ip${proto}_addr\"].set.name")"
1299 else
1300 printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::"
1301 return
1302 fi
1303 printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
1304 printf " %s\n" "Looking for IP '${ip}' on $(date "+%Y-%m-%d %H:%M:%S")"
1305 printf " %s\n" "---"
1306 cnt="1"
1307 for item in ${table_sets}; do
1308 if [ -f "${result_flag}" ]; then
1309 rm -f "${result_flag}"
1310 return
1311 fi
1312 (
1313 if "${ban_nftcmd}" get element inet banIP "${item}" "{ ${ip} }" >/dev/null 2>&1; then
1314 printf " %s\n" "IP found in Set '${item}'"
1315 : >"${result_flag}"
1316 fi
1317 ) &
1318 hold="$((cnt % ban_cores))"
1319 [ "${hold}" = "0" ] && wait
1320 cnt="$((cnt + 1))"
1321 done
1322 wait
1323 printf " %s\n" "IP not found"
1324 }
1325
1326 # Set survey
1327 #
1328 f_survey() {
1329 local set_elements input="${1}"
1330
1331 if [ -z "${input}" ]; then
1332 printf "%s\n%s\n%s\n" ":::" "::: no valid survey input" ":::"
1333 return
1334 fi
1335 set_elements="$("${ban_nftcmd}" -j list set inet banIP "${input}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]')"
1336 printf "%s\n%s\n%s\n" ":::" "::: banIP Survey" ":::"
1337 printf " %s\n" "List of elements in the Set '${input}' on $(date "+%Y-%m-%d %H:%M:%S")"
1338 printf " %s\n" "---"
1339 [ -n "${set_elements}" ] && printf "%s\n" "${set_elements}" || printf " %s\n" "empty Set"
1340 }
1341
1342 # send status mail
1343 #
1344 f_mail() {
1345 local msmtp_debug
1346
1347 # load mail template
1348 #
1349 if [ -r "${ban_mailtemplate}" ]; then
1350 . "${ban_mailtemplate}"
1351 else
1352 f_log "info" "no mail template"
1353 fi
1354 [ -z "${mail_text}" ] && f_log "info" "no mail content"
1355 [ "${ban_debug}" = "1" ] && msmtp_debug="--debug"
1356
1357 # send mail
1358 #
1359 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"
1360 printf "%b" "${ban_mailhead}${mail_text}" | "${ban_mailcmd}" --timeout=10 ${msmtp_debug} -a "${ban_mailprofile}" "${ban_mailreceiver}" >/dev/null 2>&1
1361 f_log "info" "send status mail (${?})"
1362
1363 f_log "debug" "f_mail ::: notification: ${ban_mailnotification}, template: ${ban_mailtemplate}, profile: ${ban_mailprofile}, receiver: ${ban_mailreceiver}, rc: ${?}"
1364 }
1365
1366 # log monitor
1367 #
1368 f_monitor() {
1369 local nft_expiry line proto ip log_raw log_count
1370
1371 if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ] && [ "${ban_loglimit}" != "0" ]; then
1372
1373 f_log "info" "start detached banIP log service"
1374 [ -n "${ban_nftexpiry}" ] && nft_expiry="timeout $(printf "%s" "${ban_nftexpiry}" | "${ban_grepcmd}" -oE "([0-9]+[d|h|m|s])+$")"
1375
1376 "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev/null |
1377 while read -r line; do
1378 proto=""
1379 ip="$(printf "%s" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{if(!seen[RT]++)printf "%s ",RT}')"
1380 ip="$(f_trim "${ip}")"
1381 ip="${ip##* }"
1382 [ -n "${ip}" ] && proto="v4"
1383 if [ -z "${proto}" ]; then
1384 ip="$(printf "%s" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?){3,7}[A-Fa-f0-9]{1,4}"}{if(!seen[RT]++)printf "%s ",RT}')"
1385 ip="$(f_trim "${ip}")"
1386 ip="${ip##* }"
1387 [ -n "${ip}" ] && proto="v6"
1388 fi
1389 if [ -n "${proto}" ] && ! "${ban_nftcmd}" get element inet banIP blocklist"${proto}" "{ ${ip} }" >/dev/null 2>&1; then
1390 f_log "info" "suspicious IP${proto} '${ip}'"
1391 log_raw="$("${ban_logreadcmd}" -l "${ban_loglimit}" 2>/dev/null)"
1392 log_count="$(printf "%s\n" "${log_raw}" | "${ban_grepcmd}" -c "suspicious IP${proto} '${ip}'")"
1393 if [ "${log_count}" -ge "${ban_logcount}" ]; then
1394 if "${ban_nftcmd}" add element inet banIP "blocklist${proto}" "{ ${ip} ${nft_expiry} }" >/dev/null 2>&1; then
1395 f_log "info" "add IP${proto} '${ip}' (expiry: ${ban_nftexpiry:-"-"}) to blocklist${proto} set"
1396 if [ -z "${ban_nftexpiry}" ] && [ "${ban_autoblocklist}" = "1" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_blocklist}"; then
1397 printf "%-42s%s\n" "${ip}" "# added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
1398 f_log "info" "add IP${proto} '${ip}' to local blocklist"
1399 fi
1400 fi
1401 fi
1402 fi
1403 done
1404 else
1405 f_log "info" "start detached no-op banIP service"
1406 sleep infinity
1407 fi
1408 }
1409
1410 # initial sourcing
1411 #
1412 if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]; then
1413 . "/lib/functions.sh"
1414 . "/lib/functions/network.sh"
1415 . "/usr/share/libubox/jshn.sh"
1416 else
1417 rm -rf "${ban_lock}"
1418 exit 1
1419 fi
1420
1421 # check banIP availability
1422 #
1423 f_system
1424 if [ "${ban_action}" != "stop" ]; then
1425 [ ! -d "/etc/banip" ] && f_log "err" "no banIP config directory"
1426 [ ! -r "/etc/config/banip" ] && f_log "err" "no banIP config"
1427 [ "$(uci_get banip global ban_enabled)" = "0" ] && f_log "err" "banIP is disabled"
1428 fi