vps按 frps 实际流量启动/停止 iperf3

一、vps1

#!/usr/bin/env bash
set -euo pipefail

# ==================================================
# 配置区:按需修改
# ==================================================

# VPS2 iperf3 服务端 IP
REMOTE_HOST="你的VPS2_IP"

# VPS2 iperf3 服务端口
IPERF_PORT=5201

# iperf3 测试限速:15 Mbps
LIMIT_MBIT=15

# iperf3 单次运行时长,脚本会根据 frps 是否空闲主动停止它
IPERF_DURATION=86400

# 需要监控的 frps 业务端口
# 不要写 iperf3 的 5201
FRPS_TCP_PORTS=(7000 80 443 6000 6001)

# 如果没有 UDP 映射,保持为空
FRPS_UDP_PORTS=()

# 每几秒检查一次 frps 流量
CHECK_INTERVAL=5

# 每 5 秒 frps 端口新增流量超过 5MB,才算有业务流量
ACTIVE_BYTES_THRESHOLD=5242880

# 连续多少秒没有达到流量阈值后,停止 iperf3
IDLE_STOP_SECONDS=60

# 日志文件
LOG_FILE="/var/log/frps-traffic-iperf3.log"

# systemd 服务名
SERVICE_NAME="frps-traffic-iperf3.service"

# 脚本安装路径
SCRIPT_PATH="/usr/local/bin/frps-traffic-iperf3.sh"

# iptables 监控链名称
CHAIN_NAME="FRPS_TRAFFIC_MON"

IPERF_PID=""

# ==================================================
# 通用函数
# ==================================================

log() {
    echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"
}

need_root() {
    if [ "$(id -u)" -ne 0 ]; then
        echo "请使用 root 运行"
        exit 1
    fi
}

check_command() {
    command -v "$1" >/dev/null 2>&1 || {
        echo "缺少命令: $1"
        echo "Debian/Ubuntu 可执行: apt update && apt install -y iperf3 iptables"
        exit 1
    }
}

# ==================================================
# iptables 流量监控
# ==================================================

setup_iptables_monitor() {
    log "初始化 frps 端口流量监控规则"

    iptables -N "$CHAIN_NAME" 2>/dev/null || true
    iptables -F "$CHAIN_NAME"

    for port in "${FRPS_TCP_PORTS[@]}"; do
        iptables -A "$CHAIN_NAME" -p tcp --sport "$port" -m comment --comment frpsmon -j RETURN
        iptables -A "$CHAIN_NAME" -p tcp --dport "$port" -m comment --comment frpsmon -j RETURN
    done

    for port in "${FRPS_UDP_PORTS[@]}"; do
        iptables -A "$CHAIN_NAME" -p udp --sport "$port" -m comment --comment frpsmon -j RETURN
        iptables -A "$CHAIN_NAME" -p udp --dport "$port" -m comment --comment frpsmon -j RETURN
    done

    iptables -A "$CHAIN_NAME" -j RETURN

    iptables -C INPUT -j "$CHAIN_NAME" 2>/dev/null || iptables -I INPUT 1 -j "$CHAIN_NAME"
    iptables -C OUTPUT -j "$CHAIN_NAME" 2>/dev/null || iptables -I OUTPUT 1 -j "$CHAIN_NAME"

    log "frps 端口流量监控规则已启用"
}

get_frps_bytes() {
    iptables -L "$CHAIN_NAME" -v -x -n 2>/dev/null \
        | awk '/frpsmon/ {sum += $2} END {print sum+0}'
}

remove_iptables_monitor() {
    iptables -D INPUT -j "$CHAIN_NAME" 2>/dev/null || true
    iptables -D OUTPUT -j "$CHAIN_NAME" 2>/dev/null || true
    iptables -F "$CHAIN_NAME" 2>/dev/null || true
    iptables -X "$CHAIN_NAME" 2>/dev/null || true
}

# ==================================================
# iperf3 控制
# ==================================================

iperf3_running() {
    [ -n "${IPERF_PID:-}" ] && kill -0 "$IPERF_PID" 2>/dev/null
}

start_iperf3() {
    if iperf3_running; then
        return 0
    fi

    log "检测到 frps 有实际流量,启动 iperf3"
    log "iperf3: VPS1 -> VPS2 ${REMOTE_HOST}:${IPERF_PORT}, TCP, ${LIMIT_MBIT} Mbps"

    iperf3 \
        -c "$REMOTE_HOST" \
        -p "$IPERF_PORT" \
        -b "${LIMIT_MBIT}M" \
        -t "$IPERF_DURATION" \
        -i 5 \
        >> "$LOG_FILE" 2>&1 &

    IPERF_PID=$!
    log "iperf3 已启动,PID=${IPERF_PID}"
}

stop_iperf3() {
    if iperf3_running; then
        log "frps 连续无有效流量,停止 iperf3,PID=${IPERF_PID}"
        kill "$IPERF_PID" 2>/dev/null || true
        wait "$IPERF_PID" 2>/dev/null || true
        IPERF_PID=""
        log "iperf3 已停止"
    fi
}

cleanup_runtime() {
    stop_iperf3
    log "脚本运行进程退出"
}

# ==================================================
# 主运行逻辑
# ==================================================

run_monitor() {
    need_root
    check_command iptables
    check_command iperf3
    check_command awk

    touch "$LOG_FILE"

    setup_iptables_monitor

    log "开始监控 frps 实际流量"
    log "VPS2 iperf3: ${REMOTE_HOST}:${IPERF_PORT}"
    log "iperf3 限速: ${LIMIT_MBIT} Mbps"
    log "TCP ports: ${FRPS_TCP_PORTS[*]:-none}"
    log "UDP ports: ${FRPS_UDP_PORTS[*]:-none}"
    log "检查间隔: ${CHECK_INTERVAL}s"
    log "流量阈值: ${ACTIVE_BYTES_THRESHOLD} bytes / ${CHECK_INTERVAL}s"
    log "空闲停止: ${IDLE_STOP_SECONDS}s"

    trap cleanup_runtime EXIT INT TERM

    prev_bytes=$(get_frps_bytes)
    idle_seconds=0

    while true; do
        sleep "$CHECK_INTERVAL"

        current_bytes=$(get_frps_bytes)
        delta=$((current_bytes - prev_bytes))
        prev_bytes="$current_bytes"

        if [ "$delta" -ge "$ACTIVE_BYTES_THRESHOLD" ]; then
            idle_seconds=0
            log "frps 有有效流量: ${delta} bytes / ${CHECK_INTERVAL}s"
            start_iperf3
        else
            idle_seconds=$((idle_seconds + CHECK_INTERVAL))
            log "frps 无有效流量: ${delta} bytes / ${CHECK_INTERVAL}s, idle=${idle_seconds}s"

            if [ "$idle_seconds" -ge "$IDLE_STOP_SECONDS" ]; then
                stop_iperf3
            fi
        fi
    done
}

# ==================================================
# 安装 / 卸载 / 状态
# ==================================================

install_service() {
    need_root
    check_command systemctl
    check_command iperf3
    check_command iptables

    chmod +x "$SCRIPT_PATH"

    cat > "/etc/systemd/system/${SERVICE_NAME}" <<EOF
[Unit]
Description=Run iperf3 when frps has real traffic
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${SCRIPT_PATH} run
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable --now "$SERVICE_NAME"

    echo "已创建并启动 systemd 服务: ${SERVICE_NAME}"
    echo
    systemctl --no-pager status "$SERVICE_NAME"
}

uninstall_service() {
    need_root

    systemctl disable --now "$SERVICE_NAME" 2>/dev/null || true
    rm -f "/etc/systemd/system/${SERVICE_NAME}"
    systemctl daemon-reload

    remove_iptables_monitor

    echo "已卸载服务并清理 iptables 监控规则"
}

show_status() {
    systemctl --no-pager status "$SERVICE_NAME" || true
}

show_logs() {
    tail -f "$LOG_FILE"
}

case "${1:-run}" in
    run)
        run_monitor
        ;;
    install)
        install_service
        ;;
    uninstall)
        uninstall_service
        ;;
    status)
        show_status
        ;;
    logs)
        show_logs
        ;;
    *)
        echo "用法:"
        echo "  $0 run        前台运行监控"
        echo "  $0 install    创建 systemd 服务并开机自启"
        echo "  $0 uninstall  停止并卸载 systemd 服务"
        echo "  $0 status     查看服务状态"
        echo "  $0 logs       查看日志"
        exit 1
        ;;
esac

二、VPS2

VPS1_IP="你的VPS1_IP" PORT=5201 bash -s <<'EOF'
set -e

echo "[1/5] 安装 iperf3..."

if command -v apt-get >/dev/null 2>&1; then
    apt-get update -y
    DEBIAN_FRONTEND=noninteractive apt-get install -y iperf3
elif command -v dnf >/dev/null 2>&1; then
    dnf install -y iperf3
elif command -v yum >/dev/null 2>&1; then
    yum install -y epel-release || true
    yum install -y iperf3
else
    echo "不支持的系统:未找到 apt-get / dnf / yum"
    exit 1
fi

echo "[2/5] 创建 iperf3 systemd 服务..."

cat > /etc/systemd/system/iperf3-server.service <<SERVICE
[Unit]
Description=iperf3 Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/iperf3 -s -p ${PORT}
Restart=always
RestartSec=3

DynamicUser=yes
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes

[Install]
WantedBy=multi-user.target
SERVICE

echo "[3/5] 启动 iperf3-server..."

systemctl daemon-reload
systemctl enable --now iperf3-server.service

echo "[4/5] 配置防火墙,仅允许 VPS1 访问 ${PORT}/tcp..."

if command -v ufw >/dev/null 2>&1; then
    ufw allow from "$VPS1_IP" to any port "$PORT" proto tcp || true
    ufw reload || true
elif command -v firewall-cmd >/dev/null 2>&1; then
    firewall-cmd --permanent --add-rich-rule="rule family=\"ipv4\" source address=\"$VPS1_IP\" port protocol=\"tcp\" port=\"$PORT\" accept" || true
    firewall-cmd --reload || true
else
    echo "未检测到 ufw/firewalld,跳过系统防火墙配置"
fi

echo "[5/5] 检查服务状态..."

systemctl --no-pager status iperf3-server.service

echo
echo "完成。VPS2 已启动 iperf3-server。"
echo "监听端口: ${PORT}/tcp"
echo "允许来源: ${VPS1_IP}"
echo
echo "如果云服务商有安全组,也要放行:TCP ${PORT},来源 ${VPS1_IP}/32"
EOF