215 lines
5.7 KiB
Bash
Executable File
215 lines
5.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#
|
||
# blokir.sh – manage IPv4 prefixes in the “blocked_ips” LPM-trie map
|
||
#
|
||
# sudo ./blokir.sh # help
|
||
# sudo ./blokir.sh 1.2.3.4/24 … # add
|
||
# sudo ./blokir.sh unblock … # delete exact prefix(es)
|
||
# sudo ./blokir.sh reset # flush whole map
|
||
# sudo ./blokir.sh export [f] # print or save list
|
||
#
|
||
set -euo pipefail
|
||
|
||
###############################################################################
|
||
# 1. Locate the map automatically
|
||
###############################################################################
|
||
MAP_NAME=${MAP_NAME:-blocked_ips} # change if you used another name
|
||
detect_map_id() {
|
||
local id=""
|
||
if command -v jq >/dev/null; then
|
||
id=$(bpftool -j map show | jq -r --arg n "$MAP_NAME" \
|
||
'.[]|select(.name==$n)|.id' | head -n1)
|
||
fi
|
||
if [[ -z $id ]]; then # plain-text fallback
|
||
id=$(bpftool -p map show |
|
||
awk -v n="$MAP_NAME" '$4==n{split($1,a,":");print a[1];exit}')
|
||
fi
|
||
[[ -z $id ]] && {
|
||
echo "‼︎ map \"$MAP_NAME\" not found." >&2
|
||
exit 1
|
||
}
|
||
echo "$id"
|
||
}
|
||
MAP_ID=$(detect_map_id)
|
||
|
||
###############################################################################
|
||
# 2. Helpers
|
||
###############################################################################
|
||
usage() {
|
||
cat <<EOF
|
||
Usage:
|
||
$0 reset
|
||
$0 unblock <ip|cidr> [..]
|
||
$0 import [file]
|
||
$0 export [file]
|
||
$0 <ip|cidr> [..] (add)
|
||
$0 show
|
||
$0 (help)
|
||
EOF
|
||
}
|
||
|
||
# dotted IP + plen → key-bytes "xx xx …"
|
||
gen_key_bytes() {
|
||
local ip=$1 plen=$2
|
||
IFS=. read -r o1 o2 o3 o4 <<<"$ip"
|
||
printf '%02x %02x %02x %02x %02x %02x %02x %02x' \
|
||
$((plen & 255)) 0 0 0 "$o1" "$o2" "$o3" "$o4"
|
||
}
|
||
|
||
mask_ip() { # → network/0-host bits
|
||
local ip=$1 plen=$2
|
||
IFS=. read -r a b c d <<<"$ip"
|
||
local v=$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d))
|
||
local m=$((plen == 0 ? 0 : (0xFFFFFFFF << (32 - plen)) & 0xFFFFFFFF))
|
||
v=$((v & m))
|
||
printf '%d.%d.%d.%d' $((v >> 24 & 255)) $((v >> 16 & 255)) $((v >> 8 & 255)) $((v & 255))
|
||
}
|
||
|
||
import_prefixes() {
|
||
local file="$1"
|
||
[[ -r "$file" ]] || {
|
||
echo "‼︎ Cannot read file: $file" >&2
|
||
exit 1
|
||
}
|
||
echo "📥 Importing from $file…"
|
||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||
[[ -z "$line" || "$line" =~ ^# ]] && continue # skip empty lines and comments
|
||
insert_prefix "$line"
|
||
done <"$file"
|
||
show_map
|
||
}
|
||
|
||
parse_cidr() { # validate → "ip plen"
|
||
local cidr="$1" ip plen
|
||
ip=${cidr%%/*}
|
||
plen=${cidr#*/}
|
||
[[ $ip == $plen ]] && plen=32
|
||
[[ $plen =~ ^[0-9]+$ && plen -le 32 ]] || return 1
|
||
if ! [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then return 1; fi
|
||
IFS=. read -r a b c d <<<"$ip"
|
||
for o in $a $b $c $d; do ((o > 255)) && return 1; done
|
||
echo "$ip $plen"
|
||
}
|
||
|
||
###############################################################################
|
||
# 3. Map-byte helpers (used by add/del/export)
|
||
###############################################################################
|
||
list_keys() { # stdout: "xx xx …" (8-byte key)
|
||
if command -v jq >/dev/null; then
|
||
bpftool -j map dump id "$MAP_ID" |
|
||
jq -r '.[]|.key|join(" ")'
|
||
else
|
||
bpftool -p map dump id "$MAP_ID" |
|
||
awk '/^key:/{for(i=2;i<=NF;i++)printf $i" ";print ""}'
|
||
fi
|
||
}
|
||
|
||
bytes_to_prefix() { # arg: "xx xx …" → "IP/len"
|
||
read -ra b <<<"$1" # eight bytes
|
||
local plen=$((16#${b[0]#0x})) # byte-0 = prefix length
|
||
local o1=$((16#${b[4]#0x}))
|
||
local o2=$((16#${b[5]#0x}))
|
||
local o3=$((16#${b[6]#0x}))
|
||
local o4=$((16#${b[7]#0x}))
|
||
echo "$o1.$o2.$o3.$o4/$plen"
|
||
}
|
||
|
||
###############################################################################
|
||
# 4. Core operations
|
||
###############################################################################
|
||
insert_prefix() {
|
||
local parsed
|
||
parsed=$(parse_cidr "$1") || {
|
||
echo "‼︎ Bad CIDR: $1" >&2
|
||
return 1
|
||
}
|
||
set -- $parsed
|
||
local ip=$1 plen=$2
|
||
local net=$(mask_ip "$ip" "$plen")
|
||
local k=$(gen_key_bytes "$net" "$plen")
|
||
echo "➕ $net/$plen"
|
||
bpftool map update id "$MAP_ID" key hex $k value hex 01
|
||
}
|
||
|
||
delete_prefix() {
|
||
local parsed
|
||
parsed=$(parse_cidr "$1") || {
|
||
echo "‼︎ Bad CIDR: $1" >&2
|
||
return 1
|
||
}
|
||
set -- $parsed
|
||
local ip=$1 plen=$2
|
||
local net=$(mask_ip "$ip" "$plen")
|
||
local k=$(gen_key_bytes "$net" "$plen")
|
||
echo "➖ $net/$plen"
|
||
bpftool map delete id "$MAP_ID" key hex $k 2>/dev/null || echo " (not present)"
|
||
}
|
||
|
||
flush_map() {
|
||
echo "🗑 Flushing…"
|
||
list_keys | while read -r k; do bpftool map delete id "$MAP_ID" key hex $k 2>/dev/null || true; done
|
||
}
|
||
|
||
export_map() { # [outfile]
|
||
local out=${1:-}
|
||
local lines=""
|
||
while read -r k; do
|
||
[[ -z $k ]] && continue
|
||
lines+=$(bytes_to_prefix "$k")$'\n'
|
||
done < <(list_keys)
|
||
if [[ -n $out ]]; then
|
||
printf '%s' "$lines" >"$out"
|
||
echo "📤 List written to $out"
|
||
else printf '%s' "$lines"; fi
|
||
}
|
||
|
||
show_map() {
|
||
echo -e "\n📋 Current prefixes:"
|
||
export_map
|
||
echo -e "\n"
|
||
}
|
||
|
||
###############################################################################
|
||
# 5. CLI
|
||
###############################################################################
|
||
(($# == 0)) && {
|
||
usage
|
||
exit 0
|
||
}
|
||
|
||
case "$1" in
|
||
reset)
|
||
flush_map
|
||
show_map
|
||
;;
|
||
unblock)
|
||
shift
|
||
(($# == 0)) && {
|
||
usage
|
||
exit 1
|
||
}
|
||
for p in "$@"; do delete_prefix "$p"; done
|
||
show_map
|
||
;;
|
||
import)
|
||
shift
|
||
[[ $# -ne 1 ]] && {
|
||
usage
|
||
exit 1
|
||
}
|
||
import_prefixes "$1"
|
||
;;
|
||
export)
|
||
shift
|
||
export_map "${1:-}"
|
||
;;
|
||
show)
|
||
show_map
|
||
;;
|
||
-h | --help | help) usage ;;
|
||
*)
|
||
for p in "$@"; do insert_prefix "$p"; done
|
||
show_map
|
||
;;
|
||
esac
|