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