🤞 Sync 2025-12-11 10:03:47
This commit is contained in:
47
floatip/Makefile
Normal file
47
floatip/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (C) 2024 jjm2473 <jjm2473@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the MIT License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=floatip
|
||||
PKG_VERSION:=1.0.10
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=Float IP
|
||||
DEPENDS:=+curl
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Auto setup an IP if some host down
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/floatip
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/libexec $(1)/etc/init.d $(1)/etc/config $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./files/floatip.sh $(1)/usr/libexec/floatip.sh
|
||||
$(INSTALL_BIN) ./files/floatip.init $(1)/etc/init.d/floatip
|
||||
$(INSTALL_CONF) ./files/floatip.config $(1)/etc/config/floatip
|
||||
$(INSTALL_BIN) ./files/floatip.uci-default $(1)/etc/uci-defaults/floatip
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
17
floatip/files/floatip.config
Normal file
17
floatip/files/floatip.config
Normal file
@@ -0,0 +1,17 @@
|
||||
config floatip 'main'
|
||||
# 启动时,enabled != 1 ,或者原 lan 口配置网段不包括 set_ip,清除自身的 set_ip,然后退出进程。
|
||||
option enabled '0'
|
||||
# fallback 表示后备
|
||||
option role 'fallback'
|
||||
# option role 'main'
|
||||
# 对于 fallback 节点,检查到 check_ip 都不在线超过一定时间(例如30秒),就设置自身的 set_ip,然后检查 check_ip 中任一 IP 在线就清除自身的 set_ip,重复上述流程。
|
||||
# 对于 main 节点,启动后不断检查 set_ip 和 check_url,直到 set_ip 不在线且 check_url 没有失败,就设置自身的 set_ip 并允许 LAN 口 ping,否则清除自身的 set_ip 并禁止 LAN 口 ping,重复上述流程。
|
||||
# set_ip 可以不提供前缀长度,将会按 lan 口配置的网段的长度
|
||||
option set_ip '192.168.100.3/24'
|
||||
# option set_ip '192.168.100.3'
|
||||
# check_ip 仅 fallback 有效,并且检查时只检查跟 set_ip 同一网段的
|
||||
list check_ip '192.168.100.2'
|
||||
# list check_ip '192.168.100.4'
|
||||
# check_url 仅 main 有效
|
||||
# list check_url 'https://www.google.com/generate_204'
|
||||
# option check_url_timeout '5'
|
||||
78
floatip/files/floatip.init
Executable file
78
floatip/files/floatip.init
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=98
|
||||
USE_PROCD=1
|
||||
|
||||
enable_lan_ping() {
|
||||
uci -q set firewall.floatip_lan_offline.enabled=0 || return 0
|
||||
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall reload
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load floatip
|
||||
config_get_bool enabled "main" enabled 0
|
||||
ifdown floatip
|
||||
[[ "$enabled" = 1 ]] || {
|
||||
enable_lan_ping
|
||||
return 0
|
||||
}
|
||||
[[ "`uci -q get network.lan.proto`" = "static" ]] || {
|
||||
logger -s -t floatip "LAN proto is not static"
|
||||
return 0
|
||||
}
|
||||
local lan_iface="`uci -q get network.lan.device`"
|
||||
if [ -z "$lan_iface" ]; then
|
||||
logger -s -t floatip "Cannot get LAN device"
|
||||
return 0
|
||||
fi
|
||||
local set_ip set_prefix
|
||||
config_get set_ip "main" set_ip
|
||||
[[ -n "$set_ip" ]] || return 0
|
||||
if [[ "$set_ip" = "*/*" ]]; then
|
||||
eval "$(ipcalc.sh "$set_ip" )";set_prefix=$PREFIX;set_ip=$IP
|
||||
else
|
||||
set_prefix=32
|
||||
fi
|
||||
local lan_ip="`uci -q get network.lan.ipaddr`"
|
||||
[[ -n "$lan_ip" ]] || return 0
|
||||
local lan_net lan_prefix set_net ip
|
||||
local in_range=0
|
||||
local lan_netmask="`uci -q get network.lan.netmask`"
|
||||
for ip in $lan_ip; do
|
||||
if [[ "$ip" = "*/*" ]]; then
|
||||
eval "$(ipcalc.sh $ip )";lan_net=$NETWORK;lan_prefix=$PREFIX
|
||||
else
|
||||
# prefix=32 if not present
|
||||
[[ -n "$lan_netmask" ]] || continue
|
||||
eval "$(ipcalc.sh $ip $lan_netmask )";lan_net=$NETWORK;lan_prefix=$PREFIX
|
||||
fi
|
||||
[[ "$set_prefix" -ge "$lan_prefix" ]] || continue
|
||||
eval "$(ipcalc.sh $set_ip/$lan_prefix )";set_net=$NETWORK
|
||||
[[ "$set_net" = "$lan_net" ]] && {
|
||||
[[ "$set_prefix" = 32 ]] && set_prefix=$lan_prefix
|
||||
in_range=1
|
||||
break
|
||||
}
|
||||
done
|
||||
|
||||
[[ $in_range = 1 ]] || {
|
||||
logger -s -t floatip "float ip is not belong to any LAN subnets"
|
||||
return 0
|
||||
}
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/libexec/floatip.sh "$lan_iface" "$set_prefix"
|
||||
procd_set_param stderr 1
|
||||
procd_set_param file /etc/config/floatip
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
enable_lan_ping
|
||||
ifdown floatip
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "network" "floatip"
|
||||
}
|
||||
246
floatip/files/floatip.sh
Executable file
246
floatip/files/floatip.sh
Executable file
@@ -0,0 +1,246 @@
|
||||
#!/bin/sh
|
||||
|
||||
LOCK_FILE="/var/lock/floatip_loop.lock"
|
||||
|
||||
DEFAULT_PREFIX=24
|
||||
|
||||
# random number 0-255
|
||||
random() {
|
||||
local num=$(dd if=/dev/urandom bs=1 count=1 2>/dev/null | hexdump -ve '1/1 "%u"')
|
||||
if [[ -z "$num" ]]; then
|
||||
num=$(($(grep -om1 '[0-9][0-9]$' /proc/uptime) * 255 / 100))
|
||||
fi
|
||||
echo ${num:-1}
|
||||
}
|
||||
|
||||
# check host alive, timeout in 2 seconds
|
||||
host_alive() {
|
||||
ping -4 -c 2 -A -t 1 -W 1 -q "$1" >/dev/null
|
||||
# arping -f -q -b -c 2 -w 2 -i 1 -I $LAN_IFACE "$1"
|
||||
}
|
||||
|
||||
set_up() {
|
||||
local ipaddr="$1"
|
||||
echo "set my floatip to $ipaddr" >&2
|
||||
if ! uci -q get network.floatip.ipaddr | grep -Fwq $ipaddr; then
|
||||
if [[ "x$(uci -q get network.floatip)" = xinterface ]]; then
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set network.floatip.device=$LAN_IFACE
|
||||
delete network.floatip.ipaddr
|
||||
add_list network.floatip.ipaddr=$ipaddr
|
||||
EOF
|
||||
else
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set network.floatip=interface
|
||||
set network.floatip.proto=static
|
||||
set network.floatip.device=$LAN_IFACE
|
||||
add_list network.floatip.ipaddr=$ipaddr
|
||||
set network.floatip.auto=0
|
||||
EOF
|
||||
fi
|
||||
uci commit network
|
||||
fi
|
||||
ifup floatip
|
||||
}
|
||||
|
||||
set_lan_ping() {
|
||||
if [[ "$1" = 0 ]]; then
|
||||
if [[ "x$(uci -q get firewall.floatip_lan_offline)" = xrule ]]; then
|
||||
uci -q delete firewall.floatip_lan_offline.enabled
|
||||
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||
else
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set firewall.floatip_lan_offline=rule
|
||||
set firewall.floatip_lan_offline.name=FloatIP-LAN-Offline
|
||||
set firewall.floatip_lan_offline.src=lan
|
||||
set firewall.floatip_lan_offline.proto=icmp
|
||||
set firewall.floatip_lan_offline.icmp_type=echo-request
|
||||
set firewall.floatip_lan_offline.family=ipv4
|
||||
set firewall.floatip_lan_offline.target=DROP
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
uci -q set firewall.floatip_lan_offline.enabled=0 || return 0
|
||||
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||
fi
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall reload 2>&1
|
||||
}
|
||||
|
||||
safe_sleep() {
|
||||
local sec="$1"
|
||||
[[ "$sec" -lt 1 ]] && sec=1
|
||||
sleep $sec
|
||||
}
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
fallback_loop() {
|
||||
local set_ip check_ip set_net set_prefix
|
||||
config_get set_ip "main" set_ip
|
||||
[[ -n "$set_ip" ]] || return 1
|
||||
[[ "$set_ip" = "*/*" ]] || set_ip="$set_ip/$DEFAULT_PREFIX"
|
||||
eval "$(ipcalc.sh "$set_ip" )";set_net=$NETWORK;set_prefix=$PREFIX;set_ip=$IP
|
||||
local ipaddr="$set_ip/$set_prefix"
|
||||
echo "ipaddr=$ipaddr"
|
||||
|
||||
local valid_check_ip cip
|
||||
config_get check_ip "main" check_ip
|
||||
for cip in $check_ip; do
|
||||
eval "$(ipcalc.sh $cip/$set_prefix )"
|
||||
[[ "$NETWORK" = "$set_net" ]] && valid_check_ip="$valid_check_ip $cip"
|
||||
done
|
||||
valid_check_ip="$valid_check_ip "
|
||||
|
||||
local order_check_ip="$valid_check_ip"
|
||||
local found_alive consume_time
|
||||
local dead_counter=0 floatip_up=0
|
||||
while :; do
|
||||
found_alive=0
|
||||
consume_time=0
|
||||
echo "checking host(s) $order_check_ip alive"
|
||||
for cip in $order_check_ip; do
|
||||
if host_alive $cip; then
|
||||
echo "host $cip alive"
|
||||
found_alive=1
|
||||
# reorder to reduce check time
|
||||
order_check_ip=" ${cip}${valid_check_ip// $cip / }"
|
||||
break
|
||||
fi
|
||||
consume_time=$(($consume_time + 2))
|
||||
done
|
||||
if [[ $found_alive = 1 ]]; then
|
||||
if [[ $floatip_up = 1 ]]; then
|
||||
echo "set down floatip" >&2
|
||||
ifdown floatip
|
||||
floatip_up=0
|
||||
else
|
||||
dead_counter=0
|
||||
fi
|
||||
safe_sleep $((10 - $consume_time))
|
||||
continue
|
||||
fi
|
||||
if [[ $floatip_up = 1 ]]; then
|
||||
safe_sleep $((5 - $consume_time))
|
||||
continue
|
||||
fi
|
||||
dead_counter=$(($dead_counter + 1))
|
||||
if [[ $dead_counter -lt 3 ]]; then
|
||||
safe_sleep $((10 - $consume_time))
|
||||
continue
|
||||
fi
|
||||
echo "no host alive, set up floatip $ipaddr" >&2
|
||||
set_up "$ipaddr"
|
||||
floatip_up=1
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
main_loop() {
|
||||
local set_ip set_prefix
|
||||
config_get set_ip "main" set_ip
|
||||
[[ -n "$set_ip" ]] || return 1
|
||||
[[ "$set_ip" = "*/*" ]] || set_ip="$set_ip/$DEFAULT_PREFIX"
|
||||
eval "$(ipcalc.sh "$set_ip" )";set_prefix=$PREFIX;set_ip=$IP
|
||||
local ipaddr="$set_ip/$set_prefix"
|
||||
echo "ipaddr=$ipaddr"
|
||||
|
||||
local check_urls check_url_timeout
|
||||
config_get check_urls "main" check_url
|
||||
config_get check_url_timeout "main" check_url_timeout '5'
|
||||
local dead_counter=0 floatip_up=0 url_pass check_url curl_code consume_time found_alive
|
||||
# sleep 2-6s
|
||||
sleep $(( $(random) / 60 + 2))
|
||||
while :; do
|
||||
consume_time=0
|
||||
if [[ $floatip_up = 0 ]]; then
|
||||
found_alive=0
|
||||
echo "checking host $set_ip alive"
|
||||
if host_alive $set_ip; then
|
||||
echo "host $set_ip alive"
|
||||
found_alive=1
|
||||
else
|
||||
consume_time=$(($consume_time + 2))
|
||||
fi
|
||||
fi
|
||||
url_pass=1
|
||||
for check_url in $check_urls ; do
|
||||
curl -L --fail --show-error --no-progress-meter -o /dev/null \
|
||||
--connect-timeout "$check_url_timeout" --max-time "$check_url_timeout" \
|
||||
-I "$check_url" 2>&1
|
||||
curl_code=$?
|
||||
[[ $curl_code = 0 ]] && continue
|
||||
[[ $curl_code = 6 || $curl_code = 7 || $curl_code = 28 ]] && \
|
||||
consume_time=$(($consume_time + $check_url_timeout))
|
||||
echo "check_url $check_url fail, code $curl_code"
|
||||
url_pass=0
|
||||
break
|
||||
done
|
||||
if [[ $floatip_up = 0 ]]; then
|
||||
if [[ $url_pass = 1 ]]; then
|
||||
# notify fallback node to offline
|
||||
set_lan_ping
|
||||
if [[ $found_alive = 0 ]]; then
|
||||
echo "no host alive, and url passed, set up floatip $ipaddr" >&2
|
||||
set_up "$ipaddr"
|
||||
floatip_up=1
|
||||
fi
|
||||
else
|
||||
set_lan_ping 0
|
||||
fi
|
||||
safe_sleep $((5 - $consume_time))
|
||||
continue
|
||||
else
|
||||
if [[ $url_pass = 0 ]]; then
|
||||
dead_counter=$(($dead_counter + 1))
|
||||
if [[ $dead_counter -lt 3 ]]; then
|
||||
safe_sleep $((5 - $consume_time))
|
||||
continue
|
||||
fi
|
||||
echo "set down floatip, and disable ping" >&2
|
||||
ifdown floatip
|
||||
set_lan_ping 0
|
||||
floatip_up=0
|
||||
fi
|
||||
dead_counter=0
|
||||
fi
|
||||
sleep 20
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
local role
|
||||
config_load floatip
|
||||
config_get role "main" role
|
||||
if [[ "$role" = "main" ]]; then
|
||||
main_loop
|
||||
elif [[ "$role" = "fallback" ]]; then
|
||||
fallback_loop
|
||||
fi
|
||||
}
|
||||
|
||||
try_lock() {
|
||||
exec 200>"$LOCK_FILE"
|
||||
flock -x 200 && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "locking $LOCK_FILE" >&2
|
||||
try_lock || {
|
||||
echo "lock $LOCK_FILE failed, already running?" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "lock $LOCK_FILE success" >&2
|
||||
|
||||
LAN_IFACE="$1"
|
||||
[ -n "$LAN_IFACE" ] || {
|
||||
echo "LAN_IFACE is not set" >&2
|
||||
exit 1
|
||||
}
|
||||
shift
|
||||
|
||||
if [[ -n "$1" ]]; then
|
||||
[[ "$1" -ge 0 && "$1" -lt 32 ]] && DEFAULT_PREFIX=$1
|
||||
fi
|
||||
|
||||
main
|
||||
19
floatip/files/floatip.uci-default
Normal file
19
floatip/files/floatip.uci-default
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@floatip[-1]
|
||||
add ucitrack floatip
|
||||
set ucitrack.@floatip[-1].init=floatip
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
# don't change role if enabled
|
||||
[[ "`uci -q get floatip.main.enabled`" = "1" ]] && exit 0
|
||||
|
||||
[[ "`uci -q get network.lan.proto`" = "static" && -n "`uci -q get network.lan.gateway`" ]] || exit 0
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set floatip.main.role=main
|
||||
commit floatip
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user