Quote from: franco on December 02, 2025, 11:20:09 AMHi allddd,
Nice work on this! If you want we can work on including this in a future release as an optional binary package and see how it goes from there?
Cheers,
Franco
#!/bin/sh
# dnsmasq_omada_sync.sh
# Sync dnsmasq hostnames -> Omada Controller.
# Skips Omada login if cache shows no changes.
# To create a cron job for this file in OPNsense:
# 1. Copy this file in /root/scripts
# 2. Create the file /usr/local/opnsense/service/conf/actions.d/actions_omada.conf
# 3. Append the following lines to the file:
# [start]
# command:/root/scripts/dnsmasq_omada_sync.sh
# parameters:
# type: script
# message: Synchronize dnsmasq hostnames with Omada
# description: Synchronize dnsmasq hostnames with Omada
# 4. Restart configd: service configd restart
# 5. Create the cron job in "System - Settings - Cron"
# --------------------------------
# cyruz - https://ciroprincipe.net
set -e
### ==============================
### CONFIGURATION
### ==============================
OMADA_URL="https://omada_ip:8043"
OMADAC_ID="" # Omada Controller ID
SITE_ID="" # Omada Site ID
CLIENT_ID="" # Open API Client ID
CLIENT_SECRET="" # Open API Client Secret
LEASE_FILE="/var/db/dnsmasq.leases"
LEASE_CACHE="/var/db/dnsmasq_omada.cache"
LOG_FILE="/var/log/dnsmasq_omada_sync.log"
MAX_LOG_SIZE=$((1024 * 1024))
#######################################
# REQUIREMENTS CHECK
#######################################
require_cmd() {
for cmd in "$@"; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "Error: required command missing: $cmd" >&2
exit 1
}
done
}
require_cmd curl jq awk date sort mktemp tr sed wc stat mkdir
#######################################
# LOG ROTATION
#######################################
rotate_logs() {
log_dir=$(dirname "$LOG_FILE")
[ -d "$log_dir" ] || mkdir -p "$log_dir"
if [ -f "$LOG_FILE" ]; then
size=$(stat -f %z "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$size" -ge "$MAX_LOG_SIZE" ]; then
[ -f "${LOG_FILE}.1" ] && rm -f "${LOG_FILE}.1"
mv "$LOG_FILE" "${LOG_FILE}.1"
fi
fi
}
rotate_logs
exec >>"$LOG_FILE" 2>&1
if [ ! -r "$LEASE_FILE" ]; then
echo "$(date -Iseconds) Error: dnsmasq lease file not readable: $LEASE_FILE"
exit 1
fi
echo "===== $(date -Iseconds) - dnsmasq_omada_sync start ====="
#######################################
# STEP 1: Parse dnsmasq leases
#######################################
echo "[*] Reading dnsmasq leases..."
NOW_EPOCH=$(date +%s)
TMP_LEASES=$(mktemp -t dnsmasq.XXXXXX)
TMP_NEW_HOST=$(mktemp -t newhost.XXXXXX)
cleanup() {
rm -f "$TMP_LEASES" "$TMP_NEW_HOST"
}
trap cleanup EXIT
# Keep only valid (non-expired, hostname present) leases, last entry per MAC.
awk -v now="$NOW_EPOCH" '
now <= $1 && $4 != "" && $4 != "*" {
line[tolower($2)] = $0
}
END {
for (m in line) print line[m]
}
' "$LEASE_FILE" > "$TMP_LEASES"
# Build NEW_HOST list: "mac hostname".
# dnsmasq format: expiry mac ip hostname clientid.
while read -r expiry mac ip host cid; do
[ -z "$mac" ] && continue
mac_lc=$(printf "%s\n" "$mac" | tr "A-Z" "a-z")
printf "%s %s\n" "$mac_lc" "$host"
done < "$TMP_LEASES" > "$TMP_NEW_HOST"
#######################################
# STEP 2: Compare with cache BEFORE API
#######################################
echo "[*] Loading previous cache (if any) and comparing..."
CHANGED=0
if [ -f "$LEASE_CACHE" ]; then
while read -r mac new_host; do
[ -z "$mac" ] && continue
old_host=$(awk -v m="$mac" 'tolower($1)==m {print $2; exit}' "$LEASE_CACHE" 2>/dev/null || true)
if [ -z "$old_host" ] || [ "$old_host" != "$new_host" ]; then
CHANGED=$((CHANGED + 1))
fi
done < "$TMP_NEW_HOST"
else
# No cache yet: everything is considered "changed".
CHANGED=$(wc -l < "$TMP_NEW_HOST" | awk '{print $1}')
fi
if [ "$CHANGED" -eq 0 ]; then
echo "[*] No hostname updates detected — exiting without Omada API calls."
echo "===== $(date -Iseconds) - dnsmasq_omada_sync end (no changes) ====="
exit 0
fi
echo "[*] Detected $CHANGED hostname changes — requesting access token..."
#######################################
# STEP 3: Get access token (client_credentials)
#######################################
TOKEN=$(
curl -sk -X POST \
"$OMADA_URL/openapi/authorize/token?grant_type=client_credentials" \
-H "content-type:application/json" \
-d '{
"omadacId": "'"$OMADAC_ID"'",
"client_id": "'"$CLIENT_ID"'",
"client_secret": "'"$CLIENT_SECRET"'"
}' \
| jq -r '.result.accessToken'
)
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "Error: failed to obtain access token from Omada OpenAPI"
echo "===== $(date -Iseconds) - dnsmasq_omada_sync end (token error) ====="
exit 1
fi
#######################################
# STEP 4: Apply hostname updates
#######################################
echo "[*] Applying hostname updates via OpenAPI..."
UPDATED=0
MISSING=0
FAILED=0
while read -r mac new_host; do
[ -z "$mac" ] && continue
# Load old host from cache (if any).
if [ -f "$LEASE_CACHE" ]; then
old_host=$(awk -v m="$mac" 'tolower($1)==m {print $2; exit}' "$LEASE_CACHE" 2>/dev/null || true)
else
old_host=""
fi
# Only process entries that changed vs cache.
if [ -n "$old_host" ] && [ "$old_host" = "$new_host" ]; then
continue
fi
# Build clientMac as required by OpenAPI path: upper-case with dashes.
mac_id=$(printf "%s\n" "$mac" | tr 'a-f' 'A-F' | tr ':' '-')
# Sanitize hostname for JSON (escape double quotes).
safe_host=$(printf "%s" "$new_host" | sed 's/"/\\"/g')
echo "[*] Setting name for $mac_id -> $safe_host"
RESP=$(curl -sk -X PATCH \
"$OMADA_URL/openapi/v1/$OMADAC_ID/sites/$SITE_ID/clients/$mac_id/name" \
-H "content-type:application/json" \
-H "Authorization:AccessToken=$TOKEN" \
-d "{\"name\":\"$safe_host\"}" )
ERR=$(printf "%s" "$RESP" | jq -r '.errorCode' 2>/dev/null || echo "unknown")
if [ "$ERR" = "0" ]; then
UPDATED=$((UPDATED + 1))
elif [ "$ERR" = "-41011" ]; then
echo "[!] Client $mac_id does not exist in this site (errorCode -41011)."
MISSING=$((MISSING + 1))
else
echo "[!] Failed to update $mac_id (errorCode $ERR)."
FAILED=$((FAILED + 1))
fi
done < "$TMP_NEW_HOST"
echo "[*] Updated clients: $UPDATED"
echo "[*] Missing in Omada: $MISSING"
echo "[*] Failed updates: $FAILED"
#######################################
# STEP 5: Rewrite cache
#######################################
echo "[*] Writing updated cache..."
sort "$TMP_NEW_HOST" > "$LEASE_CACHE.tmp"
mv "$LEASE_CACHE.tmp" "$LEASE_CACHE"
echo "===== $(date -Iseconds) - dnsmasq_omada_sync end ====="Quote from: cookiemonster on December 02, 2025, 03:42:31 PMAlso, rookie question but I'll ask. Do zenarmor / crowdsec interfere when running the bufferbloat tests?Not directly and not by intent. This goes around to the CPU bottleneck, if your CPU can not keep up, you will see a latency introduced by the CPU processing of the packets. For example I have ZA on N100, and there is no problem to handle 500+ throughput on WAN with shaping enabled.
Quote from: cookiemonster on December 02, 2025, 03:42:31 PMAnd to clarify. Can I/should I reset as per docs on my 25.1.12 version ? Suggested testing method ?Docs are valid for any OPNsense version.