<QNAP 453D QTS-5.x> 日志记录:使用脚本 routeswitch.sh (旧名 script: router_switcher.sh) 在 NAS 里切换默认路由

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
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值