I made a little script, that will allow one openwrt host
To have a list of other dhcp servers, to query them, and to turn itself on or off based on the verified functioning of the other "higher priority" dhcp servers
Here is the script itself
/usr/bin/dhcp-interface-watchdog.sh
#!/bin/bash
# Max allowed log file size in KB
LOG_MAX_SIZE=100
# Whether to ignore loopback interface
IGNORE_LOOPBACK=true
# Host identity
SELF_HOSTNAME=$(hostname -f 2>/dev/null || hostname)
DNSMASQ_SERVICE="/etc/init.d/dnsmasq"
# Read global log configuration
WATCHDOG_LOG=$(uci -q get dhcp.watchdoglog)
# Declare maps for storing state
declare -A INTERFACE_IPS
# Write a log message to syslog or file depending on WATCHDOG_LOG
# Usage: log <interface> <message>
log() {
local iface="$1"
shift
local msg="[$iface] $*"
# Logging is disabled
if [ "$WATCHDOG_LOG" = "0" ]; then
return
fi
# Log to syslog if no value set
if [ -z "$WATCHDOG_LOG" ]; then
logger -t "dhcp-watchdog" "$msg"
else
if [ -f "$WATCHDOG_LOG" ]; then
local filesize
filesize=$(du -k "$WATCHDOG_LOG" | cut -f1)
if [ "$filesize" -ge "$LOG_MAX_SIZE" ]; then
: > "$WATCHDOG_LOG"
fi
fi
echo "$(date +'%Y-%m-%d %H:%M:%S') $msg" >> "$WATCHDOG_LOG"
fi
}
# Returns a list of UCI interface names (e.g., lan, wan)
get_all_interfaces() {
uci show network | grep '=interface' | cut -d. -f2 | cut -d= -f1
}
# Populates INTERFACE_IPS[] with IP addresses of physical interfaces
get_interface_ip_map() {
for iface in $(ls /sys/class/net); do
[ "$iface" = "lo" ] && continue
ip=$(ip -4 addr show dev "$iface" | awk '/inet / {print $2}' | cut -d/ -f1)
[ -n "$ip" ] && INTERFACE_IPS["$iface"]=$ip
done
}
# Returns IP address for UCI interface name
get_ip_for_interface() {
local net="$1"
local ifname
ifname=$(uci -q get network."$net".ifname)
echo "${INTERFACE_IPS[$ifname]}"
}
# Returns array of DHCP priority peers for given interface
get_priority_list_for_interface() {
local net="$1"
uci -q get dhcp."$net".serverpriority | sed "s/'//g" | xargs -n1 2>/dev/null
}
# Returns true if serverwatchdog is enabled for given interface
is_watchdog_enabled_for_interface() {
local net="$1"
[ "$(uci -q get dhcp."$net".serverwatchdog)" = "1" ]
}
# Resolves host or IP string to IP
resolve_to_ip() {
local name="$1"
getent hosts "$name" | awk '{print $1}'
}
# Returns true if value is our hostname or one of our IPs
is_self_ip_or_hostname() {
local value="$1"
[[ "$value" == "$SELF_HOSTNAME" ]] && return 0
for ip in "${INTERFACE_IPS[@]}"; do
[[ "$value" == "$ip" ]] && return 0
done
return 1
}
# Sends dummy DHCP request to a peer to test if it's active
send_dhcp_probe() {
local target_ip="$1"
local source_ip="$2"
timeout 2 dhcping -s "$target_ip" -r -c "$source_ip" -h "de:ad:be:ef:00:01" >/dev/null 2>&1
}
# Enables or disables DHCP server for a given interface
# Usage: dhcp_service <enable|disable> <interface>
dhcp_service() {
local action="$1"
local net="$2"
local current_ignore
current_ignore=$(uci -q get dhcp."$net".ignore)
if [ "$action" = "enable" ]; then
if [ "$current_ignore" = "1" ]; then
log "$net" "Enabling DHCP server on $net"
uci del dhcp."$net".ignore
uci commit dhcp
$DNSMASQ_SERVICE restart
else
log "$net" "DHCP already enabled on $net"
fi
elif [ "$action" = "disable" ]; then
if [ "$current_ignore" != "1" ]; then
log "$net" "Disabling DHCP server on $net"
uci set dhcp."$net".ignore='1'
uci commit dhcp
$DNSMASQ_SERVICE restart
else
log "$net" "DHCP already disabled on $net"
fi
fi
}
# Main per-interface watchdog logic
# Usage: run_watchdog_for_interface <interface>
run_watchdog_for_interface() {
local net="$1"
# STEP 1: Get the local IP address
local local_ip
local_ip=$(get_ip_for_interface "$net")
if [ -z "$local_ip" ]; then
log "$net" "Could not determine IP address"
return
fi
# STEP 2: Fetch the priority list from UCI
local peer_found=false
local priority_list=()
mapfile -t priority_list < <(get_priority_list_for_interface "$net")
# STEP 3: Check each server in the priority list
for entry in "${priority_list[@]}"; do
local resolved
resolved=$(resolve_to_ip "$entry")
if [ -z "$resolved" ]; then
log "$net" "Could not resolve $entry"
continue
fi
# STEP 3a: If this is us, we take responsibility
if is_self_ip_or_hostname "$entry" || is_self_ip_or_hostname "$resolved"; then
log "$net" "Reached self in priority list ($entry); assuming DHCP responsibility"
break
fi
# STEP 3b: Try probing for a DHCP response
log "$net" "Probing $resolved for DHCP..."
if send_dhcp_probe "$resolved" "$local_ip"; then
log "$net" "DHCP server is alive at $resolved"
dhcp_service disable "$net"
peer_found=true
break
else
log "$net" "No DHCP response from $resolved"
fi
done
# STEP 4: If no peer found, we enable our own DHCP server
if ! $peer_found; then
dhcp_service enable "$net"
fi
}
# Script entry point
main() {
get_interface_ip_map
for net in $(get_all_interfaces); do
[ "$IGNORE_LOOPBACK" = true ] && [ "$net" = "loopback" ] && continue
is_watchdog_enabled_for_interface "$net" || continue
run_watchdog_for_interface "$net"
done
}
main
exit 0
For it to do anything, you have to set the following uci keys in your network.interface
serverwatchdog, controls if the watchdog is enabled for this interface
serverpriority, a list of dhcp server by priority
dhcp.watchdoglog, controls watchdog log method (system, off or file)
By default, it logs to system (logger) log
# Log to file (will truncate if > 100 KB)
uci set dhcp.watchdoglog='/tmp/dhcp.watchdog.log'
# Disable all logging (completely silent)
uci set dhcp.watchdoglog=0
# Use system log (default behavior if unset)
uci delete dhcp.watchdoglog
Next enable the dhcp watchdog script per interface, example
# Enable watchdog on LAN
uci set dhcp.lan.serverwatchdog=1
uci add_list dhcp.lan.serverpriority=192.168.1.1
uci add_list dhcp.lan.serverpriority=192.168.1.2
uci commit dhcp
# Enable watchdog on WAN (only if you need DHCP there, usually you don’t)
uci set dhcp.wan.serverwatchdog=1
uci add_list dhcp.wan.serverpriority=10.0.0.1
uci add_list dhcp.wan.serverpriority=10.0.0.2
uci commit dhcp
# Enable watchdog on VPN (assuming vpn is a valid UCI interface)
uci set dhcp.vpn.serverwatchdog=1
uci add_list dhcp.vpn.serverpriority=172.16.0.1
uci add_list dhcp.vpn.serverpriority=172.16.0.2
uci commit dhcp
Finally, the watchdog script is added to cron to be executed at a regular interval (every minute)
(crontab -l 2>/dev/null; echo "*/1 * * * * /usr/bin/dhcp-interface-watchdog.sh") | crontab -
Next will be, synchronizing both active leases and lease configuration from other hosts
NOTE : this code is untested