打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

使用搬瓦工api操作nftables实现更新白名单ip

来自md5.pw
EliToviyah留言 | 贡献2026年1月26日 (一) 03:31的版本 (修改一些顺序)

请注意不要泄漏API_Key 任何时候都不要泄露 防止服务器和API被盗用 应当在安全的设备上使用API 防止API_Key被盗取

根据可实时知晓ip变化的办法不同 您需要的工具也不相同..最多需要多一台本地的Linux服务器(不是防火墙所在的那个服务器哦)

使用前准备

切换到root或有高权限的用户

输入whoami(蓝色输出)或者查看 @之前的名字(绿色框)就是使用的用户

如果是root就是root就可以了

如果不是

你输入 nft list ruleset 或者 sudo nft list ruleset 输入你的密码 和上图一样 说明没权限….

尝试切换到root账户

输入 su - 然后输入 root 用户的密码。输入的密码不会显示 输入好后按回车就行 变成root就说明成功了




防火墙部分

输入 sudo nft list ruleset

你的防火墙中应该有一个空白的链 专用户白名单 然后在含有粉色框框(主要是含有hook input就行)代码的链中 jump这个链 另外先不要改成policy drop;

如果没有链的话 如何创建链

sudo nft add chain inet my_firewall allowed_ip

请根据自己的表的类型inet 表的名字 my_firewall 自行修改哦

allowed_ip可以改成自己想要的名字 比如sudo nft add chain inet my_firewall whiteip

要先有链 才能jump 链的名字

如何添加一条jump allowed_ip 规则呢

sudo nft add rule inet my_firewall my_input jump allowed_ip

还是一样要修改成自己的表的类型 表的名字 刚刚创建的链的名字

要把这个jump allowed_ip 添加到 含有粉色代码的chain里面也就是(my_input)

服务器部分

创建一个脚本 用 API 调用这个脚本就好了 免去了 API中过多的指令

如果你是从 服务器安全-防火墙 nftables 那文章一步一步 没有修改防火墙的表 链 的话 直接复制代码框的全部内容 粘贴到ssh里面 按回车 文件就创建好了

如果你修改了防火墙的表和链的名字 或不是根据 那篇文章来的

请修改下面代码中的内容 根据自身情况修改 只需要修改=后面 “” 双引号里面的内容


  • TABLE_NAME="my_firewall"
    • 这个是表的名字 蓝色框框
  • CHAIN_NAME="allowed_ip"
    • 这个是链的名字 绿色框框 这个链的名字一定不是你 包含 hook input 粉色这一行的那个链..因为会被清空..然后就会可能导致防火墙规则混乱…一定要是另外一个专门的链
  • FAMILY="inet"
    • 这个是表类型 红色框框

示例

如示例中的图就要改成这样的

CHAIN_NAME="allowed_mobile" TABLE_NAME="filter"

FAMILY="inet" 不变

如果是table ip filter就把FAMILY="inet" 改成 FAMILY="ip"

这里还应该输入 ls /root/update_fw.sh

检查/root目录是否有一个叫update_fw.sh的文件 如果有的话你也需要修改 避免被覆盖

上面 蓝色框框的 说明 有存在的同名文件 那你就需要修改 成 > /root/别的文件名.sh那么下文中的所有/root/update_fw.sh都要改成你自己的文件名

下面红色框框说明没有找到这个文件就 不用改名了 直接使用就行



在下面的代码框里的对应位置改 可以先复制到记事本上 改完再全选重新复制 粘贴到ssh里面 回车


代码框的脚本如下:

cat << 'EOF' > /root/update_fw.sh
#!/bin/bash

# --- 用户配置区 ---
TABLE_NAME="my_firewall"
CHAIN_NAME="allowed_ip"
FAMILY="inet"

# --- 获取参数并剔除所有空格 ---
IP1=$(echo "$1" | tr -d '[:space:]')
IP2=$(echo "$2" | tr -d '[:space:]')


VALID_IPS=()
NFT_TYPES=()
SUCCESS_LIST=""
FAILED_LIST=""

# --- IP 校验函数 ---
check_ip() {
    local input=$1
    [[ -z "$input" ]] && return 1
    
    local raw_ip="${input%/*}"
    local mask="${input#*/}"
    
    # 自动识别默认掩码
    if [[ "$input" != */* ]]; then
        if [[ "$raw_ip" =~ : ]]; then mask=128; else mask=32; fi
    fi

    # 1. IPv4 校验
    # 原来的 2[0-4][0-5] 改为了 2[0-4][0-9]
    if [[ "$raw_ip" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]; then
        [[ "$mask" -lt 1 || "$mask" -gt 32 ]] && return 1
        echo "ip|$raw_ip/$mask"
        return 0
    fi

    # 2. IPv6 校验 (简化版正则,兼容性更好)
    if [[ "$raw_ip" =~ ^(([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:(:[0-9a-fA-F]{1,4}){1,7}|::|([0-9a-fA-F]{1,4}:){1,7}:[0-9a-fA-F]{1,4})$ ]]; then
        [[ "$mask" -lt 1 || "$mask" -gt 128 ]] && return 1
        echo "ip6|$raw_ip/$mask"
        return 0
    fi

    return 1
}

process_arg() {
    local arg=$1
    [ -z "$arg" ] && return
    local res
    res=$(check_ip "$arg")
    if [ $? -eq 0 ]; then
        local full_ip="${res#*|}"
        VALID_IPS+=("$full_ip")
        NFT_TYPES+=("${res%|*}")
        SUCCESS_LIST+="$full_ip "
    else
        FAILED_LIST+="$arg "
    fi
}

process_arg "$IP1"
process_arg "$IP2"

# --- 极简反馈逻辑 ---
if [ ${#VALID_IPS[@]} -eq 0 ]; then
    echo "Error: No valid IP provided ($FAILED_LIST)"
    exit 1
fi

# 执行 Nftables 操作
nft flush chain $FAMILY $TABLE_NAME $CHAIN_NAME || { echo "Error: NFT chain not found"; exit 1; }

for i in "${!VALID_IPS[@]}"; do
    nft add rule $FAMILY $TABLE_NAME $CHAIN_NAME ${NFT_TYPES[$i]} saddr ${VALID_IPS[$i]} accept
done

# 成功返回
echo "OK: Allowed $SUCCESS_LIST"
EOF

解释一下代码框里面指令的意思

  • cat (Concatenate):原本是用来“查看”或“拼接”内容的,但在这种组合中,它负责接收你输入的一大段文字。
  • << (输入重定向):这叫“在此处开始读取”。它告诉系统:“别去翻别的文件了,接下来的内容就是我要给你的,直到我遇到结束标记为止。”
  • 'EOF' (End Of File)
    • 开始标志:它是你自定义的一个“暗号”。
    • 为什么要加单引号 ' ': 加了单引号,系统就会“原样搬运”中间的内容,不会去解析里面的 $变量。这对于写入包含变量的脚本至关重要,否则这些变量在写入文件前就会被当前系统搞乱。
  • > (覆盖写入):它的意思是“清空目标文件并把内容倒进去”。
  • /root/update_fw.sh:这是目的地。系统会在指定路径创建或覆盖这个脚本文件。
  • 中间的内容 就是要写入文件的内容 是我们的核心代码
  • 最后的 EOF:这是“结束暗号”。系统看到它,就知道“打包”结束了,正式保存文件。

我们可以看到最后蓝色框的时候 有点混乱 没事的 直接按回车就行了

然后我们可以输入 cat /root/update_fw.sh 查看一下文件内容 确认有写入内容就好了

创建好后


你可以先输入 ls -l /root/update_fw.sh

这个意思是 以长格式 查看/root/update_fw.sh 文件的信息

  • 第一位 - 代表是文件的意思 d是目录
  • 第二位到第四位 rw- 就是 拥有这个文件的人的权限 第一个代表的是读取 如果有这个权限显示r如果没有就是-第二个是写入(编辑)的权限有这个权限就显示w没有就是-第三位是执行(运行)这个文件的权限有就显示x没有就显示- 这里显示-证明在chmod之前是没有执行权限的
  • r-- 这个文件所属组 的权限 同上 第一位是读取r说明有 第二位是-说明没有写入权限 第三位是-说明没有执行权限
  • r-- 这个是其他人 也是一样的 有读取权限 没有写入和执行权限
  • 1 代表这个文件在磁盘上只有这一个‘
  • root root 第一个root 代表拥有这个文件的人 第二个 root 代表这个文件属于root组
  • 2675 文件大小Byte
  • Jan 22 01:07 修改时间
  • /root/update_fw.sh 文件信息的文件

为了安全取消掉其他的权限我们输入

chmod 700 /root/update_fw.sh

这个指令的意思是 设置这个文件的权限是700 700的意思就是只有这个文件的拥有人(root ls -l看到的)才可以读取写入和执行 其他的两个0代表 所属组没有任何权限和 其他所有人没有任何权限。

这样,只有 root 用户能看到这个脚本的内容,也只有 root 能运行它。


调用API部分

获取 搬瓦工服务器的API_Key和veid

请参考 搬瓦工api使用


使用搬瓦工API触发服务器上的脚本变更防火墙nftables的chain 从而实现动态更新白名单ip

ipv6的网段 V6_MASK

这个..如果你想同时更新ipv6的白名单 有一个问题

就是ipv6 每个设备都有一个ipv6地址(各不相同) ipv4的时候你用电脑手机连接到同一个路由器的话一般都是走的同一个ipv4地址 而ipv6不同…所以如果你只是更新了你这个设备的ipv6地址的话…别的设备仍然不在白名单中…这就需要网段

请登录路由器

找到这个前缀长度…把V6_MASK改成60就行了这样就是说 连接路由器的 都可以进白名单了

所有的代码中都有一个可以修改的变量.. V6_MASK="/64" 单个设备请改为 /128 一些地区可能是48 但48有点危险范围太大 你的邻居可能也在这之中 那为什么是64呢 因为我的是64……哦 我记错了 我是60….

可实时更新ip变化的办法

Linux系

OpenWrt类

检查是否安装了curl

输入 curl 如果显示 -ash: curl not found

安装curl和证书 为了访问https网站

opkg update && opkg install curl ca-bundle

curl 8.12.1有这样的版本号 有IPv6 有ssl 确保有他们就可以啦

检查 获取ip 的url是否有效

在使用前先在ssh里面输入

curl -s --max-time 5 https://ifconfig.me 进行测试

看看能不能获取ip地址 红色方块里面的就是ip地址 如果不能….

在后续的代码块中修改这两个=后面的内容

CHECK_URL_V4="https://ifconfig.me"

改成

CHECK_URL_V4=”https://ident.me”

CHECK_URL_V6="https://ifconfig.me"

改成

CHECK_URL_V6=” http://v6.ipip.net”

开始

在 OpenWrt 中,每当接口(WAN)状态发生变化(比如重连、获取到新 IP、掉线)时,系统会自动触发对应的脚本。 OpenWrt 的网络热插拔脚本存放在: /etc/hotplug.d/iface/

进入目录:cd /etc/hotplug.d/iface/

创建脚本:

修改下面代码 你可以先复制到记事本或者别的文本编辑工具里面 最前面的

export VEID="你的VEID" export API_KEY="你的API_KEY"

改成类似这样的格式

export VEID="10202" export API_KEY="private_xxxxx"

请根据你实际的veid和API_Key修改

这两个如果你知道干嘛的也可以改

保存上传的ip防止重复调用

CACHE_FILE="/etc/config/vps_last_ip"

把执行过程和结果保存到这个文件里

API LOG_FILE="/tmp/vps_sync_error.log"

最后要检查的 输入 ls /etc/hotplug.d/iface/99-sync-vps-firewall 检查一下 有没有同名的其他脚本

下图就是没有 就不需要修改

下图就是有 那么你就要换一个名字

/etc/hotplug.d/iface/99-这里改成你想要的名字

比如 /etc/hotplug.d/iface/99-sync-vps-firewall1

修改后…复制粘贴到ssh里面 回车就创建好脚本了

代码块如下:

cat << 'EOF' > /etc/hotplug.d/iface/99-sync-vps-firewall
#!/bin/sh

# 只要是接口启动(ifup)就触发
[ "$ACTION" = "ifup" ] || exit 0

# --- 用户配置区 ---
export VEID="你的VEID"
export API_KEY="你的API_KEY"

# 用户可以分别设置获取地址(可以相同,也可以不同)
CHECK_URL_V4="https://ifconfig.me"
CHECK_URL_V6="https://ifconfig.me"

V6_MASK="/64"  # IPv6 掩码
CACHE_FILE="/etc/config/vps_last_ip"
LOG_FILE="/tmp/vps_sync.log"
MAX_LINES=100

# --- 文件初始化与清理函数 ---
init_files() {
    sleep 10
    # 1. 处理日志文件
    if [ ! -f "$LOG_FILE" ]; then
        touch "$LOG_FILE"
        chmod 644 "$LOG_FILE"
        echo "$(date): [系统] 初始日志文件已创建" >> "$LOG_FILE"
    else
        # 日志滚动清理逻辑
        local current_lines=$(wc -l < "$LOG_FILE")
        if [ "$current_lines" -gt "$MAX_LINES" ]; then
            echo "$(tail -n 50 "$LOG_FILE")" > "$LOG_FILE"
            echo "$(date): [系统] 日志滚动清理完成" >> "$LOG_FILE"
        fi
    fi

    # 2. 处理缓存文件及其目录
    local cache_dir=$(dirname "$CACHE_FILE")
    if [ ! -d "$cache_dir" ]; then
        mkdir -p "$cache_dir"
        echo "$(date): [系统] 创建缓存目录: $cache_dir" >> "$LOG_FILE"
    fi

    if [ ! -f "$CACHE_FILE" ]; then
        touch "$CACHE_FILE"
        # 初始化空变量,防止脚本第一次读取时报错
        echo "LAST_IP4=\"\"" > "$CACHE_FILE"
        echo "LAST_IP6=\"\"" >> "$CACHE_FILE"
        echo "$(date): [系统] 初始缓存文件已创建" >> "$LOG_FILE"
    fi
}

# --- IPv4 暴力获取函数 ---
get_v4() {
    local count=0
    while [ $count -lt 10 ]; do
        local res=$(curl -s --max-time 5 "$CHECK_URL_V4" 2>/dev/null)
        if echo "$res" | grep -Eq "^([0-9]{1,3}\.){3}[0-9]{1,3}$"; then
            echo "$res" && return 0
        fi
        count=$((count + 1))
        sleep 2
    done
    return 1
}

# --- IPv6 暴力获取函数 ---
get_v6() {
    local count=0
    while [ $count -lt 10 ]; do
        local res=$(curl -s --max-time 5 "$CHECK_URL_V6" 2>/dev/null)
        if echo "$res" | grep -q ":"; then
	          echo "${res}${V6_MASK}"
            return 0
        fi
        count=$((count + 1))
        sleep 1
    done
    
    ip6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | grep -E '^(2|3)' | head -n 1)
    if [ -n "$ip6" ]; then
        echo "${ip6}${V6_MASK}"
        return 0
    fi
    return 1
}

# 1. 初始化
init_files

# 2. 执行双栈并行/独立获取
CURRENT_IP4=$(get_v4)
CURRENT_IP6=$(get_v6)

# 3. 基础校验:至少得有一个 IP 吧
if [ -z "$CURRENT_IP4" ] && [ -z "$CURRENT_IP6" ]; then
    echo "$(date): [错误] 尝试10次后仍无法获取到任何有效IP" >> "$LOG_FILE"
    exit 1
fi

# 4. 缓存对比 (防重复刷 API)
[ -f "$CACHE_FILE" ] && . "$CACHE_FILE"
if [ "$CURRENT_IP4" = "$LAST_IP4" ] && [ "$CURRENT_IP6" = "$LAST_IP6" ]; then
    exit 0
fi

# 5. 调用 API
CMD="bash /root/update_fw.sh $CURRENT_IP4 $CURRENT_IP6"
RESPONSE_FULL=$(curl -s -w "%{http_code}" -X POST \
    --data "veid=${VEID}&api_key=${API_KEY}" \
    --data-urlencode "command=${CMD}" \
    "https://api.64clouds.com/v1/basicShell/exec")

# 6. 解析结果
HTTP_CODE="${RESPONSE_FULL:${#RESPONSE_FULL}-3}"
RESPONSE_JSON="${RESPONSE_FULL:0:${#RESPONSE_FULL}-3}"
RESPONSE_CLEAN=$(echo "$RESPONSE_JSON" | tr -d '\n\r ')
ERROR_CODE=$(echo "$RESPONSE_CLEAN" | grep -o '"error":[0-9]*' | cut -d: -f2)
MESSAGE=$(echo "$RESPONSE_CLEAN" | grep -o '"message":"[^"]*"' | sed 's/"message":"//;s/"$//')

# 7. 写入中文日志
if [ "$HTTP_CODE" = "200" ] && [ "$ERROR_CODE" = "0" ]; then
    echo "LAST_IP4=\"$CURRENT_IP4\"" > "$CACHE_FILE"
    echo "LAST_IP6=\"$CURRENT_IP6\"" >> "$CACHE_FILE"
    echo "$(date): [成功] IP变更同步成功。IPv4: $CURRENT_IP4, IPv6: $CURRENT_IP6. VPS反馈: $MESSAGE" >> "$LOG_FILE"
    echo "同步成功: $MESSAGE"
else
    [ -z "$MESSAGE" ] && MESSAGE="请求异常: $RESPONSE_JSON"
    echo "$(date): [失败] 同步失败。详细原因: $MESSAGE" >> "$LOG_FILE"
    echo "同步失败: $MESSAGE"
fi
EOF

就像这样粘贴后回车就行


输入chmod 755 /etc/hotplug.d/iface/99-sync-vps-firewall赋予执行权限

输入 ls -l /etc/hotplug.d/iface/99-sync-vps-firewall 查看赋予权限是否成功

这样你可以重启 试试看… 可以输入

cat /tmp/vps_sync_error.log

查看是否成功

前面的蓝色框框说成功指的是API成功了 里面的ip是本地上传给服务器更新的ip 后面红色框框 OK才是防火墙成功的意思 后面的ip就是更新的ip 如果有一个ip无效或者…就不会显示了

常见错误

身份验证失败 意思就是说 veid 或者 api_key 的内容错误

没有找到链

你可以看到 蓝色框的是防火墙指令..红色框是表

可能是 表(inet my_firewall) 或者 链(allowed_ip) 的名字和服务器防火墙的对不上 你可以去服务器nft list ruleset看一下

华硕路由器 官方固件

因为官方固件没有提供 重新连接网络 后执行脚本的功能 所以我们需要一台本地的Linux服务器了(不是防火墙所在的那个服务器哦)…

查看Linux服务器的ip (本地的局域网内的)

ssh连接的那个ip就是了…

如果你忘记了 输入 ip addr 找个eth0或者 ens18 这样 一般都是默认网络接口 里面的ip地址 蓝色框框

借助监听远程日志触发API更新防火墙白名单IP

用浏览器登陆你的路由器后台 输入用户名和密码 一般的地址是 192.168.1.1 如果不是可以打开 http://www.asusrouter.com/ 试试

找到系统记录 单击

远程记录服务器 输入 你的 本地局域网linux服务器的ip


远程记录服务器端口 默认514 如果你的linux服务器占用514的话 就改一下

如何查看是否占用

ssh连接那台服务器 切换root账户

  • 输入命令:su -(注意:su 后面有个空格和减号,这很重要,代表同时切换环境变量)
  • 输入 Root 密码(输入时看不见)。
  • 此时你的提示符会变成 #,代表你又是 Root 了。

输入 sudo ss -tunlp | grep :514

如果你没有安装sudo就会提示

那就不需要输入sudo 了 直接

ss -tunlp | grep :514


这样有输出 就是被占用了 占用的话换一个就行了


这样没有输出 就是没占用

然后点击 应用本页面设置

另外一台Linux服务器部分 不是防火墙所在的那个服务器哦

ssh连接后

切换到root账户
  • 输入命令:su -(注意:su 后面有个空格和减号,这很重要,代表同时切换环境变量)
  • 输入 Root 密码(输入时看不见)。
  • 此时你的提示符会变成 #,代表你又是 Root 了。

安装socat

socat的功能很强大 是一个多功能的网络工具 我们这里用于接收路由器发过来的日志…

Debian / Ubuntu / PVE / Armbian 系:

apt update && apt install socat -y

Red Hat / CentOS / Rocky Linux 系:

yum install socat -y

新一些Red Hat/CentOS/Rocky Linux希的系统可以使用dnf install socat -y

安装后 输入 socat -V 确认安装成功

检查 获取ip 的url是否有效

在使用前先在ssh里面输入

curl -s --max-time 5 https://ifconfig.me 进行测试

看看能不能获取ip地址 红色方块里面的就是ip地址 如果不能….

在代码块中修改这两个=后面的内容

CHECK_URL_V4="https://ifconfig.me"

改成

CHECK_URL_V4=”https://ident.me”

CHECK_URL_V6="https://ifconfig.me"

改成

CHECK_URL_V6=” http://v6.ipip.net”

创建脚本

修改下面代码块 的代码 你可以复制到记事本或者其他文本编辑器里面 修改

最前面的

export VEID="你的VEID" export API_KEY="你的API_KEY"

改成类似这样的格式

export VEID="10202" export API_KEY="private_xxxxx"

请根据你实际的veid和API_Key修改

这两个如果你知道干嘛的也可以改 CACHE_FILE="/etc/config/vps_last_ip" 保存上传的ip防止重复调用API LOG_FILE="/tmp/vps_sync_error.log" 把执行过程和结果保存到这个文件里

根据刚才测试的结果来决定要不要替换

CHECK_URL_V4="https://ifconfig.me" CHECK_URL_V6="https://ifconfig.me"

V6_MASK="/64" 也可以修改 详情看上方的 ipv6的网段 V6_MASK

最后要检查的 输入 ls /root/checkip.sh 检查一下 有没有同名的其他脚本

下图就是没有 就不需要修改

下图这样就是有 就需要修改/root/checkip.sh 比如修改成 /root/checkip1.sh

修改后

全部选择 复制粘贴 到ssh里面 按回车


代码块:

cat << 'EOF' > /root/checkip.sh
#!/bin/bash

# --- 用户配置区 ---
export VEID="你的VEID"
export API_KEY="你的API_KEY"

# 获取 IP 的地址
CHECK_URL_V4="https://ifconfig.me"
CHECK_URL_V6="https://ifconfig.me"

V6_MASK="/64"  # IPv6 掩码
CACHE_FILE="/etc/config/vps_last_ip"
LOG_FILE="/var/log/vps_sync.log"
MAX_LINES=100  # 日志保留行数

# --- 1. 初始化文件与目录 ---
init_files() {
    # 处理日志文件
    if [ ! -f "$LOG_FILE" ]; then
        touch "$LOG_FILE"
        chmod 644 "$LOG_FILE"
    else
        # 日志滚动清理
        local current_lines=$(wc -l < "$LOG_FILE")
        if [ "$current_lines" -gt "$MAX_LINES" ]; then
            echo "$(tail -n 100 "$LOG_FILE")" > "$LOG_FILE"
            echo "$(date): [系统] 日志滚动清理完成 (保留末尾100行)" >> "$LOG_FILE"
        fi
    fi

    # 处理缓存目录及文件
    local cache_dir=$(dirname "$CACHE_FILE")
    [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir"
    
    if [ ! -f "$CACHE_FILE" ]; then
        echo "LAST_IP4=\"\"" > "$CACHE_FILE"
        echo "LAST_IP6=\"\"" >> "$CACHE_FILE"
    fi
}

# --- 2. IPv4 暴力获取函数 ---
get_v4() {
    local count=0
    while [ $count -lt 10 ]; do
        local res=$(curl -s4 --max-time 5 "$CHECK_URL_V4" 2>/dev/null)
        if echo "$res" | grep -Eq "^([0-9]{1,3}\.){3}[0-9]{1,3}$"; then
            echo "$res" && return 0
        fi
        count=$((count + 1))
        sleep 2
    done
    return 1
}

# --- 3. IPv6 暴力获取函数 ---
get_v6() {
    local count=0
    while [ $count -lt 10 ]; do
        local res=$(curl -s6 --max-time 5 "$CHECK_URL_V6" 2>/dev/null)
        if echo "$res" | grep -q ":"; then
            echo "${res}${V6_MASK}"
            return 0
        fi
        count=$((count + 1))
        sleep 4
    done
    
    ip6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | grep -E '^(2|3)' | head -n 1)
    if [ -n "$ip6" ]; then
        echo "${ip6}${V6_MASK}"
        return 0
    fi
    
    return 1
}

# --- 4. 核心同步逻辑 ---
sync_ip() {

    # 刚收到日志时,给网络层 5-10 秒的初始化时间
    sleep 10
    
    # 获取当前 IP
    CURRENT_IP4=$(get_v4)
    CURRENT_IP6=$(get_v6)

    # 基础校验
    if [ -z "$CURRENT_IP4" ] && [ -z "$CURRENT_IP6" ]; then
        echo "$(date): [错误] 尝试10次后仍无法获取到任何有效IP" >> "$LOG_FILE"
        return 1
    fi

    # 读取缓存并对比
    . "$CACHE_FILE"
    if [ "$CURRENT_IP4" = "$LAST_IP4" ] && [ "$CURRENT_IP6" = "$LAST_IP6" ]; then
        echo "$(date): [跳过] IP 未发生变化,取消同步" >> "$LOG_FILE"
        return 0
    fi

    # 调用 API
    CMD="bash /root/update_fw.sh $CURRENT_IP4 $CURRENT_IP6"
    local RESPONSE_FULL=$(curl -s -w "%{http_code}" -X POST \
        --data "veid=${VEID}&api_key=${API_KEY}" \
        --data-urlencode "command=${CMD}" \
        "https://api.64clouds.com/v1/basicShell/exec")

    # 解析结果
    local HTTP_CODE="${RESPONSE_FULL:${#RESPONSE_FULL}-3}"
    local RESPONSE_JSON="${RESPONSE_FULL:0:${#RESPONSE_FULL}-3}"
    
    # 核心解析:压缩 JSON 并提取字段
    local CLEAN_JSON=$(echo "$RESPONSE_JSON" | tr -d '\n\r' | tr -s ' ')
    local ERROR_CODE=$(echo "$CLEAN_JSON" | sed -n 's/.*"error":[ ]*\([0-9]*\).*/\1/p')
    local MESSAGE=$(echo "$CLEAN_JSON" | sed -n 's/.*"message":[ ]*"\(.*\)"[ ]*}.*/\1/p' | sed 's/\\//g')

    # 写入日志
    if [ "$HTTP_CODE" = "200" ] && [ "$ERROR_CODE" = "0" ]; then
        echo "LAST_IP4=\"$CURRENT_IP4\"" > "$CACHE_FILE"
        echo "LAST_IP6=\"$CURRENT_IP6\"" >> "$CACHE_FILE"
        echo "$(date): [成功] 同步成功。IPv4: $CURRENT_IP4, IPv6: $CURRENT_IP6. VPS反馈: $MESSAGE" >> "$LOG_FILE"
    else
        [ -z "$MESSAGE" ] && MESSAGE="$CLEAN_JSON"
        echo "$(date): [失败] 同步失败。HTTP: $HTTP_CODE, ERROR: $ERROR_CODE, 原因: $MESSAGE" >> "$LOG_FILE"
    fi
}

# --- 5. 监听入口 ---
init_files

# 如果直接运行脚本(无管道输入),执行一次同步
if [ -t 0 ]; then
    sync_ip
else
    # 如果有管道输入(来自 socat),则监听日志触发
    while IFS= read -r line; do
        if echo "$line" | grep -qi 'local *IP address'; then
            echo "$(date): [触发] 检测到路由器拨号日志" >> "$LOG_FILE"
            sync_ip
        fi
    done
fi
EOF

粘贴之后有点混乱 不用管 直接回车就行了

然后

检查一下 输入 cat /root/checkip.sh 有内容就好了

授予运行权限 chmod +x /root/checkip.sh

然后输入 ls -l /root/checkip.sh 查看多了三个x 就对了 如果像红色框框没有x 就不对 请重试 chmod +x /root/checkip.sh

然后你就可以输入

/root/checkip.sh

如果第一次测试成功了 以后想测试的话运行这个 rm /etc/config/vps_last_ip && /root/checkip.sh

需要rm /etc/config/vps_last_ip删除本地保留的ip记录 就是说如果这个ip记录和获取到的一样是不会运行api执行脚本的

测试一下 相当于是手动更新..不是自动更新

不会有内容输出

需要查看日志 cat /var/log/vps_sync.log

如果是下图那样就是成功了 要看红色的 红色框是vps服务器返回的OK 就是真的成功了 Allowed:后面是成功添加的ip 一个是ipv4 一个是ipv6的

蓝色框的成功说的是API使用成功 提交的IP是哪些


在此再次提醒您 使用时 保管好Veid和API_Key 你 看我打码服务器名字 然而还是在这里漏了...以前的打码就没意义了

常见错误

身份验证错误 一般代表 api_key 或 veid 的内容填写错误 就是= 后面的 "" 里面的内容 比如VEID="就这里错了" API_KEY="这里"

配置开机启动 和 自动更新

使用systemctl配置开机启动和 自动根据日志更新

修改以下代码中的

ExecStart=/bin/sh -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"

UDP-RECV:514 把514换成你的路由器那里设置的 远程记录服务器端口 比如 515 就改成 UDP-RECV:515


ExecStart=/bin/sh -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"

这里面的 /root/checkip.sh

如果你的文件名改了的话 就需要修改… 没改就不用改..

同理 /etc/systemd/system/router-log-watcher.service

这个地方你也应该输入 ls /etc/systemd/system/router-log-watcher.service

查看文件是否存在.如果存在就改名字 如果不存在就不用改了 和上面服务器部分的 ls /root/update_fw.sh 一样

修改好 全部选择 复制粘贴到ssh里面 然后按回车

cat << 'EOF' > /etc/systemd/system/router-log-watcher.service
[Unit]
Description=Router Log Watcher
After=network.target

[Service]
Type=simple
# 管道命令:socat 接收 UDP 514,然后传给脚本
ExecStart=/bin/bash -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"
Restart=always
RestartSec=5
# 如果脚本需要 root 权限,确保 User=root
User=root

[Install]
WantedBy=multi-user.target
EOF

这个不怎么混乱 直接回车就行

然后 cat /etc/systemd/system/router-log-watcher.service 如果你的文件名字 如果不是这个的话请修改

输入这个 意思是识别新服务

systemctl daemon-reload

在输入这个 意思是 启动并设置开机自启

systemctl enable --now router-log-watcher.service

最后输入

systemctl status router-log-watcher.service

蓝色的框 是 已经运行了 绿色的是enabled 是 设置了 开机自动启动

现在可以重启路由器获取新的ip试试了

输入 cat /var/log/vps_sync.log 这样就是成功了

如果ipv6获取的速度慢 可能出现防火墙没有允许ipv6的情况

遇到极端情况下 可以手动更新

rm -f /etc/config/vps_last_ip && /root/checkip.sh

删除存储的ip然后执行脚本 ip一直相同的话是不会调用api的

另外如果你修改了/root/checkip.sh 那么还要输入 systemctl restart router-log-watcher.service 重启这个服务才生效

如果成功更新ip了 就可以把允许ssh的端口删掉了就真正变成白名单模式了