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.

1870 lines
59 KiB

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