-
-
Save m7mdcc/7dea93ac01a6d1611c86b4ab6c392265 to your computer and use it in GitHub Desktop.
/etc/init.d/nft2ipset: An OPTIMIZED version for nftables set to ipset synchronizer for use with OpenWRT/mwan3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # nft -> ipset sync for MWAN3 (with logfile + daemon support) | |
| set -u | |
| SCRIPT="$(basename "$0")" | |
| TMPDIR="/tmp" | |
| LOGFILE="/tmp/nft2ipset.log" | |
| DAEMON=0 | |
| # --- parse args --- | |
| while [ $# -gt 0 ]; do | |
| case "$1" in | |
| --log) shift; LOGFILE="$1" ;; | |
| --daemon) DAEMON=1 ;; | |
| esac | |
| shift || break | |
| done | |
| log() { | |
| local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*" | |
| echo "$msg" >>"$LOGFILE" | |
| logger -t "$SCRIPT" -- "$*" | |
| } | |
| log "==== nft2ipset started (PID $$, daemon=$DAEMON) ====" | |
| # --- background if daemon mode --- | |
| if [ "$DAEMON" = "1" ]; then | |
| # if launched via procd, fork to background so it doesn't hang | |
| ( | |
| setsid "$0" --log "$LOGFILE" </dev/null &>/dev/null & | |
| ) & | |
| exit 0 | |
| fi | |
| PID=$$ | |
| RAND="${RANDOM:-$(date +%s)}" | |
| MONITORFIFO="$TMPDIR/${SCRIPT}.${PID}.${RAND}.fifo" | |
| MONITORPIDFILE="$TMPDIR/${SCRIPT}.${PID}.${RAND}.pid" | |
| cleanup() { | |
| log "cleanup() called" | |
| if [ -f "$MONITORPIDFILE" ]; then | |
| MONPID="$(cat "$MONITORPIDFILE" 2>/dev/null || echo "")" | |
| [ -n "$MONPID" ] && kill "$MONPID" 2>/dev/null && log "Killed monitor PID $MONPID" | |
| fi | |
| rm -f "$MONITORFIFO" "$MONITORPIDFILE" | |
| log "cleanup done" | |
| } | |
| trap cleanup TERM INT EXIT | |
| # --- collect mwan sets --- | |
| SET_NAMES_REGEX="$( | |
| uci -q export mwan3 | | |
| awk ' | |
| $1=="option" && $2=="ipset" { | |
| gsub(/^[ \t'\"']+|[ \t'\"']+$/, "", $3); | |
| if($3!="") names[$3]=1 | |
| } | |
| END{ i=0; for(n in names){ if(i++) printf("|"); printf("%s", n) } } | |
| ' | |
| )" | |
| if [ -z "$SET_NAMES_REGEX" ]; then | |
| log "no mwan3 ipset definitions found -> exit" | |
| exit 0 | |
| fi | |
| log "Detected MWAN3 sets: $SET_NAMES_REGEX" | |
| is_mwan_set() { echo "$1" | grep -Eq "^(${SET_NAMES_REGEX})$"; } | |
| parse_set_family() { echo "$1" | grep -q "ipv6_addr" && echo inet6 || echo inet; } | |
| parse_set_timeout() { echo "$1" | sed -n -E 's/.* timeout ([0-9]+)s.*/\1/p' | head -n1; } | |
| extract_elements() { | |
| echo "$1" | sed -E 's/.*elements = \{ ([^}]+) \}.*/\1/;t; s/.*/ /' | | |
| tr ',' '\n' | sed -E 's/^[[:space:]]+//; s/expires/timeout/g; s/s$//g' | |
| } | |
| ensure_ipset() { | |
| NAME="$1"; FAMILY="$2"; T="${3:-0}" | |
| OPTS=""; [ "$FAMILY" = inet6 ] && OPTS="$OPTS family inet6" | |
| [ "$T" -gt 0 ] 2>/dev/null && OPTS="$OPTS timeout $T" | |
| if ipset -q list "$NAME" >/dev/null 2>&1; then | |
| CUR="$(ipset list "$NAME" 2>/dev/null || true)" | |
| if ! echo "$CUR" | grep -q "family $FAMILY"; then | |
| ipset destroy "$NAME" 2>/dev/null | |
| ipset create "$NAME" hash:ip $OPTS | |
| log "recreated $NAME due to family change" | |
| elif [ "$T" -gt 0 ] && ! echo "$CUR" | grep -q "timeout $T"; then | |
| ipset create "_$NAME" hash:ip $OPTS && | |
| ipset swap "_$NAME" "$NAME" && | |
| ipset destroy "_$NAME" | |
| log "rebuilt $NAME with timeout $T" | |
| fi | |
| else | |
| ipset create "$NAME" hash:ip $OPTS | |
| log "created ipset $NAME ($FAMILY) timeout=$T" | |
| fi | |
| } | |
| seed_ipset_from_def() { | |
| DEF="$1" | |
| NAME="$(echo "$DEF" | awk '{print $2}')" | |
| FAMILY="$(parse_set_family "$DEF")" | |
| TIMEOUT="$(parse_set_timeout "$DEF")"; TIMEOUT="${TIMEOUT:-0}" | |
| ensure_ipset "$NAME" "$FAMILY" "$TIMEOUT" | |
| ELEMS="$(extract_elements "$DEF")" | |
| if [ -n "$ELEMS" ]; then | |
| echo "$ELEMS" | while read -r L; do | |
| [ -n "$L" ] && ipset -q add "$NAME" $L | |
| done | |
| log "seeded elements into $NAME" | |
| fi | |
| } | |
| # --- initial sync --- | |
| log "starting initial sync" | |
| nft -nT list sets 2>/dev/null | | |
| tr '\n' ' ' | awk '{$1=$1;print}' | | |
| sed -E 's/(set|table)/\n\1/g' | | |
| grep -E "^set (${SET_NAMES_REGEX}) " | | |
| while read -r DEF; do seed_ipset_from_def "$DEF"; done | |
| log "initial sync complete" | |
| # --- monitor setup --- | |
| mkfifo "$MONITORFIFO" | |
| (nft -nT monitor >"$MONITORFIFO" 2>&1) & | |
| echo $! > "$MONITORPIDFILE" | |
| log "spawned monitor PID $(cat "$MONITORPIDFILE")" | |
| # --- main event loop --- | |
| while read -r LINE; do | |
| case "$LINE" in | |
| *"add element inet fw4 "*) | |
| NAME="$(echo "$LINE" | awk '{print $5}')" | |
| is_mwan_set "$NAME" || continue | |
| IP="$(echo "$LINE" | awk '{print $7}')" | |
| EXPIRES="$(echo "$LINE" | sed -n -E 's/.*expires ([0-9]+)s.*/\1/p' | head -n1)" | |
| [ -n "$EXPIRES" ] && ADDOPTS="timeout $EXPIRES" || ADDOPTS="" | |
| ipset -q del "$NAME" "$IP" | |
| ipset -q add "$NAME" "$IP" $ADDOPTS | |
| log "add $IP to $NAME ($ADDOPTS)" | |
| ;; | |
| *"add set inet fw4 "*) | |
| NAME="$(echo "$LINE" | awk '{print $5}')" | |
| is_mwan_set "$NAME" || continue | |
| DEF="$(nft -nT list sets 2>/dev/null | | |
| tr '\n' ' ' | awk '{$1=$1;print}' | | |
| sed -E 's/(set|table)/\n\1/g' | | |
| grep -E "^set ${NAME} ")" | |
| [ -n "$DEF" ] && seed_ipset_from_def "$DEF" | |
| ;; | |
| *"delete set inet fw4 "*) | |
| NAME="$(echo "$LINE" | awk '{print $5}')" | |
| is_mwan_set "$NAME" || continue | |
| ipset -q clear "$NAME" | |
| ipset -q destroy "$NAME" | |
| log "deleted ipset $NAME" | |
| ;; | |
| esac | |
| done < "$MONITORFIFO" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment