You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1595 lines
57 KiB

  1. #!/bin/sh
  2. # banIP - ban incoming and outgoing ip adresses/subnets via ipset
  3. # Copyright (c) 2018-2021 Dirk Brenken (dev@brenken.org)
  4. # This is free software, licensed under the GNU General Public License v3.
  5. # (s)hellcheck exceptions
  6. # shellcheck disable=1091,2030,2031,2086,2183,3040,3043,3060
  7. # set initial defaults
  8. #
  9. export LC_ALL=C
  10. export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
  11. set -o pipefail
  12. ban_ver="0.7.10"
  13. ban_status=""
  14. ban_enabled="0"
  15. ban_mail_enabled="0"
  16. ban_proto4_enabled="0"
  17. ban_proto6_enabled="0"
  18. ban_logsrc_enabled="0"
  19. ban_logdst_enabled="0"
  20. ban_monitor_enabled="0"
  21. ban_autodetect="1"
  22. ban_autoblacklist="1"
  23. ban_autowhitelist="1"
  24. ban_whitelistonly="0"
  25. ban_logterms=""
  26. ban_loglimit="100"
  27. ban_ssh_logcount="3"
  28. ban_luci_logcount="3"
  29. ban_nginx_logcount="5"
  30. ban_mailactions=""
  31. ban_search=""
  32. ban_devs=""
  33. ban_ifaces=""
  34. ban_debug="0"
  35. ban_maxqueue="4"
  36. ban_fetchutil=""
  37. ban_fetchinsecure="0"
  38. ban_ip_cmd="$(command -v ip)"
  39. ban_ipt4_cmd="$(command -v iptables)"
  40. ban_ipt4_savecmd="$(command -v iptables-save)"
  41. ban_ipt4_restorecmd="$(command -v iptables-restore)"
  42. ban_ipt6_cmd="$(command -v ip6tables)"
  43. ban_ipt6_savecmd="$(command -v ip6tables-save)"
  44. ban_ipt6_restorecmd="$(command -v ip6tables-restore)"
  45. ban_ipset_cmd="$(command -v ipset)"
  46. ban_logger_cmd="$(command -v logger)"
  47. ban_logread_cmd="$(command -v logread)"
  48. ban_allsources=""
  49. ban_extrasources=""
  50. ban_sources=""
  51. ban_asns=""
  52. ban_countries=""
  53. ban_settype_src=""
  54. ban_settype_dst=""
  55. ban_settype_all=""
  56. ban_lan_inputchains_4=""
  57. ban_lan_inputchains_6=""
  58. ban_lan_forwardchains_4=""
  59. ban_lan_forwardchains_6=""
  60. ban_wan_inputchains_4=""
  61. ban_wan_inputchains_6=""
  62. ban_wan_forwardchains_4=""
  63. ban_wan_forwardchains_6=""
  64. ban_action="${1:-"start"}"
  65. ban_pidfile="/var/run/banip.pid"
  66. ban_bgpidfile="/var/run/banip_bg.pid"
  67. ban_tmpbase="/tmp"
  68. ban_rtfile="${ban_tmpbase}/ban_runtime.json"
  69. ban_srcfile="${ban_tmpbase}/ban_sources.json"
  70. ban_reportdir="${ban_tmpbase}/banIP-Report"
  71. ban_backupdir="${ban_tmpbase}/banIP-Backup"
  72. ban_srcarc="/etc/banip/banip.sources.gz"
  73. ban_dnsservice="/etc/banip/banip.dns"
  74. ban_mailservice="/etc/banip/banip.mail"
  75. ban_logservice="/etc/banip/banip.service"
  76. ban_maclist="/etc/banip/banip.maclist"
  77. ban_blacklist="/etc/banip/banip.blacklist"
  78. ban_whitelist="/etc/banip/banip.whitelist"
  79. ban_setcnt="0"
  80. ban_cnt="0"
  81. # load environment
  82. #
  83. f_load() {
  84. ban_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -q -e '@.model' -e '@.release.description' |
  85. awk 'BEGIN{RS="";FS="\n"}{printf "%s, %s",$1,$2}')"
  86. f_conf
  87. if [ "${ban_enabled}" = "0" ]; then
  88. f_bgsrv "stop"
  89. f_ipset "destroy"
  90. f_jsnup "disabled"
  91. f_rmbckp
  92. f_rmtmp
  93. f_log "info" "banIP is currently disabled, please set the config option 'ban_enabled' to '1' to use this service"
  94. exit 0
  95. fi
  96. f_dir "${ban_backupdir}"
  97. f_dir "${ban_reportdir}"
  98. }
  99. # check/create directories
  100. #
  101. f_dir() {
  102. local dir="${1}"
  103. if [ -d "${dir}" ]; then
  104. f_log "debug" "directory '${dir}' is used"
  105. else
  106. rm -f "${dir}"
  107. mkdir -p "${dir}"
  108. f_log "debug" "directory '${dir}' created"
  109. fi
  110. }
  111. # load banIP config
  112. #
  113. f_conf() {
  114. if [ ! -r "/etc/config/banip" ] || [ -z "$(uci -q show banip.global.ban_autodetect)" ]; then
  115. f_log "err" "no valid banIP config found, please re-install the package via opkg with the '--force-reinstall --force-maintainer' options"
  116. fi
  117. config_cb() {
  118. option_cb() {
  119. local option="${1}"
  120. local value="${2}"
  121. eval "${option}=\"${value}\""
  122. }
  123. list_cb() {
  124. local option="${1}"
  125. local value="${2}"
  126. if [ "${option}" = "ban_ifaces" ]; then
  127. eval "${option}=\"$(printf "%s" "${ban_ifaces}")${value} \""
  128. elif [ "${option}" = "ban_sources" ]; then
  129. eval "${option}=\"$(printf "%s" "${ban_sources}")${value} \""
  130. elif [ "${option}" = "ban_localsources" ]; then
  131. eval "${option}=\"$(printf "%s" "${ban_localsources}")${value} \""
  132. elif [ "${option}" = "ban_extrasources" ]; then
  133. eval "${option}=\"$(printf "%s" "${ban_extrasources}")${value} \""
  134. elif [ "${option}" = "ban_settype_src" ]; then
  135. eval "${option}=\"$(printf "%s" "${ban_settype_src}")${value} \""
  136. elif [ "${option}" = "ban_settype_dst" ]; then
  137. eval "${option}=\"$(printf "%s" "${ban_settype_dst}")${value} \""
  138. elif [ "${option}" = "ban_settype_all" ]; then
  139. eval "${option}=\"$(printf "%s" "${ban_settype_all}")${value} \""
  140. elif [ "${option}" = "ban_mailactions" ]; then
  141. eval "${option}=\"$(printf "%s" "${ban_mailactions}")${value} \""
  142. elif [ "${option}" = "ban_logterms" ]; then
  143. eval "${option}=\"$(printf "%s" "${ban_logterms}")${value} \""
  144. elif [ "${option}" = "ban_countries" ]; then
  145. eval "${option}=\"$(printf "%s" "${ban_countries}")${value} \""
  146. elif [ "${option}" = "ban_asns" ]; then
  147. eval "${option}=\"$(printf "%s" "${ban_asns}")${value} \""
  148. elif [ "${option}" = "ban_lan_inputchains_4" ]; then
  149. eval "${option}=\"$(printf "%s" "${ban_lan_inputchains_4}")${value} \""
  150. elif [ "${option}" = "ban_lan_inputchains_6" ]; then
  151. eval "${option}=\"$(printf "%s" "${ban_lan_inputchains_6}")${value} \""
  152. elif [ "${option}" = "ban_lan_forwardchains_4" ]; then
  153. eval "${option}=\"$(printf "%s" "${ban_lan_forwardchains_4}")${value} \""
  154. elif [ "${option}" = "ban_lan_forwardchains_6" ]; then
  155. eval "${option}=\"$(printf "%s" "${ban_lan_forwardchains_6}")${value} \""
  156. elif [ "${option}" = "ban_wan_inputchains_4" ]; then
  157. eval "${option}=\"$(printf "%s" "${ban_wan_inputchains_4}")${value} \""
  158. elif [ "${option}" = "ban_wan_inputchains_6" ]; then
  159. eval "${option}=\"$(printf "%s" "${ban_wan_inputchains_6}")${value} \""
  160. elif [ "${option}" = "ban_wan_forwardchains_4" ]; then
  161. eval "${option}=\"$(printf "%s" "${ban_wan_forwardchains_4}")${value} \""
  162. elif [ "${option}" = "ban_wan_forwardchains_6" ]; then
  163. eval "${option}=\"$(printf "%s" "${ban_wan_forwardchains_6}")${value} \""
  164. fi
  165. }
  166. }
  167. config_load banip
  168. ban_chain="${ban_chain:-"banIP"}"
  169. ban_global_settype="${ban_global_settype:-"src+dst"}"
  170. ban_target_src="${ban_target_src:-"DROP"}"
  171. ban_target_dst="${ban_target_dst:-"REJECT"}"
  172. ban_lan_inputchains_4="${ban_lan_inputchains_4:-"input_lan_rule"}"
  173. ban_lan_inputchains_6="${ban_lan_inputchains_6:-"input_lan_rule"}"
  174. ban_lan_forwardchains_4="${ban_lan_forwardchains_4:-"forwarding_lan_rule"}"
  175. ban_lan_forwardchains_6="${ban_lan_forwardchains_6:-"forwarding_lan_rule"}"
  176. ban_wan_inputchains_4="${ban_wan_inputchains_4:-"input_wan_rule"}"
  177. ban_wan_inputchains_6="${ban_wan_inputchains_6:-"input_wan_rule"}"
  178. ban_wan_forwardchains_4="${ban_wan_forwardchains_4:-"forwarding_wan_rule"}"
  179. ban_wan_forwardchains_6="${ban_wan_forwardchains_6:-"forwarding_wan_rule"}"
  180. ban_logchain_src="${ban_logchain_src:-"${ban_chain}_log_src"}"
  181. ban_logchain_dst="${ban_logchain_dst:-"${ban_chain}_log_dst"}"
  182. ban_logtarget_src="${ban_target_src}"
  183. ban_logtarget_dst="${ban_target_dst}"
  184. if [ "${ban_logsrc_enabled}" = "1" ]; then
  185. ban_logprefix_src="${ban_logprefix_src:-"[banIP-${ban_ver%-*}, src/${ban_target_src}] "}"
  186. ban_logopts_src="${ban_logopts_src:-"-m limit --limit 2/sec"}"
  187. ban_target_src="${ban_logchain_src}"
  188. fi
  189. if [ "${ban_logdst_enabled}" = "1" ]; then
  190. ban_logprefix_dst="${ban_logprefix_dst:-"[banIP-${ban_ver%-*}, dst/${ban_target_dst}] "}"
  191. ban_logopts_dst="${ban_logopts_dst:-"-m limit --limit 2/sec"}"
  192. ban_target_dst="${ban_logchain_dst}"
  193. fi
  194. ban_localsources="${ban_localsources:-"maclist whitelist blacklist"}"
  195. ban_logterms="${ban_logterms:-"dropbear sshd luci nginx"}"
  196. f_log "debug" "f_conf ::: ifaces: ${ban_ifaces:-"-"}, chain: ${ban_chain}, set_type: ${ban_global_settype}, log_chains (src/dst): ${ban_logchain_src}/${ban_logchain_dst}, targets (src/dst): ${ban_target_src}/${ban_target_dst}, whitelist_only: ${ban_whitelistonly}"
  197. f_log "debug" "f_conf ::: lan_inputs (4/6): ${ban_lan_inputchains_4}/${ban_lan_inputchains_6}, lan_forwards (4/6): ${ban_lan_forwardchains_4}/${ban_lan_forwardchains_6}, wan_inputs (4/6): ${ban_wan_inputchains_4}/${ban_wan_inputchains_6}, wan_forwards (4/6): ${ban_wan_forwardchains_4}/${ban_wan_forwardchains_6}"
  198. f_log "debug" "f_conf ::: local_sources: ${ban_localsources:-"-"}, extra_sources: ${ban_extrasources:-"-"}, log_terms: ${ban_logterms:-"-"}, log_prefixes (src/dst): ${ban_logprefix_src}/${ban_logprefix_dst}, log_options (src/dst): ${ban_logopts_src}/${ban_logopts_dst}"
  199. }
  200. # check environment
  201. #
  202. f_env() {
  203. local util utils packages iface insecure tmp cnt="0" cnt_max="10"
  204. ban_starttime="$(date "+%s")"
  205. f_jsnup "running"
  206. f_log "info" "start banIP processing (${ban_action})"
  207. f_tmp
  208. if [ "${ban_autodetect}" = "1" ] && [ -z "${ban_ifaces}" ]; then
  209. while [ "${cnt}" -le "${cnt_max}" ]; do
  210. network_find_wan iface
  211. if [ -n "${iface}" ] && ! printf "%s\n" "${ban_ifaces}" | grep -q "${iface}"; then
  212. ban_proto4_enabled="1"
  213. ban_ifaces="${ban_ifaces}${iface} "
  214. uci_set banip global ban_proto4_enabled "1"
  215. uci_add_list banip global ban_ifaces "${iface}"
  216. fi
  217. network_find_wan6 iface
  218. if [ -n "${iface}" ] && ! printf "%s\n" "${ban_ifaces}" | grep -q "${iface}"; then
  219. ban_proto6_enabled="1"
  220. ban_ifaces="${ban_ifaces}${iface} "
  221. uci_set banip global ban_proto6_enabled "1"
  222. uci_add_list banip global ban_ifaces "${iface}"
  223. fi
  224. if [ -z "${ban_ifaces}" ]; then
  225. if [ "${cnt}" -le "${cnt_max}" ]; then
  226. network_flush_cache
  227. cnt=$((cnt + 1))
  228. sleep 1
  229. else
  230. break
  231. fi
  232. else
  233. if [ -n "$(uci -q changes "banip")" ]; then
  234. uci_commit "banip"
  235. fi
  236. break
  237. fi
  238. done
  239. fi
  240. while [ "${cnt}" -le "${cnt_max}" ]; do
  241. for iface in ${ban_ifaces}; do
  242. network_get_device tmp "${iface}"
  243. if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_devs}" | grep -q "${tmp}"; then
  244. ban_devs="${ban_devs} ${tmp}"
  245. else
  246. network_get_physdev tmp "${iface}"
  247. if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_devs}" | grep -q "${tmp}"; then
  248. ban_devs="${ban_devs} ${tmp}"
  249. fi
  250. fi
  251. network_get_subnet tmp "${iface}"
  252. if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_subnets}" | grep -q "${tmp}"; then
  253. ban_subnets="${ban_subnets} ${tmp}"
  254. fi
  255. network_get_subnet6 tmp "${iface}"
  256. if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_subnets}" | grep -q "${tmp}"; then
  257. ban_subnets="${ban_subnets} ${tmp}"
  258. fi
  259. done
  260. if [ -z "${ban_devs}" ] || [ -z "${ban_subnets}" ]; then
  261. if [ "${cnt}" -le "${cnt_max}" ]; then
  262. network_flush_cache
  263. cnt=$((cnt + 1))
  264. sleep 1
  265. else
  266. break
  267. fi
  268. else
  269. break
  270. fi
  271. done
  272. ban_ipdevs="$("${ban_ip_cmd}" link show 2>/dev/null | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if($3!="lo"){ORS=" ";print $3}}')"
  273. if [ -z "${ban_ifaces}" ] || [ -z "${ban_devs}" ] || [ -z "${ban_ipdevs}" ]; then
  274. f_log "err" "logical wan interface(s)/device(s) '${ban_ifaces:-"-"}/${ban_devs:-"-"}' not found, please check your configuration"
  275. elif [ -z "${ban_ipdevs}" ]; then
  276. f_log "err" "ip device(s) '${ban_ipdevs:-"-"}' not found, please check your configuration"
  277. fi
  278. if [ ! -x "${ban_ipset_cmd}" ]; then
  279. f_log "err" "ipset utility '${ban_ipset_cmd:-"-"}' not executable, please install package 'ipset'"
  280. fi
  281. if { [ "${ban_proto4_enabled}" = "1" ] && { [ ! -x "${ban_ipt4_cmd}" ] || [ ! -x "${ban_ipt4_savecmd}" ] || [ ! -x "${ban_ipt4_restorecmd}" ]; }; } ||
  282. { [ "${ban_proto6_enabled}" = "1" ] && { [ ! -x "${ban_ipt6_cmd}" ] || [ ! -x "${ban_ipt6_savecmd}" ] || [ ! -x "${ban_ipt6_restorecmd}" ]; }; }; then
  283. f_log "err" "iptables utilities '${ban_ipt4_cmd:-"-"}, ${ban_ipt4_savecmd:-"-"}, ${ban_ipt4_restorecmd:-"-"}/${ban_ipt6_cmd:-"-"}', ${ban_ipt6_savecmd:-"-"}, ${ban_ipt6_restorecmd:-"-"} not executable, please install the relevant iptables packages"
  284. fi
  285. if [ -z "${ban_fetchutil}" ]; then
  286. while [ -z "${packages}" ] && [ "${cnt}" -le "${cnt_max}" ]; do
  287. packages="$(opkg list-installed 2>/dev/null)"
  288. cnt=$((cnt + 1))
  289. sleep 1
  290. done
  291. if [ -z "${packages}" ]; then
  292. f_log "err" "local opkg package repository is not available, please set 'ban_fetchutil' manually"
  293. fi
  294. utils="aria2c curl wget uclient-fetch"
  295. for util in ${utils}; do
  296. if { [ "${util}" = "uclient-fetch" ] && printf "%s" "${packages}" | grep -q "^libustream-"; } ||
  297. { [ "${util}" = "wget" ] && printf "%s" "${packages}" | grep -q "^wget -"; } ||
  298. [ "${util}" = "curl" ] || [ "${util}" = "aria2c" ]; then
  299. if [ -x "$(command -v "${util}")" ]; then
  300. ban_fetchutil="${util}"
  301. uci_set banip global ban_fetchutil "${util}"
  302. uci_commit "banip"
  303. break
  304. fi
  305. fi
  306. done
  307. elif [ ! -x "$(command -v "${ban_fetchutil}")" ]; then
  308. unset ban_fetchutil
  309. fi
  310. case "${ban_fetchutil}" in
  311. "aria2c")
  312. if [ "${ban_fetchinsecure}" = "1" ]; then
  313. insecure="--check-certificate=false"
  314. fi
  315. ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o"}"
  316. ;;
  317. "curl")
  318. if [ "${ban_fetchinsecure}" = "1" ]; then
  319. insecure="--insecure"
  320. fi
  321. ban_fetchparm="${ban_fetchparm:-"${insecure} --connect-timeout 20 --silent --show-error --location -o"}"
  322. ;;
  323. "uclient-fetch")
  324. if [ "${ban_fetchinsecure}" = "1" ]; then
  325. insecure="--no-check-certificate"
  326. fi
  327. ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 -O"}"
  328. ;;
  329. "wget")
  330. if [ "${ban_fetchinsecure}" = "1" ]; then
  331. insecure="--no-check-certificate"
  332. fi
  333. ban_fetchparm="${ban_fetchparm:-"${insecure} --no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
  334. ;;
  335. esac
  336. if [ -n "${ban_fetchutil}" ] && [ -n "${ban_fetchparm}" ]; then
  337. ban_fetchutil="$(command -v "${ban_fetchutil}")"
  338. else
  339. f_log "err" "download utility with SSL support not found, please install 'uclient-fetch' with a 'libustream-*' variant or another download utility like 'wget', 'curl' or 'aria2'"
  340. fi
  341. if [ ! -r "${ban_srcfile}" ]; then
  342. if [ -r "${ban_srcarc}" ]; then
  343. zcat "${ban_srcarc}" >"${ban_srcfile}"
  344. else
  345. f_log "err" "banIP source archive not found"
  346. fi
  347. fi
  348. if [ -r "${ban_srcfile}" ]; then
  349. json_init
  350. json_load_file "${ban_srcfile}"
  351. json_get_keys ban_allsources
  352. ban_allsources="${ban_allsources} maclist blacklist whitelist"
  353. else
  354. f_log "err" "banIP source file not found"
  355. fi
  356. f_log "debug" "f_env ::: auto_detect: ${ban_autodetect}, fetch_util: ${ban_fetchutil:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}, src_file: ${ban_srcfile:-"-"}, log_terms: ${ban_logterms}, interfaces: ${ban_ifaces:-"-"}, devices: ${ban_devs:-"-"}, subnets: ${ban_subnets:-"-"}, ip_devices: ${ban_ipdevs:-"-"}, protocols (4/6): ${ban_proto4_enabled}/${ban_proto6_enabled}"
  357. }
  358. # create temporary files and directories
  359. #
  360. f_tmp() {
  361. f_dir "${ban_tmpbase}"
  362. ban_tmpdir="$(mktemp -p "${ban_tmpbase}" -d)"
  363. ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
  364. if [ ! -f "${ban_pidfile}" ] || [ ! -s "${ban_pidfile}" ]; then
  365. printf "%s" "${$}" >"${ban_pidfile}"
  366. fi
  367. f_log "debug" "f_tmp ::: tmp_base: ${ban_tmpbase:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}, pid_file: ${ban_pidfile:-"-"}"
  368. }
  369. # remove temporary files and directories
  370. #
  371. f_rmtmp() {
  372. if [ -d "${ban_tmpdir}" ]; then
  373. rm -rf "${ban_tmpdir}"
  374. fi
  375. rm -f "${ban_srcfile}"
  376. : >"${ban_pidfile}"
  377. f_log "debug" "f_rmtmp ::: tmp_base: ${ban_tmpbase:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}, pid_file: ${ban_pidfile:-"-"}"
  378. }
  379. # remove backup files
  380. #
  381. f_rmbckp() {
  382. if [ -d "${ban_backupdir}" ]; then
  383. rm -f "${ban_backupdir}/banIP."*".gz"
  384. fi
  385. }
  386. # status helper function
  387. #
  388. f_char() {
  389. local result input="${1}"
  390. if [ "${input}" = "1" ]; then
  391. result="✔"
  392. else
  393. result="✘"
  394. fi
  395. printf "%s" "${result}"
  396. }
  397. # apply iptables rules
  398. #
  399. f_iptrule() {
  400. local rc timeout="-w 5" action="${1}" chain="${2}" rule="${3}" pos="${4}"
  401. if [ "${ban_proto4_enabled}" = "1" ] && { [ "${src_name}" = "maclist" ] || [ "${src_name##*_}" = "4" ]; }; then
  402. rc="$(
  403. "${ban_ipt4_cmd}" "${timeout}" -C ${chain} ${rule} 2>/dev/null
  404. printf "%u" ${?}
  405. )"
  406. if { [ "${rc}" != "0" ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; }; } ||
  407. { [ "${rc}" = "0" ] && [ "${action}" = "-D" ]; }; then
  408. "${ban_ipt4_cmd}" "${timeout}" "${action}" ${chain} ${pos} ${rule} 2>/dev/null
  409. rc="${?}"
  410. else
  411. rc=0
  412. fi
  413. fi
  414. if [ "${ban_proto6_enabled}" = "1" ] && { [ "${src_name}" = "maclist" ] || [ "${src_name##*_}" = "6" ]; }; then
  415. rc="$(
  416. "${ban_ipt6_cmd}" "${timeout}" -C ${chain} ${rule} 2>/dev/null
  417. printf "%u" ${?}
  418. )"
  419. if { [ "${rc}" != "0" ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; }; } ||
  420. { [ "${rc}" = "0" ] && [ "${action}" = "-D" ]; }; then
  421. "${ban_ipt6_cmd}" "${timeout}" "${action}" ${chain} ${pos} ${rule} 2>/dev/null
  422. rc="${?}"
  423. else
  424. rc=0
  425. fi
  426. fi
  427. if [ -n "${rc}" ] && [ "${rc}" != "0" ]; then
  428. : >"${tmp_err}"
  429. f_log "info" "${src_name}: iptables action '${action:-"-"}' failed with '${chain}, ${pos:-"-"}, ${rule:-"-"}'"
  430. fi
  431. }
  432. # iptables controller
  433. #
  434. f_iptables() {
  435. local ipt_cmd chain chainsets dev pos timeout="-w 5" destroy="${1}"
  436. if [ "${ban_action}" != "refresh" ] && [ "${ban_action}" != "resume" ]; then
  437. for dev in ${ban_ipdevs}; do
  438. if [ "${src_name}" = "maclist" ]; then
  439. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} src -j RETURN"
  440. elif [ "${src_name%_*}" = "whitelist" ]; then
  441. f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_logtarget_src}"
  442. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_logtarget_dst}"
  443. f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_logchain_src}"
  444. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_logchain_dst}"
  445. f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j RETURN"
  446. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j RETURN"
  447. else
  448. f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_logtarget_src}"
  449. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_logtarget_dst}"
  450. f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_logchain_src}"
  451. f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_logchain_dst}"
  452. fi
  453. done
  454. fi
  455. if [ -z "${destroy}" ] && { [ "${cnt}" -gt "0" ] || [ "${src_name%_*}" = "blacklist" ] || [ "${src_name%_*}" = "whitelist" ]; }; then
  456. if [ "${src_name##*_}" = "4" ]; then
  457. ipt_cmd="${ban_ipt4_cmd}"
  458. if [ ! -f "${ban_tmpfile}.${src_name##*_}.chains" ]; then
  459. : >"${ban_tmpfile}.${src_name##*_}.chains"
  460. chainsets="${ban_lan_inputchains_4} ${ban_wan_inputchains_4} ${ban_lan_forwardchains_4} ${ban_wan_forwardchains_4}"
  461. for chain in ${chainsets}; do
  462. f_iptrule "-I" "${chain}" "-j ${ban_chain}"
  463. done
  464. f_iptrule "-A" "${ban_chain}" "-p udp --dport 67:68 --sport 67:68 -j RETURN"
  465. f_iptrule "-A" "${ban_chain}" "-m conntrack ! --ctstate NEW -j RETURN"
  466. fi
  467. elif [ "${src_name##*_}" = "6" ]; then
  468. ipt_cmd="${ban_ipt6_cmd}"
  469. if [ ! -f "${ban_tmpfile}.${src_name##*_}.chains" ]; then
  470. : >"${ban_tmpfile}.${src_name##*_}.chains"
  471. chainsets="${ban_lan_inputchains_6} ${ban_wan_inputchains_6} ${ban_lan_forwardchains_6} ${ban_wan_forwardchains_6}"
  472. for chain in ${chainsets}; do
  473. f_iptrule "-I" "${chain}" "-j ${ban_chain}"
  474. done
  475. f_iptrule "-A" "${ban_chain}" "-p ipv6-icmp -s fe80::/10 -d fe80::/10 -j RETURN"
  476. f_iptrule "-A" "${ban_chain}" "-p udp -s fc00::/6 --sport 547 -d fc00::/6 --dport 546 -j RETURN"
  477. f_iptrule "-A" "${ban_chain}" "-m conntrack ! --ctstate NEW -j RETURN"
  478. fi
  479. fi
  480. if [ "${src_settype}" != "dst" ]; then
  481. for dev in ${ban_devs}; do
  482. if [ "${src_name}" = "maclist" ]; then
  483. f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} src -j RETURN" "1"
  484. elif [ "${src_name%_*}" = "whitelist" ]; then
  485. pos="$(($("${ipt_cmd}" "${timeout}" -vnL "${ban_chain}" --line-numbers | grep -cF "RETURN") + 1))"
  486. if [ "${ban_whitelistonly}" = "1" ]; then
  487. f_iptrule "-I" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_target_src}" "${pos}"
  488. else
  489. f_iptrule "-I" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j RETURN" "${pos}"
  490. fi
  491. else
  492. f_iptrule "${action:-"-A"}" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_target_src}"
  493. fi
  494. done
  495. fi
  496. if [ "${src_settype}" != "src" ]; then
  497. for dev in ${ban_devs}; do
  498. if [ "${src_name%_*}" = "whitelist" ]; then
  499. pos="$(($("${ipt_cmd}" "${timeout}" -vnL "${ban_chain}" --line-numbers | grep -cF "RETURN") + 1))"
  500. if [ "${ban_whitelistonly}" = "1" ]; then
  501. f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_target_dst}" "${pos}"
  502. else
  503. f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j RETURN" "${pos}"
  504. fi
  505. elif [ "${src_name}" != "maclist" ]; then
  506. f_iptrule "${action:-"-A"}" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_target_dst}"
  507. fi
  508. done
  509. fi
  510. else
  511. "${ban_ipset_cmd}" -q destroy "${src_name}"
  512. fi
  513. }
  514. # ipset controller
  515. #
  516. f_ipset() {
  517. local src src_list action rule ipt_cmd out_rc max="0" cnt="0" cnt_ip="0" cnt_cidr="0" cnt_mac="0" timeout="-w 5" mode="${1}" in_rc="4"
  518. case "${mode}" in
  519. "backup")
  520. gzip -cf "${tmp_load}" 2>/dev/null >"${ban_backupdir}/banIP.${src_name}.gz"
  521. out_rc="${?}"
  522. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  523. return "${out_rc}"
  524. ;;
  525. "restore")
  526. if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]; then
  527. zcat "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null >"${tmp_load}"
  528. out_rc="${?}"
  529. else
  530. out_rc="${in_rc}"
  531. fi
  532. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  533. return "${out_rc}"
  534. ;;
  535. "remove")
  536. if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]; then
  537. rm -f "${ban_backupdir}/banIP.${src_name}.gz"
  538. out_rc="${?}"
  539. else
  540. out_rc="${in_rc}"
  541. fi
  542. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  543. return "${out_rc}"
  544. ;;
  545. "initial")
  546. for proto in "4" "6"; do
  547. if [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; then
  548. ipt_cmd="${ban_ipt4_cmd}"
  549. chainsets="${ban_lan_inputchains_4} ${ban_lan_forwardchains_4} ${ban_wan_inputchains_4} ${ban_wan_forwardchains_4}"
  550. elif [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ]; then
  551. ipt_cmd="${ban_ipt6_cmd}"
  552. chainsets="${ban_lan_inputchains_6} ${ban_lan_forwardchains_6} ${ban_wan_inputchains_6} ${ban_wan_forwardchains_6}"
  553. fi
  554. if { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; } ||
  555. { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ]; }; then
  556. if [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]; then
  557. "${ipt_cmd}" "${timeout}" -N "${ban_chain}" 2>/dev/null
  558. out_rc="${?}"
  559. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, chain: ${ban_chain:-"-"}, out_rc: ${out_rc}"
  560. else
  561. out_rc=0
  562. for chain in ${chainsets}; do
  563. f_iptrule "-D" "${chain}" "-j ${ban_chain}"
  564. done
  565. fi
  566. if [ "${ban_logsrc_enabled}" = "1" ] && [ "${out_rc}" = "0" ] && [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_logchain_src}" 2>/dev/null)" ]; then
  567. "${ipt_cmd}" "${timeout}" -N "${ban_logchain_src}" 2>/dev/null
  568. out_rc="${?}"
  569. if [ "${out_rc}" = "0" ]; then
  570. "${ipt_cmd}" "${timeout}" -A "${ban_logchain_src}" -j LOG ${ban_logopts_src} --log-prefix "${ban_logprefix_src}"
  571. out_rc="${?}"
  572. if [ "${out_rc}" = "0" ]; then
  573. "${ipt_cmd}" "${timeout}" -A "${ban_logchain_src}" -j "${ban_logtarget_src}"
  574. out_rc="${?}"
  575. fi
  576. fi
  577. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, logchain_src: ${ban_logchain_src:-"-"}, out_rc: ${out_rc}"
  578. fi
  579. if [ "${ban_logdst_enabled}" = "1" ] && [ "${out_rc}" = "0" ] && [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_logchain_dst}" 2>/dev/null)" ]; then
  580. "${ipt_cmd}" "${timeout}" -N "${ban_logchain_dst}" 2>/dev/null
  581. out_rc="${?}"
  582. if [ "${out_rc}" = "0" ]; then
  583. "${ipt_cmd}" "${timeout}" -A "${ban_logchain_dst}" -j LOG ${ban_logopts_dst} --log-prefix "${ban_logprefix_dst}"
  584. out_rc="${?}"
  585. if [ "${out_rc}" = "0" ]; then
  586. "${ipt_cmd}" "${timeout}" -A "${ban_logchain_dst}" -j "${ban_logtarget_dst}"
  587. out_rc="${?}"
  588. fi
  589. fi
  590. f_log "debug" "f_ipset ::: name: initial, mode: ${mode:-"-"}, logchain_dst: ${ban_logchain_dst:-"-"}, out_rc: ${out_rc}"
  591. fi
  592. fi
  593. done
  594. out_rc="${out_rc:-"${in_rc}"}"
  595. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  596. return "${out_rc}"
  597. ;;
  598. "create")
  599. if [ -z "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ] &&
  600. { [ -s "${tmp_file}" ] || [ "${src_name%_*}" = "whitelist" ] || [ "${src_name%_*}" = "blacklist" ]; }; then
  601. max="$(awk 'END{print NR}' "${tmp_file}" 2>/dev/null)"
  602. max=$((max + 262144))
  603. if [ "${src_name}" = "maclist" ]; then
  604. "${ban_ipset_cmd}" create "${src_name}" hash:mac hashsize 64 maxelem "${max}" counters timeout "${ban_maclist_timeout:-"0"}"
  605. out_rc="${?}"
  606. elif [ "${src_name%_*}" = "whitelist" ]; then
  607. "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters timeout "${ban_whitelist_timeout:-"0"}"
  608. out_rc="${?}"
  609. elif [ "${src_name%_*}" = "blacklist" ]; then
  610. "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters timeout "${ban_blacklist_timeout:-"0"}"
  611. out_rc="${?}"
  612. else
  613. "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters
  614. out_rc="${?}"
  615. fi
  616. elif [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
  617. "${ban_ipset_cmd}" -q flush "${src_name}"
  618. out_rc="${?}"
  619. fi
  620. if [ -s "${tmp_file}" ] && [ "${out_rc}" = "0" ]; then
  621. "${ban_ipset_cmd}" -q -! restore <"${tmp_file}"
  622. out_rc="${?}"
  623. if [ "${out_rc}" = "0" ]; then
  624. src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
  625. cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
  626. cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
  627. cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
  628. cnt_ip=$((cnt - cnt_cidr - cnt_mac))
  629. printf "%s\n" "${cnt}" >"${tmp_cnt}"
  630. fi
  631. fi
  632. f_iptables
  633. end_ts="$(date +%s)"
  634. out_rc="${out_rc:-"${in_rc}"}"
  635. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, ipver: ${src_ipver:-"-"}, settype: ${src_settype:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
  636. return "${out_rc}"
  637. ;;
  638. "refresh")
  639. if [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
  640. out_rc=0
  641. src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
  642. cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
  643. cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
  644. cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
  645. cnt_ip=$((cnt - cnt_cidr - cnt_mac))
  646. printf "%s\n" "${cnt}" >"${tmp_cnt}"
  647. f_iptables
  648. fi
  649. end_ts="$(date +%s)"
  650. out_rc="${out_rc:-"${in_rc}"}"
  651. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
  652. return "${out_rc}"
  653. ;;
  654. "suspend")
  655. for src in ${ban_sources} ${ban_localsources}; do
  656. if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
  657. tmp_file="${ban_backupdir}/${src}.file"
  658. "${ban_ipset_cmd}" -q save "${src}" | tail -n +2 >"${tmp_file}"
  659. "${ban_ipset_cmd}" -q flush "${src}"
  660. else
  661. for proto in "4" "6"; do
  662. if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
  663. tmp_file="${ban_backupdir}/${src}_${proto}.file"
  664. "${ban_ipset_cmd}" -q save "${src}_${proto}" | tail -n +2 >"${tmp_file}"
  665. "${ban_ipset_cmd}" -q flush "${src}_${proto}"
  666. fi
  667. done
  668. fi
  669. done
  670. f_log "debug" "f_ipset ::: name: ${src:-"-"}, mode: ${mode:-"-"}"
  671. ;;
  672. "resume")
  673. if [ -f "${ban_backupdir}/${src_name}.file" ]; then
  674. "${ban_ipset_cmd}" -q -! restore <"${ban_backupdir}/${src_name}.file"
  675. out_rc="${?}"
  676. if [ "${out_rc}" = "0" ]; then
  677. rm -f "${ban_backupdir}/${src_name}.file"
  678. src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
  679. cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
  680. cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
  681. cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
  682. cnt_ip=$((cnt - cnt_cidr - cnt_mac))
  683. printf "%s\n" "${cnt}" >"${tmp_cnt}"
  684. fi
  685. f_iptables
  686. fi
  687. end_ts="$(date +%s)"
  688. out_rc="${out_rc:-"${in_rc}"}"
  689. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, ipver: ${src_ipver:-"-"}, settype: ${src_settype:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
  690. return "${out_rc}"
  691. ;;
  692. "flush")
  693. if [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
  694. f_iptables "destroy"
  695. out_rc=0
  696. fi
  697. out_rc="${out_rc:-"${in_rc}"}"
  698. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  699. return "${out_rc}"
  700. ;;
  701. "destroy")
  702. for chain in ${ban_chain} ${ban_logchain_src} ${ban_logchain_dst}; do
  703. if [ -n "$("${ban_ipt4_cmd}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]; then
  704. "${ban_ipt4_savecmd}" | grep -v -- "-j ${chain}" | "${ban_ipt4_restorecmd}"
  705. "${ban_ipt4_cmd}" "${timeout}" -F "${chain}" 2>/dev/null
  706. "${ban_ipt4_cmd}" "${timeout}" -X "${chain}" 2>/dev/null
  707. fi
  708. if [ -n "$("${ban_ipt6_cmd}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]; then
  709. "${ban_ipt6_savecmd}" | grep -v -- "-j ${chain}" | "${ban_ipt6_restorecmd}"
  710. "${ban_ipt6_cmd}" "${timeout}" -F "${chain}" 2>/dev/null
  711. "${ban_ipt6_cmd}" "${timeout}" -X "${chain}" 2>/dev/null
  712. fi
  713. done
  714. for src in ${ban_sources} maclist blacklist whitelist; do
  715. if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
  716. "${ban_ipset_cmd}" -q destroy "${src}"
  717. else
  718. for proto in "4" "6"; do
  719. if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
  720. "${ban_ipset_cmd}" -q destroy "${src}_${proto}"
  721. fi
  722. done
  723. fi
  724. done
  725. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
  726. ;;
  727. esac
  728. }
  729. # write to syslog
  730. #
  731. f_log() {
  732. local class="${1}" log_msg="${2}"
  733. if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" = "1" ]; }; then
  734. if [ -x "${ban_logger_cmd}" ]; then
  735. "${ban_logger_cmd}" -p "${class}" -t "banIP-${ban_ver%-*}[${$}]" "${log_msg}"
  736. else
  737. printf "%s %s %s\n" "${class}" "banIP-${ban_ver%-*}[${$}]" "${log_msg}"
  738. fi
  739. if [ "${class}" = "err" ]; then
  740. f_jsnup "error"
  741. f_ipset "destroy"
  742. f_rmbckp
  743. f_rmtmp
  744. exit 1
  745. fi
  746. fi
  747. }
  748. # kill all relevant background processes
  749. #
  750. f_pidx() {
  751. local pids ppid="${1}"
  752. pids="$(pgrep -P "${ppid}" 2>/dev/null | awk '{ORS=" ";print $0}')"
  753. kill -HUP "${ppid}" "${pids}" 2>/dev/null
  754. : >"${ban_bgpidfile}"
  755. }
  756. # start log service to trace failed ssh/luci logins
  757. #
  758. f_bgsrv() {
  759. local bg_pid action="${1}"
  760. bg_pid="$(cat "${ban_bgpidfile}" 2>/dev/null)"
  761. if [ "${action}" = "start" ] && [ -x "${ban_logservice}" ] && [ "${ban_monitor_enabled}" = "1" ] && [ "${ban_whitelistonly}" = "0" ]; then
  762. if [ -n "${bg_pid}" ]; then
  763. f_pidx "${bg_pid}"
  764. fi
  765. if printf "%s\n" "${ban_logterms}" | grep -q "dropbear"; then
  766. ban_search="Exit before auth from|"
  767. fi
  768. if printf "%s\n" "${ban_logterms}" | grep -q "sshd"; then
  769. ban_search="${ban_search}error: maximum authentication attempts exceeded|sshd.*Connection closed by.*\[preauth\]|"
  770. fi
  771. if printf "%s\n" "${ban_logterms}" | grep -q "luci"; then
  772. ban_search="${ban_search}luci: failed login|"
  773. fi
  774. if printf "%s\n" "${ban_logterms}" | grep -q "nginx"; then
  775. ban_search="${ban_search}nginx(\[[0-9]+\])?:.*\[error\].*open().*client: [[:alnum:].:]+|"
  776. fi
  777. (
  778. "${ban_logservice}" "${ban_search%?}" &
  779. printf "%s" "${!}" >"${ban_bgpidfile}"
  780. )
  781. elif { [ "${action}" = "stop" ] || [ "${ban_monitor_enabled}" = "0" ]; } && [ -n "${bg_pid}" ]; then
  782. f_pidx "${bg_pid}"
  783. fi
  784. f_log "debug" "f_bgsrv ::: action: ${action:-"-"}, bg_pid (old/new): ${bg_pid}/$(cat "${ban_bgpidfile}" 2>/dev/null), monitor_enabled: ${ban_monitor_enabled:-"-"}, log_service: ${ban_logservice:-"-"}"
  785. }
  786. # download controller
  787. #
  788. f_down() {
  789. local src_name="${1}" proto="${2}" src_ipver="${3}" src_url="${4}" src_rule="${5}" src_comp="${6}"
  790. local ip start_ts end_ts src_settype src_log src_rc tmp_load tmp_file tmp_raw tmp_cnt tmp_err
  791. start_ts="$(date +%s)"
  792. if printf "%s\n" "${ban_settype_src}" | grep -q "${src_name}"; then
  793. src_settype="src"
  794. elif printf "%s\n" "${ban_settype_dst}" | grep -q "${src_name}"; then
  795. src_settype="dst"
  796. elif printf "%s\n" "${ban_settype_all}" | grep -q "${src_name}"; then
  797. src_settype="src+dst"
  798. else
  799. src_settype="${ban_global_settype}"
  800. fi
  801. src_name="${src_name}_${proto}"
  802. tmp_load="${ban_tmpfile}.${src_name}.load"
  803. tmp_file="${ban_tmpfile}.${src_name}.file"
  804. tmp_raw="${tmp_file}.raw"
  805. tmp_cnt="${tmp_file}.cnt"
  806. tmp_err="${tmp_file}.err"
  807. # 'resume' mode
  808. #
  809. if [ "${ban_action}" = "resume" ]; then
  810. if [ "${src_name%_*}" = "maclist" ]; then
  811. src_name="maclist"
  812. fi
  813. f_ipset "resume"
  814. src_rc="${?}"
  815. if [ "${src_rc}" = "0" ]; then
  816. return
  817. fi
  818. fi
  819. # handle local downloads
  820. #
  821. case "${src_name%_*}" in
  822. "blacklist" | "whitelist")
  823. printf "%s\n" "0" >"${tmp_cnt}"
  824. awk "${src_rule}" "${src_url}" >"${tmp_file}"
  825. src_rc="${?}"
  826. if [ "${src_rc}" = "0" ]; then
  827. f_ipset "create"
  828. if [ ! -f "${tmp_dns}" ] && { { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; } ||
  829. { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ] && [ "${ban_proto4_enabled}" = "0" ]; }; }; then
  830. tmp_dns="${ban_tmpbase}/${src_name%_*}.dns"
  831. src_rule="/^([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}"
  832. awk "${src_rule}" "${src_url}" >"${tmp_dns}"
  833. src_rc="${?}"
  834. if [ "${src_rc}" = "0" ] && [ -s "${tmp_dns}" ]; then
  835. ("${ban_dnsservice}" "${src_name%_*}" "${tmp_dns}" &)
  836. else
  837. rm -f "${tmp_dns}"
  838. fi
  839. fi
  840. else
  841. f_log "debug" "f_down ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}"
  842. fi
  843. return
  844. ;;
  845. "maclist")
  846. src_name="${src_name%_*}"
  847. tmp_file="${ban_tmpfile}.${src_name}.file"
  848. tmp_cnt="${tmp_file}.cnt"
  849. tmp_err="${tmp_file}.err"
  850. awk "${src_rule}" "${src_url}" >"${tmp_file}"
  851. src_rc="${?}"
  852. if [ "${src_rc}" = "0" ]; then
  853. f_ipset "create"
  854. else
  855. f_log "debug" "f_down ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}"
  856. fi
  857. return
  858. ;;
  859. esac
  860. # 'refresh' mode
  861. #
  862. if [ "${ban_action}" = "refresh" ]; then
  863. f_ipset "refresh"
  864. src_rc="${?}"
  865. if [ "${src_rc}" = "0" ]; then
  866. return
  867. fi
  868. fi
  869. # 'start' mode
  870. #
  871. if [ "${ban_action}" = "start" ]; then
  872. f_ipset "restore"
  873. fi
  874. src_rc="${?}"
  875. if [ "${src_rc}" = "0" ]; then
  876. awk "${src_rule}" "${tmp_load}" 2>/dev/null >"${tmp_file}"
  877. src_rc="${?}"
  878. if [ "${src_rc}" = "0" ]; then
  879. f_ipset "create"
  880. src_rc="${?}"
  881. if [ "${src_rc}" = "0" ]; then
  882. return
  883. fi
  884. fi
  885. fi
  886. # handle country related downloads
  887. #
  888. if [ "${src_name%_*}" = "country" ]; then
  889. for country in ${ban_countries}; do
  890. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}${country}-aggregated.zone" 2>&1)"
  891. src_rc="${?}"
  892. if [ "${src_rc}" = "0" ]; then
  893. cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
  894. else
  895. continue
  896. fi
  897. done
  898. # handle asn related downloads
  899. #
  900. elif [ "${src_name%_*}" = "asn" ]; then
  901. for asn in ${ban_asns}; do
  902. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}AS${asn}" 2>&1)"
  903. src_rc="${?}"
  904. if [ "${src_rc}" = "0" ]; then
  905. cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
  906. else
  907. continue
  908. fi
  909. done
  910. # handle compressed downloads
  911. #
  912. elif [ -n "${src_comp}" ]; then
  913. case "${src_comp}" in
  914. "gz")
  915. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}" 2>&1)"
  916. src_rc="${?}"
  917. if [ "${src_rc}" -eq 0 ]; then
  918. zcat "${tmp_raw}" 2>/dev/null >"${tmp_load}"
  919. src_rc="${?}"
  920. fi
  921. ;;
  922. esac
  923. # handle normal downloads
  924. #
  925. else
  926. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_load}" "${src_url}" 2>&1)"
  927. src_rc="${?}"
  928. fi
  929. # download post-processing
  930. #
  931. if [ "${src_rc}" = "0" ]; then
  932. f_ipset "backup"
  933. src_rc="${?}"
  934. elif [ "${ban_action}" != "start" ] && [ "${ban_action}" != "refresh" ]; then
  935. f_ipset "restore"
  936. src_rc="${?}"
  937. fi
  938. if [ "${src_rc}" = "0" ]; then
  939. awk "${src_rule}" "${tmp_load}" 2>/dev/null >"${tmp_file}"
  940. src_rc="${?}"
  941. if [ "${src_rc}" = "0" ]; then
  942. f_ipset "create"
  943. src_rc="${?}"
  944. elif [ "${ban_action}" != "refresh" ]; then
  945. f_ipset "refresh"
  946. src_rc="${?}"
  947. fi
  948. else
  949. src_log="$(printf "%s" "${src_log}" | awk '{ORS=" ";print $0}')"
  950. if [ "${ban_action}" != "refresh" ]; then
  951. f_ipset "refresh"
  952. src_rc="${?}"
  953. fi
  954. f_log "debug" "f_down ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}, log: ${src_log:-"-"}"
  955. fi
  956. }
  957. # main controller
  958. #
  959. f_main() {
  960. local src_name src_url_4 src_rule_4 src_url_6 src_rule_6 src_comp src_rc src_ts log_raw log_merge log_ips log_count hold err_file cnt_file cnt=0
  961. # prepare logfile excerpts
  962. #
  963. if [ "${ban_autoblacklist}" = "1" ] || [ "${ban_monitor_enabled}" = "1" ]; then
  964. log_raw="$(${ban_logread_cmd} -l "${ban_loglimit}")"
  965. if printf "%s\n" "${ban_logterms}" | grep -q "dropbear"; then
  966. log_ips="$(printf "%s\n" "${log_raw}" | grep -E "Exit before auth from" |
  967. awk 'match($0,/<[0-9A-f:\.]+:/){printf "%s\n",substr($0,RSTART+1,RLENGTH-2)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
  968. for ip in ${log_ips}; do
  969. log_count="$(printf "%s\n" "${log_raw}" | grep -cE "Exit before auth from <${ip}")"
  970. if [ "${log_count}" -ge "${ban_ssh_logcount}" ]; then
  971. log_merge="${log_merge} ${ip}"
  972. fi
  973. done
  974. fi
  975. if printf "%s\n" "${ban_logterms}" | grep -q "sshd"; then
  976. log_ips="$(printf "%s\n" "${log_raw}" | grep -E "error: maximum authentication attempts exceeded|sshd.*Connection closed by.*\[preauth\]" |
  977. awk 'match($0,/[0-9A-f:\.]+ port/){printf "%s\n",substr($0,RSTART,RLENGTH-5)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
  978. for ip in ${log_ips}; do
  979. log_count="$(printf "%s\n" "${log_raw}" | grep -cE "error: maximum authentication attempts exceeded.*${ip}|sshd.*Connection closed by.*${ip}.*\[preauth\]")"
  980. if [ "${log_count}" -ge "${ban_ssh_logcount}" ]; then
  981. log_merge="${log_merge} ${ip}"
  982. fi
  983. done
  984. fi
  985. if printf "%s\n" "${ban_logterms}" | grep -q "luci"; then
  986. log_ips="$(printf "%s\n" "${log_raw}" | grep -E "luci: failed login on " |
  987. awk 'match($0,/[0-9A-f:\.]+$/){printf "%s\n",substr($0,RSTART,RLENGTH)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
  988. for ip in ${log_ips}; do
  989. log_count="$(printf "%s\n" "${log_raw}" | grep -cE "luci: failed login on .*from ${ip}")"
  990. if [ "${log_count}" -ge "${ban_luci_logcount}" ]; then
  991. log_merge="${log_merge} ${ip}"
  992. fi
  993. done
  994. fi
  995. if printf "%s\n" "${ban_logterms}" | grep -q "nginx"; then
  996. log_ips="$(printf "%s\n" "${log_raw}" | grep -oE "nginx(\[[0-9]+\])?:.*\[error\].*open\(\).*client: [[:alnum:].:]+" |
  997. awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
  998. for ip in ${log_ips}; do
  999. log_count="$(printf "%s\n" "${log_raw}" | grep -cE "nginx(\[[0-9]+\])?:.*\[error\].*open\(\).*client: ${ip}")"
  1000. if [ "${log_count}" -ge "${ban_nginx_logcount}" ]; then
  1001. log_merge="${log_merge} ${ip}"
  1002. fi
  1003. done
  1004. fi
  1005. fi
  1006. # prepare new black- and whitelist entries
  1007. #
  1008. if [ "${ban_autowhitelist}" = "1" ] && [ -f "${ban_whitelist}" ]; then
  1009. for ip in ${ban_subnets}; do
  1010. if ! grep -q "${ip}" "${ban_whitelist}"; then
  1011. src_ts="# added on $(date "+%d.%m.%Y %H:%M:%S")"
  1012. printf "%-42s%s\n" "${ip}" "${src_ts}" >>"${ban_whitelist}"
  1013. f_log "info" "IP address '${ip}' added to whitelist"
  1014. fi
  1015. done
  1016. fi
  1017. if [ "${ban_autoblacklist}" = "1" ] && [ -f "${ban_blacklist}" ]; then
  1018. for ip in ${log_merge}; do
  1019. if ! grep -q "${ip}" "${ban_blacklist}"; then
  1020. src_ts="# added on $(date "+%d.%m.%Y %H:%M:%S")"
  1021. printf "%-42s%s\n" "${ip}" "${src_ts}" >>"${ban_blacklist}"
  1022. f_log "info" "IP address '${ip}' added to blacklist"
  1023. fi
  1024. done
  1025. fi
  1026. # initial ipset/iptables creation
  1027. #
  1028. if ! f_ipset "initial"; then
  1029. f_log "err" "banIP processing failed, fatal error during ipset/iptables creation (${ban_sysver})"
  1030. fi
  1031. # load local source files (maclist, blacklist, whitelist)
  1032. #
  1033. for src_name in ${ban_localsources}; do
  1034. if [ "${src_name}" = "maclist" ] && [ -s "${ban_maclist}" ]; then
  1035. (
  1036. src_rule_4="/^([0-9A-z][0-9A-z]:){5}[0-9A-z]{2}([[:space:]]|$)/{print \"add ${src_name} \"toupper(\$1)}"
  1037. f_down "${src_name}" "mac" "mac" "${ban_maclist}" "${src_rule_4}"
  1038. ) &
  1039. fi
  1040. if [ "${ban_proto4_enabled}" = "1" ]; then
  1041. if [ "${src_name}" = "blacklist" ] && [ -s "${ban_blacklist}" ] && [ "${ban_whitelistonly}" = "0" ]; then
  1042. (
  1043. src_rule_4="/^(([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:]]|$)/{print \"add ${src_name}_4 \"\$1}"
  1044. f_down "${src_name}" "4" "inet" "${ban_blacklist}" "${src_rule_4}"
  1045. ) &
  1046. elif [ "${src_name}" = "whitelist" ] && [ -s "${ban_whitelist}" ]; then
  1047. (
  1048. src_rule_4="/^(([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:]]|$)/{print \"add ${src_name}_4 \"\$1}"
  1049. f_down "${src_name}" "4" "inet" "${ban_whitelist}" "${src_rule_4}"
  1050. ) &
  1051. fi
  1052. else
  1053. (
  1054. src_name="${src_name}_4"
  1055. f_ipset "flush"
  1056. ) &
  1057. fi
  1058. if [ "${ban_proto6_enabled}" = "1" ]; then
  1059. if [ "${src_name}" = "blacklist" ] && [ -s "${ban_blacklist}" ] && [ "${ban_whitelistonly}" = "0" ]; then
  1060. (
  1061. src_rule_6="/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add ${src_name}_6 \"\$1}"
  1062. f_down "${src_name}" "6" "inet6" "${ban_blacklist}" "${src_rule_6}"
  1063. ) &
  1064. elif [ "${src_name}" = "whitelist" ] && [ -s "${ban_whitelist}" ]; then
  1065. (
  1066. src_rule_6="/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add ${src_name}_6 \"\$1}"
  1067. f_down "${src_name}" "6" "inet6" "${ban_whitelist}" "${src_rule_6}"
  1068. ) &
  1069. fi
  1070. else
  1071. (
  1072. src_name="${src_name}_6"
  1073. f_ipset "flush"
  1074. ) &
  1075. fi
  1076. done
  1077. wait
  1078. # loop over all external sources
  1079. #
  1080. if [ "${ban_whitelistonly}" = "0" ]; then
  1081. for src_name in ${ban_sources}; do
  1082. # get source data from JSON file
  1083. #
  1084. if ! json_select "${src_name}" >/dev/null 2>&1; then
  1085. continue
  1086. fi
  1087. json_objects="url_4 rule_4 url_6 rule_6 comp"
  1088. for object in ${json_objects}; do
  1089. eval json_get_var src_${object} '${object}' >/dev/null 2>&1
  1090. done
  1091. json_select ..
  1092. # handle external IPv4 source downloads in a subshell
  1093. #
  1094. if [ "${ban_proto4_enabled}" = "1" ] && [ -n "${src_url_4}" ] && [ -n "${src_rule_4}" ]; then
  1095. (
  1096. f_down "${src_name}" "4" "inet" "${src_url_4}" "${src_rule_4}" "${src_comp}"
  1097. ) &
  1098. fi
  1099. # handle external IPv6 source downloads in a subshell
  1100. #
  1101. if [ "${ban_proto6_enabled}" = "1" ] && [ -n "${src_url_6}" ] && [ -n "${src_rule_6}" ]; then
  1102. (
  1103. f_down "${src_name}" "6" "inet6" "${src_url_6}" "${src_rule_6}" "${src_comp}"
  1104. ) &
  1105. fi
  1106. # control/limit download queues
  1107. #
  1108. hold=$((cnt % ban_maxqueue))
  1109. if [ "${hold}" = "0" ]; then
  1110. wait
  1111. fi
  1112. cnt=$((cnt + 1))
  1113. done
  1114. wait
  1115. fi
  1116. # error out
  1117. #
  1118. for err_file in "${ban_tmpfile}."*".err"; do
  1119. if [ -f "${err_file}" ]; then
  1120. f_log "err" "banIP processing failed, fatal iptables errors during subshell processing (${ban_sysver})"
  1121. fi
  1122. done
  1123. # finish processing
  1124. #
  1125. ban_sources=""
  1126. for cnt_file in "${ban_tmpfile}."*".cnt"; do
  1127. if [ -f "${cnt_file}" ]; then
  1128. read -r cnt <"${cnt_file}"
  1129. ban_cnt=$((ban_cnt + cnt))
  1130. ban_setcnt=$((ban_setcnt + 1))
  1131. src_name="$(printf "%s" "${cnt_file}" | grep -Eo "[a-z0-9_]+.file.cnt")"
  1132. src_name="${src_name%%.*}"
  1133. if ! printf "%s" "${ban_sources}" | grep -q "${src_name%_*}"; then
  1134. ban_sources="${ban_sources} ${src_name%_*}"
  1135. ban_allsources="${ban_allsources//${src_name%_*}/}"
  1136. fi
  1137. fi
  1138. done
  1139. for src_name in ${ban_allsources}; do
  1140. if [ "${src_name}" = "maclist" ]; then
  1141. f_ipset "flush"
  1142. else
  1143. for proto in "4" "6"; do
  1144. src_name="${src_name%_*}_${proto}"
  1145. f_ipset "flush"
  1146. if [ "${src_name%_*}" != "blacklist" ] && [ "${src_name%_*}" != "whitelist" ]; then
  1147. f_ipset "remove"
  1148. fi
  1149. done
  1150. fi
  1151. done
  1152. f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
  1153. f_jsnup
  1154. f_rmtmp
  1155. f_bgsrv "start"
  1156. }
  1157. # query ipsets for certain IP
  1158. #
  1159. f_query() {
  1160. local src proto result query_start query_end query_timeout="30" match="0" search="${1}"
  1161. if [ -z "${search}" ]; then
  1162. printf "%s\n" "::: missing search term, please submit a single ip or mac address :::"
  1163. else
  1164. query_start="$(date "+%s")"
  1165. printf "%s\n%s\n%s\n" ":::" "::: search '${search}' in banIP related IPSets" ":::"
  1166. for src in ${ban_localsources} ${ban_sources} ${ban_extrasources}; do
  1167. if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
  1168. result="$(
  1169. ipset -q test ${src} ${search} >/dev/null 2>&1
  1170. printf "%u" "${?}"
  1171. )"
  1172. if [ "${result}" = "0" ]; then
  1173. match="1"
  1174. printf "%s\n" " - found in IPSet '${src}'"
  1175. break
  1176. fi
  1177. else
  1178. for proto in "4" "6"; do
  1179. if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
  1180. result="$(
  1181. ipset -q test ${src}_${proto} ${search} >/dev/null 2>&1
  1182. printf "%u" "${?}"
  1183. )"
  1184. if [ "${result}" = "0" ]; then
  1185. match="1"
  1186. printf "%s\n" " - found in IPSet '${src}_${proto}'"
  1187. fi
  1188. fi
  1189. done
  1190. fi
  1191. query_end="$(date "+%s")"
  1192. if [ "$((query_end - query_start))" -gt "${query_timeout}" ]; then
  1193. printf "%s\n\n" " - [...]"
  1194. break
  1195. fi
  1196. done
  1197. if [ "${match}" = "0" ]; then
  1198. printf "%s\n\n" " - no match"
  1199. fi
  1200. fi
  1201. }
  1202. # generate statistics
  1203. #
  1204. f_report() {
  1205. local report_json report_txt bg_pid content proto src src_list detaillist type jsnval jsnvals jsnval1 jsnval2 jsnval3 jsnval4 jsnval5 jsnval6 jsnval7
  1206. local cnt cnt_mac cnt_cidr cnt_ip cnt_acc cnt_sum="0" cnt_set_sum="1" cnt_acc_sum="0" cnt_mac_sum="0" cnt_ip_sum="0" cnt_cidr_sum="0" cnt_set_sum="0" action="${1}"
  1207. report_json="${ban_reportdir}/ban_report.json"
  1208. report_txt="${ban_reportdir}/ban_mailreport.txt"
  1209. # build json file
  1210. #
  1211. if [ "${action}" != "json" ] && { "${ban_ipt4_savecmd}" | grep -q " ${ban_chain} " || "${ban_ipt6_savecmd}" | grep -q " ${ban_chain} "; }; then
  1212. : >"${report_json}"
  1213. printf "%s\n" "{" >>"${report_json}"
  1214. printf "%s\n" '"ipsets": {' >>"${report_json}"
  1215. for src in ${ban_localsources} ${ban_sources} ${ban_extrasources}; do
  1216. if printf "%s" "${ban_extrasources}" | grep -q "${src}"; then
  1217. set_type="n/a"
  1218. else
  1219. if printf "%s\n" "${ban_settype_src}" | grep -q "${src}"; then
  1220. set_type="src"
  1221. elif printf "%s\n" "${ban_settype_dst}" | grep -q "${src}"; then
  1222. set_type="dst"
  1223. elif printf "%s\n" "${ban_settype_all}" | grep -q "${src}"; then
  1224. set_type="src+dst"
  1225. else
  1226. set_type="${ban_global_settype}"
  1227. fi
  1228. fi
  1229. if [ "${src}" = "maclist" ]; then
  1230. src_list="$("${ban_ipset_cmd}" -q list "${src}")"
  1231. if [ -n "${src_list}" ]; then
  1232. cnt="$(printf "%s" "${src_list}" | awk '/^Number of entries:/{print $4}')"
  1233. cnt_acc="$(printf "%s" "${src_list}" | grep -cE "packets [1-9]+")"
  1234. cnt_acc_sum=$((cnt_acc_sum + cnt_acc))
  1235. cnt_mac_sum="${cnt}"
  1236. cnt_sum=$((cnt_sum + cnt))
  1237. {
  1238. if [ "${cnt_set_sum}" != "0" ]; then
  1239. printf "%s\n" ","
  1240. fi
  1241. printf "\t%s\n" "\"${src}\": {"
  1242. printf "\t\t%s\n" "\"type\": \"${set_type}\","
  1243. printf "\t\t%s\n" "\"count\": \"${cnt}\","
  1244. printf "\t\t%s\n" '"count_ip": "0",'
  1245. printf "\t\t%s\n" '"count_cidr": "0",'
  1246. printf "\t\t%s\n" "\"count_mac\": \"${cnt}\","
  1247. printf "\t\t%s" "\"count_acc\": \"${cnt_acc}\""
  1248. printf ",\n\t\t%s" '"member_acc": ['
  1249. printf "%s" "${src_list}" | awk 'match($0,/ packets [1-9]+/){printf "%s %s\n",$1,substr($0,RSTART+9,RLENGTH-9)}' |
  1250. awk 'BEGIN{i=0};{i=i+1;if(i==1){printf "{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}
  1251. else{printf ",\n\t\t{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}}'
  1252. printf "%s\n" "]"
  1253. printf "\t%s" "}"
  1254. } >>"${report_json}"
  1255. cnt_set_sum=$((cnt_set_sum + 1))
  1256. fi
  1257. else
  1258. for proto in "4" "6"; do
  1259. src_list="$("${ban_ipset_cmd}" -q list "${src}_${proto}")"
  1260. if [ -n "${src_list}" ]; then
  1261. cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
  1262. cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "/[0-9]{1,3} ")"
  1263. cnt_ip=$((cnt - cnt_cidr - cnt_mac))
  1264. cnt_acc="$(printf "%s\n" "${src_list}" | grep -cE "packets [1-9]+")"
  1265. cnt_cidr_sum=$((cnt_cidr_sum + cnt_cidr))
  1266. cnt_ip_sum=$((cnt_ip_sum + cnt_ip))
  1267. cnt_acc_sum=$((cnt_acc_sum + cnt_acc))
  1268. cnt_sum=$((cnt_sum + cnt))
  1269. {
  1270. if [ "${cnt_set_sum}" != "0" ]; then
  1271. printf "%s\n" ","
  1272. fi
  1273. printf "\t%s\n" "\"${src}_${proto}\": {"
  1274. printf "\t\t%s\n" "\"type\": \"${set_type}\","
  1275. printf "\t\t%s\n" "\"count\": \"${cnt}\","
  1276. printf "\t\t%s\n" "\"count_ip\": \"${cnt_ip}\","
  1277. printf "\t\t%s\n" "\"count_cidr\": \"${cnt_cidr}\","
  1278. printf "\t\t%s\n" '"count_mac": "0",'
  1279. printf "\t\t%s" "\"count_acc\": \"${cnt_acc}\""
  1280. printf ",\n\t\t%s" '"member_acc": ['
  1281. printf "%s" "${src_list}" | awk 'match($0,/ packets [1-9]+/){printf "%s %s\n",$1,substr($0,RSTART+9,RLENGTH-9)}' |
  1282. awk 'BEGIN{i=0};{i=i+1;if(i==1){printf "{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}
  1283. else{printf ",\n\t\t{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}}'
  1284. printf "%s\n" "]"
  1285. printf "\t%s" "}"
  1286. } >>"${report_json}"
  1287. cnt_set_sum=$((cnt_set_sum + 1))
  1288. fi
  1289. done
  1290. fi
  1291. done
  1292. {
  1293. printf "\n%s" "}"
  1294. printf ",\n%s\n" "\"timestamp\": \"$(date "+%d.%m.%Y %H:%M:%S")\","
  1295. printf "%s\n" "\"cnt_set_sum\": \"${cnt_set_sum}\","
  1296. printf "%s\n" "\"cnt_ip_sum\": \"${cnt_ip_sum}\","
  1297. printf "%s\n" "\"cnt_cidr_sum\": \"${cnt_cidr_sum}\","
  1298. printf "%s\n" "\"cnt_mac_sum\": \"${cnt_mac_sum}\","
  1299. printf "%s\n" "\"cnt_sum\": \"${cnt_sum}\","
  1300. printf "%s\n" "\"cnt_acc_sum\": \"${cnt_acc_sum}\""
  1301. printf "%s\n" "}"
  1302. } >>"${report_json}"
  1303. fi
  1304. # output preparation
  1305. #
  1306. if [ -s "${report_json}" ] && { [ "${action}" = "cli" ] || [ "${action}" = "mail" ]; }; then
  1307. : >"${report_txt}"
  1308. json_init
  1309. if json_load_file "${report_json}" >/dev/null 2>&1; then
  1310. json_get_var jsnval1 "timestamp" >/dev/null 2>&1
  1311. json_get_var jsnval2 "cnt_set_sum" >/dev/null 2>&1
  1312. json_get_var jsnval3 "cnt_sum" >/dev/null 2>&1
  1313. json_get_var jsnval4 "cnt_ip_sum" >/dev/null 2>&1
  1314. json_get_var jsnval5 "cnt_cidr_sum" >/dev/null 2>&1
  1315. json_get_var jsnval6 "cnt_mac_sum" >/dev/null 2>&1
  1316. json_get_var jsnval7 "cnt_acc_sum" >/dev/null 2>&1
  1317. {
  1318. printf "%s\n%s\n%s\n" ":::" "::: report on all banIP related IPSets" ":::"
  1319. printf " + %s\n" "Report timestamp ::: ${jsnval1:-"-"}"
  1320. printf " + %s\n" "Number of all IPSets ::: ${jsnval2:-"0"}"
  1321. printf " + %s\n" "Number of all entries ::: ${jsnval3:-"0"}"
  1322. printf " + %s\n" "Number of IP entries ::: ${jsnval4:-"0"}"
  1323. printf " + %s\n" "Number of CIDR entries ::: ${jsnval5:-"0"}"
  1324. printf " + %s\n" "Number of MAC entries ::: ${jsnval6:-"0"}"
  1325. printf " + %s\n" "Number of accessed entries ::: ${jsnval7:-"0"}"
  1326. } >>"${report_txt}"
  1327. json_select "ipsets"
  1328. json_get_keys ipsetlist
  1329. if [ -n "${ipsetlist}" ]; then
  1330. {
  1331. printf "%s\n%s\n%s\n" ":::" "::: IPSet details" ":::"
  1332. printf "%-25s%-12s%-11s%-10s%-10s%-10s%-10s%s\n" " Name" "Type" "Count" "Cnt_IP" "Cnt_CIDR" "Cnt_MAC" "Cnt_ACC" "Entry details (Entry/Count)"
  1333. printf "%s\n" " --------------------------------------------------------------------------------------------------------------------"
  1334. } >>"${report_txt}"
  1335. fi
  1336. for ipset in ${ipsetlist}; do
  1337. set_info="${ipset}"
  1338. acc_info=""
  1339. json_select "${ipset}"
  1340. json_get_keys detaillist
  1341. for detail in ${detaillist}; do
  1342. if [ "${detail}" != "member_acc" ]; then
  1343. json_get_var jsnval "${detail}" >/dev/null 2>&1
  1344. set_info="${set_info} ${jsnval}"
  1345. elif [ "${detail}" = "member_acc" ]; then
  1346. index=1
  1347. json_select "${detail}"
  1348. while json_get_type type "${index}" && [ "${type}" = "object" ]; do
  1349. json_get_values jsnvals "${index}" >/dev/null 2>&1
  1350. acc_info="${acc_info} ${jsnvals}"
  1351. index=$((index + 1))
  1352. done
  1353. json_select ".."
  1354. fi
  1355. done
  1356. {
  1357. printf " %-21s%-12s%-11s%-10s%-10s%-10s%s\n" ${set_info}
  1358. if [ -n "${acc_info}" ]; then
  1359. printf " %-25s%s\n" ${acc_info}
  1360. fi
  1361. printf "%s\n" " --------------------------------------------------------------------------------------------------------------------"
  1362. } >>"${report_txt}"
  1363. json_select ".."
  1364. done
  1365. content="$(cat "${report_txt}" 2>/dev/null)"
  1366. rm -f "${report_txt}"
  1367. fi
  1368. fi
  1369. # report output
  1370. #
  1371. if [ "${action}" = "cli" ]; then
  1372. printf "%s\n" "${content}"
  1373. elif [ "${action}" = "json" ]; then
  1374. cat "${ban_reportdir}/ban_report.json"
  1375. elif [ "${action}" = "mail" ] && [ "${ban_mail_enabled}" = "1" ] && [ -x "${ban_mailservice}" ]; then
  1376. ("${ban_mailservice}" "${content}" >/dev/null 2>&1) &
  1377. bg_pid="${!}"
  1378. fi
  1379. f_log "debug" "f_report ::: action: ${action}, report_json: ${report_json}, report_txt: ${report_txt}, bg_pid: ${bg_pid:-"-"}"
  1380. }
  1381. # update runtime information
  1382. #
  1383. f_jsnup() {
  1384. local memory entry runtime cnt_info status="${1:-"enabled"}"
  1385. if [ "${status}" = "enabled" ] || [ "${status}" = "error" ]; then
  1386. ban_endtime="$(date "+%s")"
  1387. cnt_info="${ban_setcnt} IPSets with ${ban_cnt} IPs/Prefixes"
  1388. memory="$(awk '/^MemTotal|^MemFree|^MemAvailable/{ORS="/"; print int($2/1000)}' "/proc/meminfo" 2>/dev/null | awk '{print substr($0,1,length($0)-1)}')"
  1389. if [ "$(((ban_endtime - ban_starttime) / 60))" -lt "60" ]; then
  1390. runtime="${ban_action}, $(((ban_endtime - ban_starttime) / 60))m $(((ban_endtime - ban_starttime) % 60))s, ${memory:-0}, $(date "+%d.%m.%Y %H:%M:%S")"
  1391. else
  1392. runtime="${ban_action}, n/a, ${memory:-0}, $(date "+%d.%m.%Y %H:%M:%S")"
  1393. fi
  1394. fi
  1395. : >"${ban_rtfile}"
  1396. json_init
  1397. json_load_file "${ban_rtfile}" >/dev/null 2>&1
  1398. json_add_string "status" "${status}"
  1399. json_add_string "version" "${ban_ver}"
  1400. json_add_string "ipset_info" "${cnt_info:-"-"}"
  1401. json_add_array "active_sources"
  1402. if [ "${status}" = "running" ] || [ "${status}" = "error" ]; then
  1403. json_add_object
  1404. json_add_string "source" "-"
  1405. json_close_object
  1406. else
  1407. for entry in ${ban_sources}; do
  1408. json_add_object
  1409. json_add_string "source" "${entry}"
  1410. json_close_object
  1411. done
  1412. fi
  1413. json_close_array
  1414. json_add_array "active_devs"
  1415. for entry in ${ban_devs}; do
  1416. json_add_object
  1417. json_add_string "dev" "${entry}"
  1418. json_close_object
  1419. done
  1420. json_close_array
  1421. json_add_array "active_ifaces"
  1422. for entry in ${ban_ifaces}; do
  1423. json_add_object
  1424. json_add_string "iface" "${entry}"
  1425. json_close_object
  1426. done
  1427. json_close_array
  1428. json_add_array "active_logterms"
  1429. for entry in ${ban_logterms}; do
  1430. json_add_object
  1431. json_add_string "term" "${entry}"
  1432. json_close_object
  1433. done
  1434. json_close_array
  1435. json_add_array "active_subnets"
  1436. for entry in ${ban_subnets}; do
  1437. json_add_object
  1438. json_add_string "subnet" "${entry}"
  1439. json_close_object
  1440. done
  1441. json_close_array
  1442. json_add_string "run_infos" "settype: ${ban_global_settype}, backup_dir: ${ban_backupdir}, report_dir: ${ban_reportdir}"
  1443. json_add_string "run_flags" "protocols (4/6): $(f_char ${ban_proto4_enabled})/$(f_char ${ban_proto6_enabled}), log (src/dst): $(f_char ${ban_logsrc_enabled})/$(f_char ${ban_logdst_enabled}), monitor: $(f_char ${ban_monitor_enabled}), mail: $(f_char ${ban_mail_enabled}), whitelist only: $(f_char ${ban_whitelistonly})"
  1444. json_add_string "last_run" "${runtime:-"-"}"
  1445. json_add_string "system" "${ban_sysver}"
  1446. json_dump >"${ban_rtfile}"
  1447. if [ "${ban_mail_enabled}" = "1" ] && [ -x "${ban_mailservice}" ] && { [ "${status}" = "error" ] ||
  1448. { [ "${status}" = "enabled" ] && { [ -z "${ban_mailactions}" ] || printf "%s\n" "${ban_mailactions}" | grep -q "${ban_action}"; }; }; }; then
  1449. ("${ban_mailservice}" "${ban_ver}" >/dev/null 2>&1) &
  1450. bg_pid="${!}"
  1451. fi
  1452. f_log "debug" "f_jsnup ::: status: ${status:-"-"}, action: ${ban_action}, mail_enabled: ${ban_mail_enabled}, mail_actions: ${ban_mailactions}, mail_service: ${ban_mailservice}, mail_pid: ${bg_pid:-"-"}"
  1453. }
  1454. # source required system libraries
  1455. #
  1456. if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]; then
  1457. . "/lib/functions.sh"
  1458. . "/lib/functions/network.sh"
  1459. . "/usr/share/libubox/jshn.sh"
  1460. else
  1461. f_log "err" "system libraries not found"
  1462. fi
  1463. if [ "${ban_action}" = "suspend" ] || [ "${ban_action}" = "resume" ] ||
  1464. [ "${ban_action}" = "report" ] || [ "${ban_action}" = "query" ]; then
  1465. json_init
  1466. json_load_file "${ban_rtfile}" >/dev/null 2>&1
  1467. json_get_var ban_status "status"
  1468. fi
  1469. # handle different banIP actions
  1470. #
  1471. f_load
  1472. case "${ban_action}" in
  1473. "stop")
  1474. f_bgsrv "stop"
  1475. f_ipset "destroy"
  1476. f_jsnup "stopped"
  1477. f_rmbckp
  1478. ;;
  1479. "restart")
  1480. f_ipset "destroy"
  1481. f_rmbckp
  1482. f_env
  1483. f_main
  1484. ;;
  1485. "suspend")
  1486. if [ "${ban_status}" = "enabled" ] && [ "${ban_whitelistonly}" = "0" ]; then
  1487. f_bgsrv "stop"
  1488. f_jsnup "running"
  1489. f_ipset "suspend"
  1490. f_jsnup "paused"
  1491. fi
  1492. f_rmtmp
  1493. ;;
  1494. "resume")
  1495. if [ "${ban_status}" = "paused" ] && [ "${ban_whitelistonly}" = "0" ]; then
  1496. f_env
  1497. f_main
  1498. else
  1499. f_rmtmp
  1500. fi
  1501. ;;
  1502. "query")
  1503. if [ "${ban_status}" = "enabled" ]; then
  1504. f_query "${2}"
  1505. fi
  1506. ;;
  1507. "report")
  1508. if [ "${ban_status}" = "enabled" ] || [ "${2}" = "json" ]; then
  1509. f_report "${2}"
  1510. fi
  1511. ;;
  1512. "start" | "reload" | "refresh")
  1513. f_env
  1514. f_main
  1515. ;;
  1516. esac