让你的服务器自动向路由器申请端口转发,省去手动登录路由器配置的繁琐步骤。本文提供完整的安装、使用教程,以及一个支持多端口、多 IP 的自动续期脚本,让 UPnP 映射像静态端口转发一样持久有效。

🔧 准备工作:确保路由器开启 UPnP

在使用 UPnP 客户端之前,需要先确认你的路由器已经开启了 UPnP 功能。不同品牌路由器的管理界面可能略有差异,一般步骤如下:

  1. 登录路由器管理后台(通常是 192.168.1.1192.168.0.1)。
  2. 寻找“转发规则”、“高级设置”或“安全设置”等选项。
  3. 找到“UPnP”或“通用即插即用”功能,并确保其状态为 “开启”“启用”
💡 如果找不到该选项,可以查阅路由器说明书或搜索“你的路由器型号 + UPnP 开启”。

📦 在 Rocky Linux 9 上安装 miniupnpc

miniupnpc 是 UPnP 客户端的核心工具,它存在于 EPEL(Extra Packages for Enterprise Linux)仓库中。

# 启用 EPEL 仓库
sudo dnf install epel-release -y

# 安装 miniupnpc
sudo dnf install miniupnpc -y

安装完成后,你会获得 upnpc 命令行工具,用于管理 UPnP 端口映射。

🚀 核心命令与操作步骤

1. 发现 UPnP 设备

首先使用 -s 参数列出网络中的 UPnP 设备和服务信息,确认客户端与路由器之间的 UPnP 通信正常:

upnpc -s

如果一切正常,该命令会返回路由器的描述信息、公网 IP 等。如果提示 No valid UPnP device found,请检查路由器的 UPnP 是否已开启,以及防火墙是否阻止了 UPnP 广播(见下文“注意事项”)。

2. 添加单条端口映射

使用 -a 参数添加一条端口转发规则,基本命令格式:

upnpc -a [内网IP] [内网端口] [外网端口] [协议]
  • [内网IP]:运行服务的 Rocky Linux 服务器的内网 IP 地址,例如 192.168.1.100
  • [内网端口]:服务器上服务实际监听的端口,例如 8080
  • [外网端口]:希望从互联网访问的端口,例如 80
  • [协议]tcpudp

示例:将内网 IP 192.168.1.1008080 端口的 Web 服务,通过路由器的 80 端口对外发布:

upnpc -a 192.168.1.100 8080 80 tcp

成功后,从外部网络访问路由器的公网 IP 的 80 端口,就会被转发到服务器的 8080 端口。

⏱️ 可选参数:你还可以在命令末尾加上一个有效期(单位:秒),例如 upnpc -a 192.168.1.100 8080 80 tcp 3600 表示映射只在 1 小时内有效。不指定则请求“无限期”,但大部分路由器并不会真正永久保留。

3. 管理现有规则

  • 列出所有规则

    upnpc -l
  • 删除一条规则(按外部端口和协议):

    upnpc -d 80 tcp

⚠️ 注意事项与常见排错

防火墙配置

Rocky Linux 9 自带的 firewalld 可能会阻止 UPnP 广播。请放行 UPnP 服务:

sudo firewall-cmd --permanent --add-service=upnp
sudo firewall-cmd --reload

如果仍然无法发现 UPnP 设备,可以临时关闭防火墙进行测试(sudo systemctl stop firewalld),以确认问题来源。

内网 IP 地址稳定性

如果你的服务器通过 DHCP 获取 IP,地址可能会变动,导致端口映射规则指向错误的 IP。建议:

  • 在路由器中为服务器设置 静态 DHCP 分配(IP 与 MAC 绑定),或
  • 直接在服务器上配置 静态 IP

安全性

UPnP 在带来便利的同时,也可能被恶意软件利用来在路由器上创建后门。建议:

  • 只为必要的、可信的服务启用 UPnP 映射。
  • 定期运行 upnpc -l 检查当前激活的规则。
  • 考虑在不需要时关闭路由器的 UPnP 功能。

💾 如何让端口映射永久生效(支持多端口、多 IP)

上面介绍的 upnpc -a 命令添加的端口映射是临时的。路由器重启、WAN 口重连或 UPnP 服务重启后,这些规则往往会丢失。如果你希望映射能够长期稳定存在,可以使用下面提供的自动续期脚本

脚本特性

  • 支持多个端口映射(不同内网 IP、不同端口、混合 TCP/UDP)。
  • 通过配置文件集中管理所有需要保活的映射。
  • 定期检查,发现缺失则自动重新添加。
  • 轻量级,仅依赖 miniupnpc 和 bash。

1. 创建配置文件

创建一个配置文件(例如 /etc/upnpc/maps.conf),每行一条映射规则,格式为:

<内网IP> <内网端口> <外网端口> <协议> [可选:有效期秒数]

示例:

sudo mkdir -p /etc/upnpc
sudo vim /etc/upnpc/maps.conf
# 格式: 内网IP 内网端口 外网端口 协议 [duration]
192.168.1.100 8080 80 tcp
192.168.1.100 22 10022 tcp
192.168.1.101 9000 9000 udp
192.168.1.102 51820 51820 udp 86400   # 有效期1天
  • 字段之间用空格或 Tab 分隔。
  • # 开头的行为注释,空行会被跳过。
  • duration 是可选参数,不写则请求无限期。

2. 编写自动续期脚本

创建 /usr/local/bin/upnp_keepalive.sh

sudo vim /usr/local/bin/upnp_keepalive.sh

粘贴以下内容:

#!/bin/bash

# ============================================
# UPnP 端口映射自动续期脚本
# 支持多端口、多 IP,基于配置文件
# ============================================

CONFIG_FILE="/etc/upnpc/maps.conf"
LOG_FILE="/var/log/upnp_keepalive.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

# 检查单条映射是否已存在于路由器上
# 参数: 内网IP 内网端口 外网端口 协议
mapping_exists() {
    local int_ip=$1
    local int_port=$2
    local ext_port=$3
    local proto=$4

    # upnpc -l 输出格式可能有两种,这里都匹配
    if upnpc -l 2>/dev/null | grep -q "${ext_port} ${proto} -> ${int_ip}:${int_port}"; then
        return 0
    fi
    if upnpc -l 2>/dev/null | grep -q "${ext_port} ${proto} ${int_ip}:${int_port}"; then
        return 0
    fi
    return 1
}

main() {
    if [ ! -f "$CONFIG_FILE" ]; then
        log "ERROR: 配置文件 $CONFIG_FILE 不存在"
        exit 1
    fi

    while IFS= read -r line || [ -n "$line" ]; do
        # 去除前后空格,跳过空行和注释
        line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
        [[ -z "$line" || "$line" =~ ^# ]] && continue

        read -r int_ip int_port ext_port proto duration <<< "$line"

        if [ -z "$int_ip" ] || [ -z "$int_port" ] || [ -z "$ext_port" ] || [ -z "$proto" ]; then
            log "WARNING: 跳过格式错误的行: $line"
            continue
        fi

        if mapping_exists "$int_ip" "$int_port" "$ext_port" "$proto"; then
            log "OK: 映射已存在 $ext_port $proto -> $int_ip:$int_port"
        else
            log "MISSING: 重新添加映射 $ext_port $proto -> $int_ip:$int_port"
            cmd="upnpc -a $int_ip $int_port $ext_port $proto"
            if [ -n "$duration" ] && [ "$duration" -eq "$duration" ] 2>/dev/null; then
                cmd="$cmd $duration"
            fi
            if $cmd >> "$LOG_FILE" 2>&1; then
                log "SUCCESS: 已添加映射 $ext_port $proto -> $int_ip:$int_port"
            else
                log "FAILED: 添加映射失败 $ext_port $proto -> $int_ip:$int_port"
            fi
        fi
    done < "$CONFIG_FILE"
}

main

赋予执行权限:

sudo chmod +x /usr/local/bin/upnp_keepalive.sh

3. 手动测试脚本

sudo /usr/local/bin/upnp_keepalive.sh
cat /var/log/upnp_keepalive.log

确认日志中没有错误,并且缺失的映射被成功添加。

4. 设置定时任务(自动续期)

使用 cron 每 5 分钟运行一次:

sudo crontab -e

添加以下行:

*/5 * * * * /usr/local/bin/upnp_keepalive.sh >/dev/null 2>&1

现在,无论路由器重启还是映射意外丢失,脚本都会在 5 分钟内自动补回,达到接近“永久生效”的效果。

5. (可选)清理不再需要的旧映射

如果你希望脚本不仅添加缺失的映射,还能自动删除路由器上那些不在配置文件中的多余规则(例如其他程序遗留的),可以在脚本的 main 函数末尾增加一个清理逻辑。由于这种“严格同步”模式可能误删其他应用创建的 UPnP 规则,默认不开启。如有需要,欢迎在评论区留言获取增强版脚本。

最后修改:2026 年 04 月 20 日
如果觉得我的文章对你有用,请随意赞赏