Last active
July 21, 2024 13:07
-
-
Save levid0s/2cb9f64234aab3b3a400b57d5d92f840 to your computer and use it in GitHub Desktop.
OpenWRT DNS Watch
This file contains 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/ash | |
# https://gist.github.com/levid0s/2cb9f64234aab3b3a400b57d5d92f840 | |
set -eu | |
usage=" | |
Suggested crontab: | |
*/5 * * * * /root/dns_watch.sh 2>> /tmp/dns_watch.log | |
" | |
PREFERRED_DNS="192.168.1.2" # eg. Adguard Home | |
FALLBACK_DNS="8.8.8.8" | |
ROUTER_IP=$(ip addr show br-lan | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1) | |
TEST_QUERY="www.example.com" | |
RESOLVER="/usr/bin/nslookup" | |
CHECK_INTERVAL=20 | |
FALLBACK_THRESHOLD="${1-120}" # $1 or default | |
PREFERRED_THRESHOLD="${1-120}" # $1 or default | |
: ${debug:=false} | |
[ "$debug" = 1 ] && debug=true | |
command -v $debug >/dev/null || { echo "ERROR: Incorrect value for \$debug" >&2; exit 1; } | |
echo "$ROUTER_IP" | grep -qE '^((10\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(192\.168\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])))$' || { echo "ERROR: Error fetching the router's LAN IP: $ROUTER_IP" >&2; exit 1; } | |
check_preferred_dns_alive() { | |
$RESOLVER $TEST_QUERY $PREFERRED_DNS >/dev/null 2>&1 | |
} | |
update_to_failover() { | |
dnsfw_base=$(uci get dhcp.@dnsmasq[0].server | sed "s~\s\?$PREFERRED_DNS~~g") | |
dnsfw_new="$dnsfw_base $FALLBACK_DNS" | |
$debug && echo "DEBUG: uci set dhcp.@dnsmasq[0].server=\"$dnsfw_new\"" >&2 | |
$debug || uci set dhcp.@dnsmasq[0].server="$dnsfw_new" | |
dhcp_base=$(uci get dhcp.lan.dhcp_option | sed "s~\s\?6,$PREFERRED_DNS~~g") | |
dhcp_new="$dhcp_base 6,$ROUTER_IP" | |
$debug && echo "DEBUG: uci set dhcp.lan.dhcp_option=\"$dhcp_new\"" >&2 | |
$debug || uci set dhcp.lan.dhcp_option="$dhcp_new" | |
$debug && echo "DEBUG: restarting dnsmasq.." >&2 | |
$debug || /etc/init.d/dnsmasq restart | |
echo "INFO: DNS FAILED OVER to: $ROUTER_IP -> $FALLBACK_DNS" >&2 | |
} | |
update_to_preferred() { | |
dnsfw_base=$(uci get dhcp.@dnsmasq[0].server | sed "s~\s\?$FALLBACK_DNS~~g") | |
dnsfw_new="$dnsfw_base $PREFERRED_DNS" | |
$debug && echo "DEBUG: uci set dhcp.@dnsmasq[0].server="$dnsfw_new"" >&2 | |
$debug || uci set dhcp.@dnsmasq[0].server="$dnsfw_new" | |
dhcp_base=$(uci get dhcp.lan.dhcp_option | sed "s~\s\?6,$ROUTER_IP~~g") | |
dhcp_new="$dhcp_base 6,$PREFERRED_DNS" | |
$debug && echo "DEBUG: uci set dhcp.lan.dhcp_option="$dhcp_new"" >&2 | |
$debug || uci set dhcp.lan.dhcp_option="$dhcp_new" | |
$debug || /etc/init.d/dnsmasq restart | |
echo "INFO: DNS RESTORED to: $PREFERRED_DNS" >&2 | |
} | |
wait_for_dns() { | |
local target_status="$1" duration="$2" | |
local elapsed=0 last_status | |
$debug && echo "DEBUG: Waiting ${duration}s for preferred_dns_status to keep the status $target_status" >&2 | |
while [ $elapsed -lt $duration ]; do | |
check_preferred_dns_alive && last_status=0 || last_status=1 | |
$debug && echo "DEBUG: check_preferred_dns_alive status: $last_status" >&2 | |
if [ "$last_status" -ne "$target_status" ]; then | |
echo "WARN: Got DNS server status: $last_status, cancelling operation." >&2 | |
exit 0 | |
fi | |
sleep $CHECK_INTERVAL | |
elapsed=$((elapsed + CHECK_INTERVAL)) | |
done | |
} | |
get_current_mode() { | |
if uci get dhcp.@dnsmasq[0].server | grep -qE "[^/]$PREFERRED_DNS"; then | |
echo PREFERRED | |
elif uci get dhcp.@dnsmasq[0].server | grep -qE "[^/]$FALLBACK_DNS"; then | |
echo FALLBACK | |
else | |
echo "ERROR: Error determining fallback mode" >&2 | |
exit 1 | |
fi | |
} | |
CURRENT_MODE=$(get_current_mode) | |
DESIRED_MODE=$(check_preferred_dns_alive && echo "PREFERRED" || echo "FALLBACK") | |
if $debug; then | |
echo -e "CURRENT=$CURRENT_MODE\nDESIRED=$DESIRED_MODE" >&2 | |
echo "uci get dhcp.@dnsmasq[0].server=$(uci get dhcp.@dnsmasq[0].server)" >&2 | |
echo "uci get dhcp.lan.dhcp_option=$(uci get dhcp.lan.dhcp_option)" >&2 | |
fi | |
if [ "$CURRENT_MODE" = "$DESIRED_MODE" ]; then | |
$debug && echo "DEBUG: Current mode already matches desired mode, exiting. ($CURRENT_MODE)" >&2 | |
exit 0 | |
fi | |
if [ "$DESIRED_MODE" = FALLBACK ]; then | |
echo "INFO: DNS server unreachable: $PREFERRED_DNS, waiting ${FALLBACK_THRESHOLD}s before failover.." >&2 | |
wait_for_dns 1 $FALLBACK_THRESHOLD && update_to_failover | |
exit 0 | |
fi | |
if [ "$DESIRED_MODE" = PREFERRED ]; then | |
echo "INFO: DNS server back online: $PREFERRED_DNS, waiting ${PREFERRED_THRESHOLD}s before restore.." >&2 | |
wait_for_dns 0 $PREFERRED_THRESHOLD && update_to_preferred | |
exit 0 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment