feat: rebase static IP on gateway change
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
- 多种打印机驱动可选
|
- 多种打印机驱动可选
|
||||||
- 驱动管理器(默认仅允许内网访问)
|
- 驱动管理器(默认仅允许内网访问)
|
||||||
- 网络和打印服务守护(断网自动切 DHCP,CUPS 卡死自动恢复)
|
- 网络和打印服务守护(断网自动切 DHCP,CUPS 卡死自动恢复)
|
||||||
|
- 静态 IP 支持网关变更自愈:先切 DHCP,再按原静态 IP 尾号迁移到新网段
|
||||||
- 支持一键卸载
|
- 支持一键卸载
|
||||||
|
|
||||||
## 快速安装
|
## 快速安装
|
||||||
@@ -151,6 +152,9 @@ lpinfo -v | grep usb
|
|||||||
### 手机发送打印任务很慢
|
### 手机发送打印任务很慢
|
||||||
脚本已配置 `HostNameLookups Off` 解决此问题。如果仍然慢,检查网络连接。
|
脚本已配置 `HostNameLookups Off` 解决此问题。如果仍然慢,检查网络连接。
|
||||||
|
|
||||||
|
### 更换路由器或网关后找不到打印服务器
|
||||||
|
网络守护会在连续检测失败后先切换到 DHCP 恢复连接。如果发现 DHCP 获取到的新网关和原静态网关不同,会保留原静态 IP 的最后一段,例如 `192.168.11.219` 的 `219`,自动改成新网段下的 `x.x.x.219`。
|
||||||
|
|
||||||
### 界面显示英文
|
### 界面显示英文
|
||||||
确保安装时下载了中文模板、中文首页和前端汉化脚本。重新运行安装脚本可以修复。
|
确保安装时下载了中文模板、中文首页和前端汉化脚本。重新运行安装脚本可以修复。
|
||||||
|
|
||||||
|
|||||||
@@ -1196,6 +1196,7 @@ install_watchdogs() {
|
|||||||
local current_interface=$(get_interface)
|
local current_interface=$(get_interface)
|
||||||
local current_netmask=$(get_netmask)
|
local current_netmask=$(get_netmask)
|
||||||
local current_dns=$(get_dns)
|
local current_dns=$(get_dns)
|
||||||
|
local current_host_octet="${current_ip##*.}"
|
||||||
current_dns="${current_dns:-114.114.114.114 223.5.5.5}"
|
current_dns="${current_dns:-114.114.114.114 223.5.5.5}"
|
||||||
|
|
||||||
local ping_targets="223.5.5.5 114.114.114.114"
|
local ping_targets="223.5.5.5 114.114.114.114"
|
||||||
@@ -1213,9 +1214,12 @@ STATIC_IP="$current_ip"
|
|||||||
STATIC_PREFIX="$current_netmask"
|
STATIC_PREFIX="$current_netmask"
|
||||||
STATIC_GATEWAY="$current_gateway"
|
STATIC_GATEWAY="$current_gateway"
|
||||||
STATIC_DNS="$current_dns"
|
STATIC_DNS="$current_dns"
|
||||||
|
STATIC_HOST_OCTET="$current_host_octet"
|
||||||
PING_TARGETS="$ping_targets"
|
PING_TARGETS="$ping_targets"
|
||||||
FAIL_THRESHOLD=3
|
FAIL_THRESHOLD=3
|
||||||
DHCP_AFTER_FAILURE=1
|
DHCP_AFTER_FAILURE=1
|
||||||
|
REBASE_STATIC_ON_GATEWAY_CHANGE=1
|
||||||
|
REBASE_WAIT_SECONDS=20
|
||||||
LOG_FILE="/var/log/cups-watchdog/network.log"
|
LOG_FILE="/var/log/cups-watchdog/network.log"
|
||||||
MAX_LOG_SIZE_KB=256
|
MAX_LOG_SIZE_KB=256
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
# Network watchdog for the CUPS print server.
|
# Network watchdog for the CUPS print server.
|
||||||
# Default behavior: when connectivity checks fail repeatedly, switch the
|
# Default behavior: when connectivity checks fail repeatedly, switch the
|
||||||
# configured interface back to DHCP so the box can regain network access.
|
# configured interface back to DHCP so the box can regain network access.
|
||||||
|
# If DHCP reveals a different gateway, the script can rebuild the static IP
|
||||||
|
# with the new network prefix while keeping the old host octet.
|
||||||
#
|
#
|
||||||
# Edit /etc/cups-watchdog/network-watchdog.conf to set STATIC_IP,
|
# Edit /etc/cups-watchdog/network-watchdog.conf to set STATIC_IP,
|
||||||
# STATIC_PREFIX, STATIC_GATEWAY and STATIC_DNS. You can also run:
|
# STATIC_PREFIX, STATIC_GATEWAY and STATIC_DNS. You can also run:
|
||||||
@@ -20,9 +22,12 @@ STATIC_IP=""
|
|||||||
STATIC_PREFIX="24"
|
STATIC_PREFIX="24"
|
||||||
STATIC_GATEWAY=""
|
STATIC_GATEWAY=""
|
||||||
STATIC_DNS="114.114.114.114 223.5.5.5"
|
STATIC_DNS="114.114.114.114 223.5.5.5"
|
||||||
|
STATIC_HOST_OCTET=""
|
||||||
PING_TARGETS="223.5.5.5 114.114.114.114"
|
PING_TARGETS="223.5.5.5 114.114.114.114"
|
||||||
FAIL_THRESHOLD=3
|
FAIL_THRESHOLD=3
|
||||||
DHCP_AFTER_FAILURE=1
|
DHCP_AFTER_FAILURE=1
|
||||||
|
REBASE_STATIC_ON_GATEWAY_CHANGE=1
|
||||||
|
REBASE_WAIT_SECONDS=20
|
||||||
LOG_FILE="/var/log/cups-watchdog/network.log"
|
LOG_FILE="/var/log/cups-watchdog/network.log"
|
||||||
MAX_LOG_SIZE_KB=256
|
MAX_LOG_SIZE_KB=256
|
||||||
|
|
||||||
@@ -71,6 +76,56 @@ detect_interface() {
|
|||||||
ip route show default 2>/dev/null | awk '{print $5; exit}'
|
ip route show default 2>/dev/null | awk '{print $5; exit}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_ipv4_for_interface() {
|
||||||
|
local iface="$1"
|
||||||
|
|
||||||
|
ip -4 -o addr show dev "$iface" scope global 2>/dev/null | awk '{split($4, a, "/"); print a[1]; exit}'
|
||||||
|
}
|
||||||
|
|
||||||
|
current_prefix_for_interface() {
|
||||||
|
local iface="$1"
|
||||||
|
|
||||||
|
ip -4 -o addr show dev "$iface" scope global 2>/dev/null | awk '{split($4, a, "/"); print a[2]; exit}'
|
||||||
|
}
|
||||||
|
|
||||||
|
current_gateway_for_interface() {
|
||||||
|
local iface="$1"
|
||||||
|
local gateway
|
||||||
|
|
||||||
|
gateway="$(ip route show default dev "$iface" 2>/dev/null | awk '{print $3; exit}')"
|
||||||
|
if [ -z "$gateway" ]; then
|
||||||
|
gateway="$(ip route show default 2>/dev/null | awk '{print $3; exit}')"
|
||||||
|
fi
|
||||||
|
echo "$gateway"
|
||||||
|
}
|
||||||
|
|
||||||
|
last_octet() {
|
||||||
|
local ip="$1"
|
||||||
|
|
||||||
|
case "$ip" in
|
||||||
|
*.*.*.*) echo "${ip##*.}" ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix24() {
|
||||||
|
local ip="$1"
|
||||||
|
|
||||||
|
case "$ip" in
|
||||||
|
*.*.*.*) echo "${ip%.*}" ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_host_octet() {
|
||||||
|
local octet="$1"
|
||||||
|
|
||||||
|
case "$octet" in
|
||||||
|
''|*[!0-9]*) return 1 ;;
|
||||||
|
esac
|
||||||
|
[ "$octet" -ge 1 ] 2>/dev/null && [ "$octet" -le 254 ] 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
nm_connection_for_interface() {
|
nm_connection_for_interface() {
|
||||||
local iface="$1"
|
local iface="$1"
|
||||||
nmcli -t -f NAME,DEVICE con show --active 2>/dev/null | awk -F: -v iface="$iface" '$2 == iface {print $1; exit}'
|
nmcli -t -f NAME,DEVICE con show --active 2>/dev/null | awk -F: -v iface="$iface" '$2 == iface {print $1; exit}'
|
||||||
@@ -141,6 +196,70 @@ configure_dhcp() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_config_value() {
|
||||||
|
local key="$1"
|
||||||
|
local value="$2"
|
||||||
|
local tmp_file
|
||||||
|
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
mkdir -p "$(dirname "$CONFIG_FILE")" 2>/dev/null || true
|
||||||
|
: > "$CONFIG_FILE" 2>/dev/null || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_file="${CONFIG_FILE}.tmp.$$"
|
||||||
|
if grep -q "^${key}=" "$CONFIG_FILE" 2>/dev/null; then
|
||||||
|
awk -v key="$key" -v value="$value" '
|
||||||
|
BEGIN { q = sprintf("%c", 34) }
|
||||||
|
$0 ~ "^" key "=" { print key "=" q value q; next }
|
||||||
|
{ print }
|
||||||
|
' "$CONFIG_FILE" > "$tmp_file" && mv "$tmp_file" "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
printf '%s="%s"\n' "$key" "$value" >> "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
chmod 600 "$CONFIG_FILE" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_dhcp_network() {
|
||||||
|
local iface="$1"
|
||||||
|
local i=0
|
||||||
|
local ip
|
||||||
|
local gateway
|
||||||
|
|
||||||
|
while [ "$i" -lt "${REBASE_WAIT_SECONDS:-20}" ]; do
|
||||||
|
ip="$(current_ipv4_for_interface "$iface")"
|
||||||
|
gateway="$(current_gateway_for_interface "$iface")"
|
||||||
|
if [ -n "$ip" ] && [ -n "$gateway" ]; then
|
||||||
|
echo "$ip $gateway"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate_ip_in_use() {
|
||||||
|
local iface="$1"
|
||||||
|
local candidate_ip="$2"
|
||||||
|
local current_ip="$3"
|
||||||
|
|
||||||
|
[ "$candidate_ip" = "$current_ip" ] && return 1
|
||||||
|
|
||||||
|
if command -v arping >/dev/null 2>&1; then
|
||||||
|
# arping duplicate-address detection returns success when no reply is seen.
|
||||||
|
arping -D -I "$iface" -c 2 -w 3 "$candidate_ip" >/dev/null 2>&1
|
||||||
|
[ "$?" != "0" ] && return 0
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ping -c 1 -W 1 "$candidate_ip" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
configure_static_nmcli() {
|
configure_static_nmcli() {
|
||||||
local iface="$1"
|
local iface="$1"
|
||||||
local conn
|
local conn
|
||||||
@@ -243,6 +362,105 @@ configure_static() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rebase_static_after_dhcp() {
|
||||||
|
local iface="$1"
|
||||||
|
local dhcp_state
|
||||||
|
local dhcp_ip
|
||||||
|
local dhcp_gateway
|
||||||
|
local dhcp_prefix
|
||||||
|
local host_octet
|
||||||
|
local base_prefix
|
||||||
|
local candidate_ip
|
||||||
|
local old_static_ip="$STATIC_IP"
|
||||||
|
local old_static_gateway="$STATIC_GATEWAY"
|
||||||
|
local old_static_prefix="$STATIC_PREFIX"
|
||||||
|
local old_static_dns="$STATIC_DNS"
|
||||||
|
local current_dns
|
||||||
|
|
||||||
|
[ "${REBASE_STATIC_ON_GATEWAY_CHANGE:-1}" = "1" ] || return 0
|
||||||
|
|
||||||
|
if [ -z "${STATIC_IP:-}" ] || [ -z "${STATIC_GATEWAY:-}" ]; then
|
||||||
|
log_msg "skip static rebase: STATIC_IP or STATIC_GATEWAY is empty"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
dhcp_state="$(wait_for_dhcp_network "$iface" 2>/dev/null || true)"
|
||||||
|
if [ -z "$dhcp_state" ]; then
|
||||||
|
log_msg "skip static rebase: DHCP did not provide an IP and gateway in time"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
dhcp_ip="$(echo "$dhcp_state" | awk '{print $1}')"
|
||||||
|
dhcp_gateway="$(echo "$dhcp_state" | awk '{print $2}')"
|
||||||
|
if [ "$dhcp_gateway" = "$STATIC_GATEWAY" ]; then
|
||||||
|
log_msg "DHCP gateway is still $dhcp_gateway; keep DHCP recovery without rebasing static IP"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
host_octet="${STATIC_HOST_OCTET:-$(last_octet "$STATIC_IP")}"
|
||||||
|
if ! valid_host_octet "$host_octet"; then
|
||||||
|
log_msg "skip static rebase: invalid STATIC_HOST_OCTET '$host_octet'"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
base_prefix="$(prefix24 "$dhcp_ip")"
|
||||||
|
[ -n "$base_prefix" ] || base_prefix="$(prefix24 "$dhcp_gateway")"
|
||||||
|
if [ -z "$base_prefix" ]; then
|
||||||
|
log_msg "skip static rebase: cannot derive IPv4 /24 prefix from DHCP state"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
candidate_ip="${base_prefix}.${host_octet}"
|
||||||
|
if candidate_ip_in_use "$iface" "$candidate_ip" "$dhcp_ip"; then
|
||||||
|
log_msg "skip static rebase: candidate $candidate_ip appears to be in use; staying on DHCP"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
dhcp_prefix="$(current_prefix_for_interface "$iface")"
|
||||||
|
STATIC_IP="$candidate_ip"
|
||||||
|
STATIC_GATEWAY="$dhcp_gateway"
|
||||||
|
STATIC_PREFIX="${dhcp_prefix:-$STATIC_PREFIX}"
|
||||||
|
current_dns="$(grep -E '^nameserver ' /etc/resolv.conf 2>/dev/null | awk '{print $2}' | head -n2 | tr '\n' ' ')"
|
||||||
|
STATIC_DNS="${current_dns:-$STATIC_DNS}"
|
||||||
|
|
||||||
|
log_msg "gateway changed from $old_static_gateway to $STATIC_GATEWAY; rebasing static IP from $old_static_ip to $STATIC_IP/$STATIC_PREFIX"
|
||||||
|
configure_static
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
if connectivity_ok; then
|
||||||
|
set_config_value "STATIC_IP" "$STATIC_IP" || true
|
||||||
|
set_config_value "STATIC_GATEWAY" "$STATIC_GATEWAY" || true
|
||||||
|
set_config_value "STATIC_PREFIX" "$STATIC_PREFIX" || true
|
||||||
|
set_config_value "STATIC_DNS" "$STATIC_DNS" || true
|
||||||
|
set_config_value "STATIC_HOST_OCTET" "$host_octet" || true
|
||||||
|
set_config_value "PING_TARGETS" "$STATIC_GATEWAY 223.5.5.5 114.114.114.114" || true
|
||||||
|
log_msg "static rebase succeeded: $STATIC_IP via $STATIC_GATEWAY"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATIC_IP="$old_static_ip"
|
||||||
|
STATIC_GATEWAY="$old_static_gateway"
|
||||||
|
STATIC_PREFIX="$old_static_prefix"
|
||||||
|
STATIC_DNS="$old_static_dns"
|
||||||
|
log_msg "static rebase to $candidate_ip failed connectivity check; falling back to DHCP"
|
||||||
|
configure_dhcp
|
||||||
|
}
|
||||||
|
|
||||||
|
recover_after_failure() {
|
||||||
|
local iface
|
||||||
|
local fallback_iface
|
||||||
|
|
||||||
|
fallback_iface="$(detect_interface)"
|
||||||
|
configure_dhcp || return 1
|
||||||
|
iface="$(detect_interface)"
|
||||||
|
[ -n "$iface" ] || iface="$fallback_iface"
|
||||||
|
if [ -z "$iface" ]; then
|
||||||
|
log_msg "skip static rebase: network interface not found after DHCP recovery"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
rebase_static_after_dhcp "$iface"
|
||||||
|
}
|
||||||
|
|
||||||
connectivity_ok() {
|
connectivity_ok() {
|
||||||
local iface
|
local iface
|
||||||
local gateway
|
local gateway
|
||||||
@@ -288,7 +506,7 @@ check_network() {
|
|||||||
|
|
||||||
log_msg "connectivity failed ($fail_count/$FAIL_THRESHOLD)"
|
log_msg "connectivity failed ($fail_count/$FAIL_THRESHOLD)"
|
||||||
if [ "$fail_count" -ge "$FAIL_THRESHOLD" ] && [ "$DHCP_AFTER_FAILURE" = "1" ]; then
|
if [ "$fail_count" -ge "$FAIL_THRESHOLD" ] && [ "$DHCP_AFTER_FAILURE" = "1" ]; then
|
||||||
configure_dhcp
|
recover_after_failure
|
||||||
echo 0 > "$count_file"
|
echo 0 > "$count_file"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user