updated on 24Jan.2025
Modified on 29Jan.2025 ver.1.7 增加变量 VPN_INTERFACES, tun2001 到对照表
原因:
在 QVPN 中创建多个连接,但每次要用命令行来修改,很难记住。这前做了一个 脚本,用参数来切换,当有第三个的时候,还要写 vpn1 vpn2 vpn3 ,在脚本里重复太多无意义操作,太蠢。 故,完全重脚本。
脚本:router_switcher.sh
1. 功能
通过界面,切换系统的默认路由。 显示公网 IP, 超时处理,维护列表,当前判断的关键词 wgc
2. 界面
脚本运行后,分四部分:路由表、位置信息、可用的路由、输入框
输入:只有数字 1-99 、q、 Q 与 回车
3. 演示
在输入选择的路由后,会执行命令切换路由,并在界面显示当前也之前的 "出口IP" (curl ifconfig.me 的结果)
4. 完整 脚本内容
粘贴代码到 shell 运行,会在当前目录下创建 router_switcher.sh 脚本并执行 chmod +x :
#!/bin/sh
# Created by Dave on 20Jan.2025
# History of Version
# Ver.1.0 was re-created script to support UI with simple input.
# Ver.1.1 added get_vpn_location() for location list and public IP info
# Ver.1.2 added UTF-8 to support unicode
# Ver.1.3 added timeout and error handling for connection issues
# Ver.1.5 added default gateway variable
# Ver.1.6 added CURRENT_DEFAULT_ROUTE variable and show in status
# Ver.1.7 added VPN_INTERFACES veriable for matching fixed interfaces
# ver.1.8 added after switched route to update ROUTE_DEVICE in cron_default_route_changed.sh on 1Feb.2025
# Ver.1.9 added restart_cloudflared() for restarting Cloudflared, modified restore_default() and switch_to_vpn() to include Cloudflared restart on 1Feb.2025
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# Define lines UI
TOP_LINE="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
DIV_LINE="─────────────────────────────────────────────"
# Configuration variables
DEFAULT_GATEWAY="192.168.1.254"
DEFAULT_INTERFACE="br0"
LAST_IP=""
CURL_TIMEOUT=6
CURRENT_DEFAULT_ROUTE=$(ip route show default | awk '/^default/ {print $3; exit}')
VPN_INTERFACES="\(wgc3\|tun2\)"
# Get public IP with curl options for timeout
get_public_ip() {
ip=$(curl -s --connect-timeout 5 --max-time $CURL_TIMEOUT ifconfig.me)
if [ $? -eq 0 ] && [ -n "$ip" ]; then
echo "$ip"
return 0
else
echo "Unable to retrieve"
return 1
fi
}
# Update the current default route
update_default_route() {
CURRENT_DEFAULT_ROUTE=$(ip route show default | awk '/^default/ {print $3; exit}')
}
# Check if interface exists
check_interface() {
interface=$1
if ! ip link show dev "$interface" >/dev/null 2>&1; then
echo "Interface $interface does not exist"
return 1
fi
return 0
}
# Show current status
show_current_status() {
echo "$TOP_LINE"
echo " Default Route:"
ip route show default | sed 's/^/ /'
echo "$DIV_LINE"
echo " Public IP:"
CURRENT_IP=$(get_public_ip)
echo " Current: $CURRENT_IP"
echo " Current default route is: $CURRENT_DEFAULT_ROUTE"
if [ -n "$LAST_IP" ] && [ "$LAST_IP" != "$CURRENT_IP" ] && [ "$LAST_IP" != "Unable to retrieve" ]; then
echo " Previous: $LAST_IP"
fi
echo "$DIV_LINE"
}
# Update last IP before changing route
update_last_ip() {
LAST_IP=$(get_public_ip)
}
# Restart Cloudflared and check the exit code
restart_cloudflared() {
echo "Restarting Cloudflared..."
if /opt/CloudFlared/CloudFlared.sh restart; then
echo "Cloudflared restarted successfully"
sleep 1
else
echo "Error: Failed to restart Cloudflared"
sleep 1
# Retry restarting Cloudflared
for i in {1..3}; do
echo "Attempt $i to kill and restart Cloudflared..."
pkill -f cloudflared # Kill all cloudflared processes
sleep 1 # Wait a bit before restarting
if /opt/CloudFlared/CloudFlared.sh restart; then
echo "Cloudflared restarted successfully on attempt $i"
break
else
echo "Error: Failed to restart Cloudflared on attempt $i"
fi
done
# return error if failed after 3 attempts
if [ $i -eq 3 ]; then
echo "Error: Failed to restart Cloudflared after 3 attempts"
return 1
fi
fi
}
# Switch to specified VPN interface
switch_to_vpn() {
interface=$1
echo "Switching to VPN route using interface $interface..."
if ! check_interface "$interface"; then
return 1
fi
update_last_ip
ip route del default 2>/dev/null || true
if ip route add default dev "$interface"; then
update_default_route
echo "Successfully switched to VPN route"
sleep 1
# Update ROUTE_DEVICE in cron_default_route_changed.sh on 1Feb.2025
sed -i "s/^ROUTE_DEVICE=.*/ROUTE_DEVICE=\"$interface\"/" /share/Multimedia/2024-MyProgramFiles/29.QTS_conf_files/cron/cron_default_route_changed.sh
echo "Updated ROUTE_DEVICE in cron_default_route_changed.sh to $interface"
sleep 1
# Restart Cloudflared
restart_cloudflared
sleep 2
if get_public_ip >/dev/null; then
echo "Connection verified"
sleep 1
return 0
else
echo "Warning: Cannot verify connection, but route is set"
sleep 1
return 0
fi
else
echo "Error: Failed to add VPN route"
restore_default
sleep 1
return 1
fi
}
# Restore default route
restore_default() {
echo "Restoring default route..."
update_last_ip
ip route del default 2>/dev/null || true
if ip route add default via $DEFAULT_GATEWAY dev $DEFAULT_INTERFACE; then
update_default_route
echo "Successfully restored default route"
# Update ROUTE_DEVICE in cron_default_route_changed.sh on 1Feb.2025
sed -i "s/^ROUTE_DEVICE=.*/ROUTE_DEVICE=\"$interface\"/" /share/Multimedia/2024-MyProgramFiles/29.QTS_conf_files/cron/cron_default_route_changed.sh
echo "Updated ROUTE_DEVICE in cron_default_route_changed.sh to 192.168.1.254"
sleep 1
# Restart Cloudflared
restart_cloudflared
sleep 2
if get_public_ip >/dev/null; then
echo "Connection verified"
sleep 1
return 0
else
echo "Warning: Cannot verify connection, but route is set"
sleep 1
return 0
fi
else
echo "Error: Failed to restore default route"
sleep 1
return 1
fi
}
# Get location for VPN interfaces
get_vpn_location() {
case "$1" in
"wgc3001") echo "JPN";;
"wgc3002") echo "US.Texas1";;
"wgc3003") echo "US.Washington";;
"tun2001") echo "JP.Tokyo";;
"tun2002") echo "US.WestCoast";;
*) echo "Unknown";;
esac
}
# Main loop
while true; do
clear
echo " VPN Route Switcher "
show_current_status
echo " Available Options:"
echo " 0) RESTORE DEFAULT ROUTE (192.168.1.254)"
# grep here
ip link show | grep ": $VPN_INTERFACES" | cut -d: -f2 | tr -d ' ' > /tmp/vpn_interfaces.tmp
i=1
while read -r interface; do
location=$(get_vpn_location "$interface")
printf " %d) %-10s [%s]\n" "$i" "$interface" "$location"
i=$((i + 1))
done < /tmp/vpn_interfaces.tmp
echo "$DIV_LINE"
echo " Enter number to select (99/q/Q to exit)"
echo " Enter key to refresh"
echo "$TOP_LINE"
printf " Select → "
read choice
if [ "$choice" = "99" ] || [ "$choice" = "q" ] || [ "$choice" = "Q" ]; then
echo " Exiting..."
rm -f /tmp/vpn_interfaces.tmp
exit 0
fi
case $choice in
''|*[!0-9]*)
echo " Please enter a number between 0 and 99, or q/Q to exit"
sleep 2
continue
;;
*)
if [ "$choice" -gt 99 ]; then
echo " Please enter a number between 0 and 99"
sleep 2
continue
fi
;;
esac
if [ "$choice" = "0" ]; then
if ! restore_default; then
echo "Press any key to continue..."
read -n 1
fi
else
selected_interface=$(sed -n "${choice}p" /tmp/vpn_interfaces.tmp)
if [ -n "$selected_interface" ]; then
location=$(get_vpn_location "$selected_interface")
echo "Switching to $selected_interface [$location]"
if ! switch_to_vpn "$selected_interface"; then
echo "Press any key to continue..."
read -n 1
fi
else
echo " Invalid selection: no interface for number $choice"
sleep 2
continue
fi
fi
rm -f /tmp/vpn_interfaces.tmp
done
总结:
在脚本里:
# Get location for VPN interfaces
get_vpn_location() {
case "$1" in
"wgc3001") echo "JPN";;
"wgc3002") echo "US.1";;
"wgc3003") echo "US.2";;
*) echo "Unknown";;
esac
}
# Store interfaces in a temporary file for easy access
ip link show | grep ': wgc' | cut -d: -f2 | tr -d ' ' > /tmp/vpn_interfaces.tmp
这两部分内容,要依据自身的情况来修改。前者是:对照表;后者是:提取关键词,现在用的是 wgc
/tmp/vpn_interfaces.tmp 如果你的系统里已经有同名文件在使用,同样要修改。
默认网关是 192.168.1.254 网口是 br0 ,也要根据环境修改。
添加:
on 1Feb.2025
当使用 routeswitcher.sh 修改 default route 后,同时修改 cron_default_route_changed.sh 中的 全局变量 ROUTE_DEVICE= 值
cron_default_route_changed.sh 是运行在 crontab 中,用来修改默认路由。
这个修改使用硬代码,存储文件路径:
在 sed 命令中使用,文件位置:/share/Multimedia/2024-MyProgramFiles/29.QTS_conf_files
因为使用的是 free tier of CloudFlared tunnel, 路由切换不到自动变化。 调用命令执行进程重启
路由表,内容有重复,修改 sed 内容
更新以下:
修改 switch_to_vpn()
添加 switch_to_vpn()
修改 restore_default()
脚本已经同步为最新 version 1.9 on 1Feb.2025
on 2Feb.2025
修改了切换路由时的逻辑:
在切换后,先检测公网 IP,如果成功再执行 Cloudflared tunnel 切换,修改 cron 文件。
在切换后,检测不到公网 IP, 就回退。
Version 2.0 on 2Feb.2025
switch_to_vpn() {
interface=$1
echo "Switching to VPN route using interface $interface..."
if ! check_interface "$interface"; then
return 1
fi
# 检测当前网络连接状态
echo "Checking network connectivity..."
if ! get_public_ip >/dev/null; then
echo "Warning: Cannot get public IP. Network might be unstable."
echo "Attempting to restore default route first..."
# 尝试恢复到默认路由
ip route del default 2>/dev/null || true
ip route add default via $DEFAULT_GATEWAY dev $DEFAULT_INTERFACE
sleep 2
# 验证默认路由是否恢复连接
if ! get_public_ip >/dev/null; then
echo "Error: Cannot restore network connectivity with default route"
return 1
fi
echo "Successfully restored default route connectivity"
fi
update_last_ip
ip route del default 2>/dev/null || true
if ip route add default dev "$interface"; then
update_default_route
echo "Successfully switched to VPN route"
# 检查新路由是否可以访问互联网
echo "Verifying new route connectivity..."
sleep 2
if ! get_public_ip >/dev/null; then
echo "Error: Cannot verify connection after route change"
echo "Rolling back to default route..."
# 回滚到默认路由
ip route del default 2>/dev/null
ip route add default via $DEFAULT_GATEWAY dev $DEFAULT_INTERFACE
echo "Rolled back to default route"
sleep 2
return 1
fi
# 如果连接正常,继续执行其他操作
echo "Connection verified successfully"
sleep 1
# Update ROUTE_DEVICE in cron_default_route_changed.sh
sed -i "s/^ROUTE_DEVICE=.*/ROUTE_DEVICE=\"$interface\"/" /share/Multimedia/2024-MyProgramFiles/29.QTS_conf_files/cron/cron_default_route_changed.sh
echo "Updated ROUTE_DEVICE in cron_default_route_changed.sh to $interface"
sleep 1
# Restart Cloudflared
restart_cloudflared
return 0
else
echo "Error: Failed to add VPN route"
restore_default
sleep 1
return 1
fi
}
# Restore default route
restore_default() {
echo "Restoring default route..."
# 先检测当前网络连接状态
echo "Checking network connectivity..."
if ! get_public_ip >/dev/null; then
echo "Error: Cannot get public IP. Network might be unstable."
echo "Aborting route restoration..."
return 1
fi
update_last_ip
ip route del default 2>/dev/null || true
if ip route add default via $DEFAULT_GATEWAY dev $DEFAULT_INTERFACE; then
update_default_route
echo "Successfully restored default route"
# 检查新路由是否可以访问互联网
echo "Verifying new route connectivity..."
sleep 2
if ! get_public_ip >/dev/null; then
echo "Error: Cannot verify connection after route change"
echo "Please check your network configuration manually"
return 1
fi
# 如果连接正常,继续执行其他操作
echo "Connection verified successfully"
sleep 1
# Update ROUTE_DEVICE in cron_default_route_changed.sh
sed -i "s/^ROUTE_DEVICE=.*/ROUTE_DEVICE=\"$DEFAULT_INTERFACE\"/" /share/Multimedia/2024-MyProgramFiles/29.QTS_conf_files/cron/cron_default_route_changed.sh
echo "Updated ROUTE_DEVICE in cron_default_route_changed.sh to $DEFAULT_INTERFACE"
sleep 1
# Restart Cloudflared
restart_cloudflared
return 0
else
echo "Error: Failed to restore default route"
sleep 1
return 1
fi
}