zapret/ipset/create_ipset.sh
2024-10-28 09:32:24 +03:00

309 lines
8.0 KiB
Bash
Executable File

#!/bin/sh
# create ipset or ipfw table from resolved ip's
# $1=no-update - do not update ipset, only create if its absent
# $1=clear - clear ipset
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
. "$EXEDIR/def.sh"
. "$ZAPRET_BASE/common/fwtype.sh"
. "$ZAPRET_BASE/common/nft.sh"
IPSET_CMD="$TMPDIR/ipset_cmd.txt"
IPSET_SAVERAM_CHUNK_SIZE=20000
IPSET_SAVERAM_MIN_FILESIZE=131072
NFSET_TEMP="$TMPDIR/nfset_temp.txt"
NFSET_SAVERAM_MIN_FILESIZE=16384
NFSET_SAVERAM_CHUNK_SIZE=1000
IPSET_HOOK_TEMP="$TMPDIR/ipset_hook.txt"
while [ -n "$1" ]; do
[ "$1" = "no-update" ] && NO_UPDATE=1
[ "$1" = "clear" ] && DO_CLEAR=1
shift
done
file_extract_lines()
{
# $1 - filename
# $2 - from line (starting with 0)
# $3 - line count
# awk "{ err=1 } NR < $(($2+1)) { next } { print; err=0 } NR == $(($2+$3)) { exit err } END {exit err}" "$1"
$AWK "NR < $(($2+1)) { next } { print } NR == $(($2+$3)) { exit }" "$1"
}
ipset_restore_chunked()
{
# $1 - filename
# $2 - chunk size
local pos lines
[ -f "$1" ] || return
lines=$(wc -l <"$1")
pos=$lines
while [ "$pos" -gt "0" ]; do
pos=$((pos-$2))
[ "$pos" -lt "0" ] && pos=0
file_extract_lines "$1" $pos $2 | ipset -! restore
sed -i "$(($pos+1)),$ d" "$1"
done
}
ipset_get_script()
{
# $1 - ipset name
sed -nEe "s/^.+$/add $1 &/p"
}
ipset_get_script_from_file()
{
# $1 - filename
# $2 - ipset name
zzcat "$1" | sort -u | ipset_get_script $2
}
ipset_restore()
{
# $1 - ipset name
# $2 - filename
zzexist "$2" || return
local fsize=$(zzsize "$2")
local svram=0
# do not saveram small files. file can also be gzipped
[ "$SAVERAM" = "1" ] && [ "$fsize" -ge "$IPSET_SAVERAM_MIN_FILESIZE" ] && svram=1
local T="Adding to ipset $1 "
[ "$svram" = "1" ] && T="$T (saveram)"
T="$T : $f"
echo $T
if [ "$svram" = "1" ]; then
ipset_get_script_from_file "$2" "$1" >"$IPSET_CMD"
ipset_restore_chunked "$IPSET_CMD" $IPSET_SAVERAM_CHUNK_SIZE
rm -f "$IPSET_CMD"
else
ipset_get_script_from_file "$2" "$1" | ipset -! restore
fi
}
create_ipset()
{
if [ "$1" -eq "6" ]; then
FAMILY=inet6
else
FAMILY=inet
fi
ipset create $2 $3 $4 family $FAMILY 2>/dev/null || {
[ "$NO_UPDATE" = "1" ] && return 0
}
ipset flush $2
[ "$DO_CLEAR" = "1" ] || {
for f in "$5" "$6" ; do
ipset_restore "$2" "$f"
done
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK $2 | ipset_get_script $2 | ipset -! restore
}
return 0
}
nfset_get_script_multi()
{
# $1 - set name
# $2,$3,... - filenames
# all in one shot. this allows to merge overlapping ranges
# good but eats lots of RAM
local set=$1 nonempty N=1 f
shift
# first we need to make sure at least one element exists or nft will fail
while :
do
eval f=\$$N
[ -n "$f" ] || break
nonempty=$(zzexist "$f" && zzcat "$f" 2>/dev/null | head -n 1)
[ -n "$nonempty" ] && break
N=$(($N+1))
done
[ -n "$nonempty" ] && {
echo "add element inet $ZAPRET_NFT_TABLE $set {"
while [ -n "$1" ]; do
zzexist "$1" && zzcat "$1" | sed -nEe "s/^.+$/&,/p"
shift
done
echo "}"
}
}
nfset_restore()
{
# $1 - set name
# $2,$3,... - filenames
echo "Adding to nfset $1 : $2 $3 $4 $5"
local hookfile
[ -n "$IPSET_HOOK" ] && {
$IPSET_HOOK $1 >"$IPSET_HOOK_TEMP"
[ -s "$IPSET_HOOK_TEMP" ] && hookfile=$IPSET_HOOK_TEMP
}
nfset_get_script_multi "$@" $hookfile | nft -f -
rm -f "$IPSET_HOOK_TEMP"
}
create_nfset()
{
# $1 - family
# $2 - set name
# $3 - maxelem
# $4,$5 - list files
local policy
[ $SAVERAM = "1" ] && policy="policy memory;"
nft_create_set $2 "type ipv${1}_addr; size $3; flags interval; auto-merge; $policy" || {
[ "$NO_UPDATE" = "1" ] && return 0
nft flush set inet $ZAPRET_NFT_TABLE $2
}
[ "$DO_CLEAR" = "1" ] || {
nfset_restore $2 $4 $5
}
return 0
}
add_ipfw_table()
{
# $1 - table name
sed -nEe "s/^.+$/table $1 add &/p" | ipfw -q /dev/stdin
}
populate_ipfw_table()
{
# $1 - table name
# $2 - ip list file
zzexist "$2" || return
zzcat "$2" | sort -u | add_ipfw_table $1
}
create_ipfw_table()
{
# $1 - table name
# $2 - table options
# $3,$4, ... - ip list files. can be v4,v6 or mixed
local name=$1
ipfw table "$name" create $2 2>/dev/null || {
[ "$NO_UPDATE" = "1" ] && return 0
}
ipfw -q table $1 flush
shift
shift
[ "$DO_CLEAR" = "1" ] || {
while [ -n "$1" ]; do
echo "Adding to ipfw table $name : $1"
populate_ipfw_table $name "$1"
shift
done
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK $name | add_ipfw_table $name
}
return 0
}
print_reloading_backend()
{
# $1 - backend name
local s="reloading $1 backend"
if [ "$NO_UPDATE" = 1 ]; then
s="$s (no-update)"
elif [ "$DO_CLEAR" = 1 ]; then
s="$s (clear)"
else
s="$s (forced-update)"
fi
echo $s
}
oom_adjust_high
get_fwtype
if [ -n "$LISTS_RELOAD" ] ; then
if [ "$LISTS_RELOAD" = "-" ] ; then
echo not reloading ip list backend
true
else
echo executing custom ip list reload command : $LISTS_RELOAD
$LISTS_RELOAD
[ -n "$IPSET_HOOK" ] && $IPSET_HOOK
fi
else
case "$FWTYPE" in
iptables)
# ipset seem to buffer the whole script to memory
# on low RAM system this can cause oom errors
# in SAVERAM mode we feed script lines in portions starting from the end, while truncating source file to free /tmp space
# only /tmp is considered tmpfs. other locations mean tmpdir was redirected to a disk
SAVERAM=0
[ "$TMPDIR" = "/tmp" ] && {
RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}')
[ "$RAMSIZE" -lt "110000" ] && SAVERAM=1
}
print_reloading_backend ipset
[ "$DISABLE_IPV4" != "1" ] && {
create_ipset 4 $ZIPSET hash:net "$IPSET_OPT" "$ZIPLIST" "$ZIPLIST_USER"
create_ipset 4 $ZIPSET_IPBAN hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_ipset 4 $ZIPSET_EXCLUDE hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
}
[ "$DISABLE_IPV6" != "1" ] && {
create_ipset 6 $ZIPSET6 hash:net "$IPSET_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipset 6 $ZIPSET_IPBAN6 hash:net "$IPSET_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipset 6 $ZIPSET_EXCLUDE6 hash:net "$IPSET_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
}
true
;;
nftables)
nft_create_table && {
SAVERAM=0
RAMSIZE=$($GREP MemTotal /proc/meminfo | $AWK '{print $2}')
[ "$RAMSIZE" -lt "420000" ] && SAVERAM=1
print_reloading_backend "nftables set"
[ "$DISABLE_IPV4" != "1" ] && {
create_nfset 4 $ZIPSET $SET_MAXELEM "$ZIPLIST" "$ZIPLIST_USER"
create_nfset 4 $ZIPSET_IPBAN $SET_MAXELEM "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_nfset 4 $ZIPSET_EXCLUDE $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE"
}
[ "$DISABLE_IPV6" != "1" ] && {
create_nfset 6 $ZIPSET6 $SET_MAXELEM "$ZIPLIST6" "$ZIPLIST_USER6"
create_nfset 6 $ZIPSET_IPBAN6 $SET_MAXELEM "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_nfset 6 $ZIPSET_EXCLUDE6 $SET_MAXELEM_EXCLUDE "$ZIPLIST_EXCLUDE6"
}
true
}
;;
ipfw)
print_reloading_backend "ipfw table"
if [ "$DISABLE_IPV4" != "1" ] && [ "$DISABLE_IPV6" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE" "$ZIPLIST_EXCLUDE6"
elif [ "$DISABLE_IPV4" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST" "$ZIPLIST_USER"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN" "$ZIPLIST_USER_IPBAN"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE"
elif [ "$DISABLE_IPV6" != "1" ]; then
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT" "$ZIPLIST6" "$ZIPLIST_USER6"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT" "$ZIPLIST_IPBAN6" "$ZIPLIST_USER_IPBAN6"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE" "$ZIPLIST_EXCLUDE6"
else
create_ipfw_table $ZIPSET "$IPFW_TABLE_OPT"
create_ipfw_table $ZIPSET_IPBAN "$IPFW_TABLE_OPT"
create_ipfw_table $ZIPSET_EXCLUDE "$IPFW_TABLE_OPT_EXCLUDE"
fi
true
;;
*)
echo no supported ip list backend found
true
;;
esac
fi