Initial import
This commit is contained in:
commit
73cd11d943
9
install.sh
Executable file
9
install.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p /etc/config /etc/init.d /usr/sbin
|
||||
[ -f /etc/config/wan-failover ] || cp -a wan-failover.config /etc/config/wan-failover
|
||||
cp -a wan-failover.init.d /etc/init.d/wan-failover
|
||||
cp -a wan-failover /usr/sbin/wan-failover
|
||||
chmod +x /etc/init.d/wan-failover /usr/sbin/wan-failover
|
||||
ln -sf ../init.d/wan-failover /etc/rc.d/S99wan-failover
|
||||
ln -sf ../init.d/wan-failover /etc/rc.d/K85wan-failover
|
221
wan-failover
Executable file
221
wan-failover
Executable file
@ -0,0 +1,221 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) Brian Tarricone <brian@tarricone.org>
|
||||
# Released under the terms of the BSD 3-clause license.
|
||||
# See https://opensource.org/licenses/BSD-3-Clause for details.
|
||||
|
||||
set -e
|
||||
|
||||
default_check_interval=5
|
||||
default_health_ips="8.8.8.8 1.1.1.1"
|
||||
default_ping_count=5
|
||||
default_health_quorum=1
|
||||
default_up_successes=5
|
||||
default_down_failures=3
|
||||
default_active_metric=10
|
||||
default_inactive_metric=20
|
||||
|
||||
log() {
|
||||
logger -t wan-failover -p daemon.notice "$1"
|
||||
}
|
||||
|
||||
dlog() {
|
||||
if [ "$debug" ]; then
|
||||
logger -t wan-failover -p daemon.debug "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
elog() {
|
||||
logger -t wan-failover -p daemon.err "$1"
|
||||
}
|
||||
|
||||
cfg() {
|
||||
uci get "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
cfg_set() {
|
||||
uci set "$1"="$2"
|
||||
}
|
||||
|
||||
cfg_commit() {
|
||||
uci commit "$1"
|
||||
}
|
||||
|
||||
ourcfg() {
|
||||
cfg "wan-failover.$1"
|
||||
}
|
||||
|
||||
cfg_init() {
|
||||
[ "$(ourcfg globals.debug || echo 'false')" = "true" ] && debug=1 || debug=
|
||||
check_interval=$(ourcfg globals.check_interval || echo $default_check_interval)
|
||||
|
||||
primary_iface=$(ourcfg globals.primary || echo '')
|
||||
primary_ifname=$(cfg network.$primary_iface.ifname || echo '')
|
||||
fallback_iface=$(ourcfg globals.fallback || echo '')
|
||||
fallback_ifname=$(cfg network.$fallback_iface.ifname || echo '')
|
||||
|
||||
primary_health_ips=$(ourcfg $primary_iface.ip || echo $default_health_ips)
|
||||
primary_ping_count=$(ourcfg $primary_iface.count || echo $default_ping_count)
|
||||
primary_health_quorum=$(ourcfg $primary_iface.quorum || echo $default_health_quorum)
|
||||
primary_up_successes=$(ourcfg $primary_iface.up || echo $default_up_successes)
|
||||
primary_down_failures=$(ourcfg $primary_iface.down || echo $default_down_failures)
|
||||
|
||||
fallback_health_ips=$(ourcfg $fallback_iface.ip || echo $default_health_ips)
|
||||
fallback_ping_count=$(ourcfg $fallback_iface.count || echo $default_ping_count)
|
||||
fallback_health_quorum=$(ourcfg $fallback_iface.quorum || echo $default_health_quorum)
|
||||
fallback_up_successes=$(ourcfg $fallback_iface.up || echo $default_up_successes)
|
||||
fallback_down_failures=$(ourcfg $fallback_iface.down || echo $default_down_failures)
|
||||
|
||||
active_metric=$(ourcfg globals.active_metric || echo $default_active_metric)
|
||||
inactive_metric=$(ourcfg globals.inactive_metric || echo $default_inactive_metric)
|
||||
|
||||
if [ -z "$primary_iface" ]; then
|
||||
elog "Must set wan-failover.globals.primary to the primary interface"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$primary_ifname" ]; then
|
||||
elog "Can't figure out interface device name for interface $primary_iface" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$fallback_iface" ]; then
|
||||
elog "Must set wan-failover.globals.fallback to the fallback interface"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$fallback_ifname" ]; then
|
||||
elog "Can't figure out interface device name for interface $fallback_iface" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "initialized with primary interface $primary_iface ($primary_ifname), fallback interface $fallback_iface ($fallback_ifname); will wait $check_interval second(s) between each check"
|
||||
log "will check primary using $primary_health_ips ($primary_health_quorum must work) with $primary_ping_count ping(s), and will require $primary_up_successes successes to be up, $primary_down_failures to be down"
|
||||
log "will check fallback using $fallback_health_ips ($fallback_health_quorum must work) with $fallback_ping_count ping(s), and will require $fallback_up_successes successes to be up, $fallback_down_failures to be down"
|
||||
log "active interface metric will be set to $active_metric, inactive to $inactive_metric"
|
||||
}
|
||||
|
||||
get_active_iface() {
|
||||
local primary=$(cfg network.$primary_iface.metric)
|
||||
local fallback=$(cfg network.$fallback_iface.metric)
|
||||
dlog "current primary metric is $primary, fallback metric is $fallback"
|
||||
[ $primary -gt $fallback ] && echo $fallback_iface || echo $primary_iface
|
||||
}
|
||||
|
||||
set_active_iface() {
|
||||
local active=$1
|
||||
local inactive=$2
|
||||
dlog "setting interface $active active, $inactive inactive"
|
||||
cfg_set network.$active.metric $active_metric
|
||||
cfg_set network.$inactive.metric $inactive_metric
|
||||
cfg_commit network
|
||||
/etc/init.d/network reload
|
||||
}
|
||||
|
||||
ping_target() {
|
||||
local ifname=$1
|
||||
local count=$2
|
||||
local ip=$3
|
||||
|
||||
echo "$ip" | grep -q ':' && ping=ping6 || ping=ping
|
||||
|
||||
local successes=0
|
||||
local failures=0
|
||||
while [ $count -gt 0 ]; do
|
||||
dlog "[$ifname,$ip] ping"
|
||||
$ping -n -q -I $ifname -c 1 -w 2 $ip >/dev/null 2>&1 &&
|
||||
successes=$(expr $successes + 1) ||
|
||||
failures=$(expr $failures + 1)
|
||||
dlog "[$ifname,$ip] ping successes: $successes, failures: $failures"
|
||||
count=$(expr $count - 1 || true)
|
||||
done
|
||||
|
||||
[ $successes -gt $failures ] && return 0 || return 1
|
||||
}
|
||||
|
||||
check_health() {
|
||||
local ifname=$1
|
||||
shift
|
||||
local count=$1
|
||||
shift
|
||||
local quorum=$1
|
||||
shift
|
||||
local ips="$@"
|
||||
|
||||
local pids=
|
||||
local ip
|
||||
for ip in $ips; do
|
||||
ping_target $ifname $count $ip &
|
||||
pids="$pids $!"
|
||||
done
|
||||
|
||||
local successes=0
|
||||
local failures=0
|
||||
local pid
|
||||
for pid in $pids; do
|
||||
wait $pid && {
|
||||
dlog "[$pid] got success"
|
||||
successes=$(expr $successes + 1)
|
||||
} || {
|
||||
dlog "[$pid] got failure"
|
||||
failures=$(expr $failures + 1)
|
||||
}
|
||||
done
|
||||
|
||||
[ $successes -ge $quorum ] && return 0 || return 1
|
||||
}
|
||||
|
||||
cfg_init
|
||||
|
||||
primary_successes=0
|
||||
primary_failures=0
|
||||
fallback_failures=0
|
||||
fallback_successes=0
|
||||
|
||||
while true; do
|
||||
sleep $check_interval
|
||||
|
||||
active=$(get_active_iface)
|
||||
dlog "currently active: $active"
|
||||
|
||||
dlog "checking health"
|
||||
check_health $primary_ifname $primary_ping_count $primary_health_quorum $primary_health_ips &
|
||||
primary_pid=$!
|
||||
check_health $fallback_ifname $fallback_ping_count $fallback_health_quorum $fallback_health_ips &
|
||||
fallback_pid=$!
|
||||
|
||||
wait $primary_pid && {
|
||||
primary_successes=$(expr $primary_successes + 1)
|
||||
primary_failures=0
|
||||
} || {
|
||||
primary_successes=0
|
||||
primary_failures=$(expr $primary_failures + 1)
|
||||
}
|
||||
dlog "[$primary_iface] health check done; successes: $primary_successes, failures: $primary_failures"
|
||||
|
||||
wait $fallback_pid && {
|
||||
fallback_successes=$(expr $fallback_successes + 1)
|
||||
fallback_failures=0
|
||||
} || {
|
||||
fallback_successes=0
|
||||
fallback_failures=$(expr $fallback_failures + 1)
|
||||
}
|
||||
dlog "[$fallback_iface] health check done; successes: $fallback_successes, failures: $fallback_failures"
|
||||
|
||||
if [ "$active" = "$primary_iface" ]; then
|
||||
if [ $primary_failures -ge $primary_down_failures ]; then
|
||||
if [ $fallback_failures -ge $fallback_down_failures ]; then
|
||||
elog "primary is down, but fallback is as well"
|
||||
else
|
||||
log "$primary_iface is down, setting $fallback_iface active"
|
||||
set_active_iface $fallback_iface $primary_iface
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ $primary_successes -ge $primary_up_successes ]; then
|
||||
log "$primary_iface is back up; setting active"
|
||||
set_active_iface $primary_iface $fallback_iface
|
||||
elif [ $primary_successes -gt 0 ]; then
|
||||
dlog "$primary_iface is coming back up; sticking with $fallback_iface until certain"
|
||||
else
|
||||
dlog "$primary_iface is still down; sticking with $fallback_iface"
|
||||
fi
|
||||
fi
|
||||
done
|
27
wan-failover.config
Normal file
27
wan-failover.config
Normal file
@ -0,0 +1,27 @@
|
||||
config globals 'globals'
|
||||
option primary 'wan'
|
||||
option fallback 'wwan'
|
||||
option check_interval 10
|
||||
option debug 'false'
|
||||
|
||||
config interface 'wan'
|
||||
list ip '8.8.8.8'
|
||||
list ip '1.1.1.1'
|
||||
list ip '2001:4860:4860::8888'
|
||||
list ip '2606:4700:4700::1112'
|
||||
option quorum '2'
|
||||
option count '5'
|
||||
option timeout '2'
|
||||
option down '3'
|
||||
option up '5'
|
||||
|
||||
config interface 'wwan'
|
||||
list ip '8.8.8.8'
|
||||
list ip '1.1.1.1'
|
||||
# list ip '2001:4860:4860::8888'
|
||||
# list ip '2606:4700:4700::1112'
|
||||
option quorum '2'
|
||||
option count '5'
|
||||
option timeout '2'
|
||||
option down '3'
|
||||
option up '5'
|
31
wan-failover.init.d
Executable file
31
wan-failover.init.d
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
#
|
||||
# Copyright (c) Brian Tarricone <brian@tarricone.org>
|
||||
# Released under the terms of the BSD 3-clause license.
|
||||
# See https://opensource.org/licenses/BSD-3-Clause for details.
|
||||
|
||||
START=99
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/wan-failover
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
json_add_array interfaces
|
||||
for i in $(load_ifaces); do
|
||||
json_add_string "" "$i"
|
||||
done
|
||||
json_close_array
|
||||
|
||||
ubus call umdns set_config "$(json_dump)"
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_open_trigger
|
||||
procd_add_config_trigger "config.change" "wan-failover" /etc/init.d/wan-failover restart
|
||||
procd_close_trigger
|
||||
procd_close_instance
|
||||
}
|
Loading…
Reference in New Issue
Block a user