在 CentOS 系统中启用 UDP 转发是一项常见的网络配置任务,通常用于构建 VPN 网关、游戏代理、DNS 中继或特定应用协议的流量穿透,与 TCP 转发相比,UDP 作为无连接协议,其转发配置在核心思路上与 TCP 类似,但细节上略有不同,本文将详细介绍在 CentOS 系统上,从内核参数调整到防火墙规则配置的完整 UDP 转发实现过程。

前提与核心概念
在开始操作之前,我们需要理解 UDP 转发的两个核心组成部分:
- 内核 IP 转发:Linux 内核本身具备路由和转发数据包的能力,默认情况下,出于安全考虑,此功能是禁用的,要使任何形式的转发(无论是 TCP 还是 UDP)成为可能,必须先开启此内核参数。
- 防火墙规则:开启内核转发能力后,系统仍然不知道应该将哪些数据包、从哪个接口、转发到哪里,这时就需要借助防火墙工具(如
firewalld或iptables)来定义具体的转发策略,即网络地址转换(NAT)规则。
整个过程可以概括为:开启内核开关 -> 配置防火墙策略 -> 验证与排查。
第一步:启用内核 IP 转发
这是所有转发操作的基础,我们可以通过 sysctl 命令来查看、临时修改或永久设置此参数。
检查当前状态
检查内核 IP 转发功能是否已启用,在终端中执行以下命令:
sysctl net.ipv4.ip_forward
如果输出为 net.ipv4.ip_forward = 0,则表示当前是禁用状态。
临时启用
为了立即测试效果,可以临时开启它,这种方式在系统重启后会失效。
sudo sysctl -w net.ipv4.ip_forward=1
执行后,系统将立即开始转发 IP 数据包。
永久启用
为了确保配置在重启后依然生效,需要将其写入到系统配置文件中,推荐在 /etc/sysctl.d/ 目录下创建一个新的配置文件,99-custom.conf,这样做可以更好地管理自定义配置,而不直接修改主配置文件。
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-custom.conf
应用配置使其立即生效,无需重启:
sudo sysctl -p /etc/sysctl.d/99-custom.conf
至此,内核层面已经为数据包转发做好了准备。
第二步:配置防火墙规则
CentOS 7 及更高版本默认使用 firewalld 作为防火墙管理工具,而一些旧系统或特定环境可能仍在使用 iptables,我们将分别介绍这两种主流工具的配置方法。

假设我们的需求是:将到达服务器公网 IP(假设接口为 eth0)的 UDP 53 端口(DNS 查询)流量,转发到内网的一台机器 168.1.100 的 53 端口。
使用 firewalld 配置
firewalld 提供了富规则功能,可以灵活地实现端口转发。
启用 IP 伪装(MASQUERADE)
为了让内网机器 168.1.100 的响应包能够正确地返回给外部客户端,我们需要对从 eth0 接口出去、源地址为内网网段的流量进行地址伪装,这相当于动态的 SNAT。
sudo firewall-cmd --permanent --zone=public --add-masquerade
添加端口转发规则
使用 --add-forward-port 选项来定义转发规则。
sudo firewall-cmd --permanent --zone=public --add-forward-port=port=53:proto=udp:toaddr=192.168.1.100:toport=53
port=53: 指定监听的本地端口。proto=udp: 指定协议为 UDP。toaddr=192.168.1.100: 指定目标转发地址。toport=53: 指定目标端口,如果与源端口相同,此项可省略。
重载 firewalld 配置
所有 --permanent 的修改都需要重载才能生效。
sudo firewall-cmd --reload
使用 iptables 配置
对于熟悉 iptables 其规则更加直观和底层。
添加 PREROUTING 规则(DNAT)
在 nat 表的 PREROUTING 链中,将目标地址和端口进行修改。
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination 192.168.1.100:53
添加 POSTROUTING 规则(SNAT/MASQUERADE)
在 nat 表的 POSTROUTING 链中,对来自内网目标、即将从公网接口发出的数据包进行源地址转换。
sudo iptables -t nat -A POSTROUTING -d 192.168.1.100 -p udp --dport 53 -j MASQUERADE
保存 iptables 规则

iptables 的规则在重启后会丢失,需要手动保存。
sudo service iptables save
或者,如果系统使用 iptables-services,则执行:
sudo iptables-save > /etc/sysconfig/iptables
第三步:验证与故障排查
配置完成后,如何验证它是否正常工作?
-
使用
tcpdump监听:这是最可靠的验证方法,在 CentOS 服务器上,分别监听公网接口eth0和内网接口(eth1)。# 监听公网接口,看数据包是否到达 sudo tcpdump -i eth0 udp port 53 # 监听内网接口,看数据包是否被转发出去 sudo tcpdump -i eth1 udp and host 192.168.1.100 and port 53
然后从外部客户端向服务器的公网 IP 发送一个 UDP 53 端口的请求,观察
tcpdump的输出。 -
检查防火墙区域:使用
firewalld时,确保规则被添加到了正确的区域(通常是public或external),可以通过firewall-cmd --get-active-zones查看。 -
SELinux:CentOS 默认启用的 SELinux 可能会阻止某些网络转发行为,在排查时,可以临时将其设置为 Permissive 模式进行测试:
sudo setenforce 0,如果问题解决,则需要配置 SELinux 策略以允许转发,而不是永久禁用它。
firewalld 与 iptables UDP 转发对比
下表小编总结了两种工具在配置 UDP 转发时的主要特点:
| 特性 | firewalld (富规则) | iptables |
|---|---|---|
| 配置风格 | 抽象化、高层次,更易理解和管理 | 底层、直接,对数据包链和表的操作更为精细 |
| 易用性 | 对新手友好,命令相对语义化 | 学习曲线较陡峭,需要理解其复杂的链表结构 |
| 灵活性 | 灵活,但某些复杂场景可能需要使用直接规则 | 极度灵活,可以实现几乎任何复杂的网络逻辑 |
| 持久化 | 通过 --permanent 和 --reload 实现,自动化程度高 |
需要手动执行 save 命令,否则重启失效 |
相关问答FAQs
Q1: 我已经按照步骤开启了内核转发和 firewalld 规则,但 UDP 转发依然不生效,最可能的原因是什么?
A1: 这是一个非常常见的问题,通常由以下三个“遗忘”导致:
- 忘记添加 MASQUERADE 规则:这是最频繁的错误,如果没有地址伪装,内网服务器的响应包会直接尝试发送给外部客户端,但客户端的防火墙通常会丢弃这个源地址不明的包,导致通信失败,请务必确认已在相应区域执行了
--add-masquerade。 - 防火墙区域错误:
firewalld的规则是按区域应用的,你可能将规则添加到了public区,但你的网络接口(如eth0)实际上被分配到了external或dmz区,请使用firewall-cmd --get-active-zones检查接口所在的区域,并将规则添加到正确的区域。 - SELinux 阻断:严格模式的 SELinux 可能会阻止网络转发,你可以通过查看
/var/log/audit/audit.log来寻找与type=AVC相关的拒绝消息,为了快速排查,可以临时执行sudo setenforce 0将其设为 Permissive 模式,如果转发成功,说明就是 SELinux 的问题,后续应配置相应的布尔值(如setsebool -P httpd_can_network_connect 1)或自定义策略,而不是永久关闭 SELinux。
Q2: 如何使用 firewalld 转发一个范围的 UDP 端口,而不仅仅是单个端口?
A2: firewalld 支持端口范围的转发,你只需要在指定端口时使用 端口起始:端口结束 的格式即可,要将所有到达本地 UDP 端口 20000 到 25000 的流量转发到内网服务器 168.1.101 的相同端口范围,命令如下:
# 1. 确保已启用 masquerade sudo firewall-cmd --permanent --zone=public --add-masquerade # 2. 添加端口范围转发规则 sudo firewall-cmd --permanent --zone=public --add-forward-port=port=20000-25000:proto=udp:toaddr=192.168.1.101 # 3. 重载配置 sudo firewall-cmd --reload
这个例子中,toport 被省略了,因为目标端口范围与源端口范围相同,如果需要转发到不同的端口范围(转发的目标是 30000-35000),则可以明确指定 toport=30000-35000。