ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DNAT端口映射故障全解析:NAT回流、MTU黑洞与iptables排障实战

DNAT端口映射故障全解析:NAT回流、MTU黑洞与iptables排障实战 1. 开刀之前先布局企业DNAT的原理与设计边界1.1 别急着敲命令DNAT的底层逻辑与数据流我在处理过多家制造型企业和互联网公司的网络故障后发现“端口映射不通”排障90%都死在几类相同的问题上。很多运维拿到需求后第一反应就是跑到防火墙上敲一条DNAT规则敲完发现不通然后开始瞎试把PREROUTING链翻来覆去改了好几遍还是不通最后才想起来看数据包到底走到了哪一步。所以我想先花点篇幅把DNAT的底层逻辑讲透因为排查流程本质上就是沿着数据包的走向逐段验证的过程如果你对链条本身没有清晰的认知后面所有的抓包和分析都会变成无头苍蝇式的乱撞。DNATDestination Network Address Translation的中文叫“目的地址转换”它的核心动作发生在Linux内核Netfilter框架的PREROUTING钩子点。当数据包从外网接口进入防火墙时内核会先经过PREROUTING链在这里我们可以根据规则将包的目的IP和目的端口改写成内网服务器的真实地址。改完之后内核会做一次路由决策发现这个包的目标地址已经变成了内网段于是把它从FORWARD链转发出去经过POSTROUTING链如果配置了SNAT/MASQUERADE再改源地址最后从内网接口发出。这里最容易被忽略的是conntrack连接跟踪机制。DNAT规则改写的是数据包的首包但后续的每一个包包括回包都必须靠conntrack来维护映射关系。Linux会在第一条数据包经过时建立一条连接表项后续属于同一TCP连接的所有包都直接查表转发不再匹配用户自定义的DNAT规则。这个机制的好处是性能高坏处是如果你改错了规则但连接表里还残留着旧的映射那么新规则的生效就会延迟甚至完全不生效。顺带提一句很多人问我为什么Linux上做端口映射还要额外开启net.ipv4.ip_forward1。其实道理很简单DNAT只是改写了数据包的目标地址但内核默认情况下是不允许在接口之间转发流量的ip_forward就是那个总开关。你不打开它数据包在PREROUTING改写完之后直接被内核丢弃了连FORWARD链都不会走到。1.2 一个关键前提别在设计时埋下“内网回流”隐患说完数据流我们再聊一个设计层面的问题。很多企业初期规划网络时只考虑了“从外网访问内网服务器”这一个场景完全没有想过“内网用户通过公网域名访问内网服务器”这个更普遍的需求。等网络搭完了OA系统上线了老板在公司里打开OA系统发现页面转圈然后IT部门就开始背锅。这个现象在技术圈里叫作NAT回流问题也有人叫它Hairpin NAT问题。它的本质是内网用户访问的是公网IP这个数据的走向是“内网客户端 - 防火墙 - 公网IP - 防火墙DNAT改写 - 内网服务器”。表面上看起来数据包只是在防火墙内部绕了一个圈但实际上涉及了两次NAT转换而且源IP、目的IP的转换时机和路由决策之间存在着天然的坑。我在做网络设计时有一条铁律如果这个服务需要被内外网同时访问那么在设计阶段就一定要规划好回流的处理方案。是走“源NATSNAT 静态路由”的硬核方案还是走“DNS视图分离”的轻量方案又或者是“防火墙开NAT Loopback”的一键方案必须提前定下来否则等到线上出故障再来补往往就需要在防火墙上加一堆临时规则搞得配置烂成一锅粥。这里也顺便回答一个很多新手困惑的问题为什么我开启回流功能之后内网能通了但内网用户看到的服务器源IP变成了防火墙的内网接口地址而不是真实的客户端IP因为启用回流往往意味着防火墙需要对内网发往公网IP的流量做一次源地址转换SNAT否则回包会因为路由不对称而被丢弃。这是NAT机制的固有行为不是配置出错了。2. 最常见的4类端口映射故障与实战排除手法2.1 第1类规则存在但“不通”——防火墙顺序与规则冲突这是最典型的“看起来什么都是对的但就是不通”的故障。现象是用户在防火墙上已经添加了DNAT规则使用iptables -t nat -L -n也能看到这条规则从外网telnet公网IP的端口就是超时。我这几年排查过几十例这种问题总结下来有四个高频原因按出现概率排序分别是规则链顺序错误、接口选择错误、上游防火墙拦截、配置没生效服务没重启/iptables没保存。先说规则链顺序。iptables -t nat -A PREROUTING是追加到链尾的如果之前已经存在一条更宽泛的DNAT规则比如把整个IP段都DNAT到某台服务器而后加的这条更精确的规则在链尾那么数据包在PREROUTING链中从上往下匹配时会先被前面的规则拦截并完成改写后面的规则根本不会被执行。这就是为什么我反复强调改NAT规则之前先执行iptables -t nat -L -n --line-numbers看清楚顺序如果顺序不对用iptables -t nat -I PREROUTING 1把规则插到最前面去。再说接口选择。很多企业防火墙上会有多个内网接口比如办公网、服务器网、视频监控网分开规则中如果使用-i参数指定了接口你必须确认这个接口的命名和实际物理连接是一致的。我遇到过一个案例对方在防火墙上是按接口别名写的规则结果系统重启之后接口名变了规则还在但实际上已经没有流量从这个接口进来了排查了半小时才发现是这种低级错误。第三个原因是企业网络里“防火墙前置”的架构。很多公司出口处不止一台设备可能有一台硬件防火墙比如深信服、山石后面还跑了台Linux服务器做NAT或负载均衡两层设备之间还有交换机。你只检查了Linux这一层的规则但出问题的其实是前面那台硬件防火墙。这种情况下的排查技巧是在Linux防火墙的入口抓包看看数据包有没有到你这层就真相大白了。第四个原因比较隐性我单独说一句如果你配置完iptables规则后发现不生效顺手执行systemctl restart iptables或者service iptables save一下。很多人配置完忘了保存规则只是临时在内存里生效下次重启就丢了又得重新排查一遍。2.2 第2类时通时断——MTU黑洞与链路协商问题这类故障的现象很典型端口映射配置好了小流量访问完全正常但一旦传输大文件、跑视频流或者上传比较大的附件时就会卡住过一会儿报连接被重置或者超时小包又恢复了。我把它单独列为一类故障是因为它在“4类典型故障”里最不容易被第一时间定位到而且一旦你怀疑到MTU问题上恰恰说明你已经迈过了初级排查阶段。这个问题的根本原因是路径上的MTU最大传输单元不一致。默认以太网的MTU是1500字节但企业出口经常使用PPPoE拨号ADSL/LAN接入PPPoE的包头会占掉8字节导致实际可用的MTU只有1492。如果中间还叠加了GRE隧道或者VXLAN隧道MTU会更小可能只有1400甚至更低。当客户端发送一个1500字节的数据包时防火墙将其转发到PPPoE链路时发现超长如果IP头部的DFDont Fragment不分片标志位被置位了那么这个包就不能被分片只能被丢弃同时理论上应该回一个ICMP消息“需要分片但DF置位”给发送方。但很多企业网络的ACL把ICMP报文全部丢弃了发送方永远收不到这个通知它会一直尝试发送同样大小的数据包结果就是大流量全部被静默丢弃表现为“卡死不断线”。解决这个问题有两个层面。一个是治本的调整链路的MTU另一个是治标的也是最通用的做法在防火墙上对TCP流量启用MSS钳制TCPMSS。具体命令是在FORWARD链上加一条规则对穿越防火墙的TCP SYN包进行MSS值重写iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu这条规则的含义是自动探测路径上的最小MTU并把这个值减去40字节TCP头IP头的开销作为MSS值向下游通告。客户端收到这个SYN-ACK后就会用这个更小的MSS值发送数据从而避免分片和丢弃。这里有一个判断技巧值得分享当传输大文件卡住时在防火墙的内网接口和后端服务器上同时抓包如果发现防火墙发出了数据包但服务器没有收到或者收到了但重传极其频繁同时伴有ICMP消息那十有八九就是MTU黑洞。另外你可以用ping -M do -s 1472命令以太网非Jumbo帧场景直接测试链路上最大的可用包大小这比盲猜高效得多。2.3 第3类内网访问外网IP失败——NAT回流与“Intrusion Detection”的战争前面我已经提到了NAT回流问题的原理这里展开讲实战处置。这个故障的场景很固定服务发布到公网之后外网用户访问一切正常但内网用户通过同一个公网域名访问时要么完全不通要么通了但速度奇慢要么通了但服务表现怪异比如页面上的图片能加载但接口请求全部失败。为什么会这样我们还原一下完整的数据走向。内网客户端设置DNS解析到公网IP然后发送数据包源IP是192.168.1.100目的IP是202.102.x.x公网IP。数据包到达防火墙内网接口后PREROUTING链根据DNAT规则把它改写为“源IP 192.168.1.100目的IP 192.168.10.5”内网服务器。内核路由决策后将这个包从内网接口发往192.168.10.5。问题来了目标服务器接收到这个包后看到的源IP是192.168.1.100真实客户端于是回包时会直接将响应发送给192.168.1.100而这个源地址在同一个二层网络或者直连路由中可以直接到达。看起来一切正常对不对但实际上回包不再经过防火墙的任何NAT处理而客户端发出的请求却是先经过防火墙再到达服务器的这就是典型的非对称路由。非对称路由在很多场景下能工作但在做了严格安全策略的防火墙上就会被拦截。因为你请求时经过了防火墙响应的内容里TCP序列号等都是通过防火墙的NAT表映射过去的如果防火墙没有在连接表里记下这次转换实际上它记了但由于回包不走它所有后续的包都变成了无状态的新包那么客户端收到的响应和它发出的请求在TCP层面根本对不上号表现就是握手卡死或者连接被防火墙的会话表老化机制给断开。解决这个问题的标准方案是“在DNAT之后再做一次SNAT”。也就是说当内网用户访问公网IP时防火墙不仅要改写目的地址DNAT到内网服务器还要把源地址也改写成一个防火墙内网接口的IP地址SNAT这样内网服务器看到的流量来源就是防火墙本身回包也会统一回到防火墙再由防火墙将回包源地址改回公网IP后送给内网客户端形成一条完美的对称链路。具体操作上以iptables为例先做DNAT再加一条针对内网网段的SNAT规则iptables -t nat -A PREROUTING -d 202.102.x.x -p tcp --dport 8080 -j DNAT --to-destination 192.168.10.5:8080 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.10.5 -p tcp --dport 8080 -j SNAT --to-source 192.168.10.1这种方案在硬件防火墙上通常对应一个叫作“NAT Loopback”或者“Hairpin NAT”的功能开关。如果你是h3c或华为的设备可能还需要额外配置一个“NAT内部服务器”的内网访问优化开关不同厂商命令略有差异但思路完全一致。2.4 第4类目标服务器的ARP/路由响应异常——源IP被“吞掉”的丢包陷阱这类故障平时见得不算特别多但一旦遇到极其让人抓狂。现象也很奇怪外网访问时TCP握手发出去了服务器也收到了但服务器的回包就是到不了客户端导致连接永远建立不起来。我用一个我实际处理过的案例来说明。某个企业内网有一台NFS服务器双网卡一块网卡接办公网192.168.1.0/24另一块接存储网10.10.0.0/24默认网关指向办公网的防火墙。后来运维做了一个端口映射把这台NFS服务器的某个管理端口发布到了公网DNAT目标地址写的是存储网网卡IP10.10.0.5。配置完成后外网死活连不上。排查过程非常有意思。在防火墙上看DNAT规则没问题流量也确实被转发到了10.10.0.5在服务器上用tcpdump抓包也能看到SYN包正常到达了10.10.0.5网卡。但再细看发现服务器回复SYN-ACK时源地址却变成了办公网网卡的IP192.168.1.5而不是存储网网卡的IP10.10.0.5。原因在于Linux的弱主机模型与路由策略。当服务器收到目的IP是10.10.0.5的包时它发现自己另外一块网卡上有通往客户端源IP是公网IP的默认路由于是内核认为最优的发送接口是办公网网卡系统会自动选择一个“和源IP最匹配”的本地地址作为回包的源地址导致回包源地址与客户端请求的目的地址不一致。客户端收到一个来自陌生IP的SYN-ACK自然会把包丢弃TCP握手必然失败。这类问题在负载均衡后端服务器上特别常见尤其是那些服务器上配了VIP虚拟IP但实际响应却从物理网卡发出的情况。解决思路有几种。第一种是检查目标服务器的路由表确保从哪个网卡进来的流量就从哪个网卡回应。可以添加策略路由来实现。第二种是更加“无脑”的做法在防火墙上把DNAT和SNAT都配上也就是Full NAT让目标服务器看到的源地址永远是防火墙的内网IP目标服务器就不需要为了迎合真实客户端IP而选择错误的回包路径了。这种情况下服务器只需要把默认网关指到防火墙的内网接口就行所有流量都会对称地回到防火墙上。第三种是针对VS/NAT模式负载均衡架构的如果后端服务器有多个网卡而且需要保留客户端真实IP做审计那就需要在服务器上配置静态ARP条目或者使用透明代理设备转发回包这个技术细节比较深这里就不再展开了。3. 拿什么定位问题完整排查流程与工具链清单3.1 打蛇打七寸抓包是唯一的“照妖镜”当端口映射故障发生后最重要的事情不是去翻配置而是先搞清楚“数据包到底走到了哪一步”。我见过太多运维一上来就改iptables改完没效果又开始改防火墙策略最后连自己都搞不清是哪一步出了问题。我推荐的排查方式是“四段链路法”把数据链路切分成四个关键观察点逐个抓包验证防火墙公网接口的入向流量确认外网请求确实到达了防火墙。防火墙内网接口的出向流量确认DNAT改写生效且数据包被转发出来。后端服务器的入向流量确认服务器网卡收到了数据包。后端服务器出向流量及防火墙的入向回包确认回包路径是否对称。这四个点的tcpdump命令很关键。比如在防火墙上看公网侧接口tcpdump -i eth0 host 202.102.x.x and tcp port 8080 -nn然后在内网接口上抓tcpdump -i eth1 host 192.168.10.5 and tcp port 8080 -nn如果第一点能看到SYN第二点看不到说明DNAT规则没生效问题在防火墙NAT配置本身。如果第二点能看到第三点看不到说明问题出在防火墙转发链或者路由决策。如果第三点能看到但第四点回包异常那基本就是上一节说到的路由不对称或者弱主机模型的坑。这里有个细节提醒大家tcpdump抓包时一定要加-nn参数否则它会尝试反向解析IP和端口不仅慢在DNS出故障时还会干扰判断。另外抓包文件用-w参数保存下来方便后续用Wireshark打开做TCP流分析重点看TCP三次握手的序列号和数据包的窗口大小。3.2 分阶段自查从外网客户端到内网服务器的四段链路法抓包虽然是最直接的证据但并不是所有人都有权限在两台设备上同时抓包。在没有抓包条件的情况下我建议你用分阶段自查的思路来排查。第一阶段验证服务本身是否存活。直接在服务器本机执行curl http://127.0.0.1:8080确认Web服务正常响应。如果是数据库端口映射就执行ss -lntp确认端口监听着。这个阶段可以把“服务挂了”这个可能性直接排除。第二阶段验证服务器到防火墙的通达性。在内网找一台机器直接访问内网服务器的内网IPcurl http://192.168.10.5:8080确认内网直连正常。如果这一步直接失败那说明问题根本不在DNAT而在服务器自身的服务绑定地址或防火墙拦阻上。第三阶段验证防火墙转发能力。在防火墙本机执行curl http://公网IP:8080要求防火墙自身支持DNAT到本机发起的流量如果能通说明DNAT规则和内部转发逻辑没问题。如果通不了重点检查PREROUTING链的规则顺序和接口匹配。第四阶段验证外网链路可达性。从外网用telnet或者nc -vz连接防火墙公网IP的8080端口同时确认上游运营商没有封禁这个端口国内云服务商有时会默认封禁某些端口。这一步是最终的端到端验证。这四个阶段分别是逐步拆解问题域的过程每通过一个阶段就把故障可能性压缩到一个更小的范围最终定位到具体环节。3.3 精确定位工具iptables/nftables的计数器与Conntrack机制抓包能告诉你“包到了哪里”但如果你想知道“防火墙到底对包做了什么”就需要利用iptables的计数器功能了。每条iptables规则其实都自带了两个计数器一个统计匹配的数据包数量packets一个统计匹配的总字节数bytes。通过观察这个计数器你可以精确判断数据包是否命中了某条规则iptables -t nat -L PREROUTING -n -v --line-numbers输出结果中有Packets和Bytes两列。如果公网侧有客户端持续访问但你这台防火墙的DNAT规则计数器始终是0那说明数据包根本没有进入这个链。如果计数器在增长但内网抓包看不到数据包那就说明数据包经过NAT改写后后续流程路由判定出错了。第二个神器是conntrack工具。Linux维护着所有的连接跟踪表通过它你能看到每个TCP连接当前所处的状态conntrack -L -n | grep 8080观察输出中的[ESTABLISHED]或者[SYN_SENT]状态。如果发现连接一直卡在SYN_SENT那说明SYN已经发出去了等不到回包大概率是回包路径的问题。如果conntrack表里压根没有这个连接说明PREROUTING阶段就没匹配上问题出在规则匹配条件上。还有一个细节容易被忽略conntrack表溢出的问题。如果企业内网的并发连接数非常大而你的conntrack表默认大小不够用内核会在/var/log/messages或者dmesg中打印“nf_conntrack: table full, dropping packet”的报错。这种情况下任何新连接包括端口映射的新连接都会被直接丢弃表现为“时通时断重试几次偶尔成功一次”。调整conntrack表大小的常用命令# 查看当前最大值 cat /proc/sys/net/netfilter/nf_conntrack_max # 临时调大例如到 100万 sysctl -w net.netfilter.nf_conntrack_max1000000 # 永久生效写入 /etc/sysctl.conf4. 内网端口映射的“隐形杀手”NAT回流机制原理与实操解决方案4.1 为什么企业网络尤其多发三个前置条件的叠加我在2.3节已经讲过一次回流问题的故障现象和处理思路了但为什么还要单独开一个章节因为在实际企业环境中回流问题往往不是孤立的它经常和其他网络策略纠缠在一起让排查变得困难。我见过最夸张的一个案例客户内网访问自己发布的OA系统需要跨三层、过两套防火墙、再经过一套上网行为管理设备其中任何一层对非对称路由的处理方式不同表现出的故障现象就不一样。NAT回流多发于企业网络是因为企业网络通常具备三个特征有多个内网网段、有严格的安全访问控制看得见摸得着的策略、有统一的DNS域名解析。这三个条件叠加之后用户访问“自己的公网域名”就会走向NAT回流这条险路。深入研究你会发现回流问题本质上是“源地址转换策略缺失”的问题。我在排查时最喜欢用的判定方法是在内网客户端上使用ping公网IP测试如果外网能通内网不能通那就一定是回流问题如果内外网都不通那可能还有防火墙别的问题在干扰。4.2 三种方案横评从Linux到商用防火墙的落地指南处理NAT回流市面上有三种主流方案分别适用于不同规模的网络。方案一是“双NAT法”也就是在第2.3节里提到的DNATSNAT方案。它的原理是把内网访问公网IP的流量“打回原形”让服务器看到的源地址是防火墙的内网IP回包必须回到防火墙再转回去。这个方案的好处是立竿见影适用于Linux iptables或者支持策略路由的中高端防火墙。缺点是要多维护一条SNAT规则并且在多个防火墙堆叠的场景下容易漏配。方案二是“DNS视图分离法”。企业如果在用Bind或者Panabit等支持智能DNS的设备可以配置两条解析路径内网用户解析域名时返回内网服务器IP外网用户解析时返回公网IP。这样内网用户根本不会去访问公网IPNAT回流问题在源头上就被避免了。这个方案的优点是性能开销最小网络行为也最符合直觉。缺点是如果你不能控制客户的DNS服务器比如有些用户手动改了DNS为8.8.8.8那么他们依然会解析到公网IP依然会触发回流问题。方案三是“硬件防火墙自带的NAT Loopback功能”。像华为、H3C、深信服等主流防火墙都有这个开关。开启之后防火墙会自动处理内网用户访问公网IP时所需的源地址转换。这个方案最省心但你需要彻底了解自己设备的行为模型因为不同设备的NAT Loopback实现方式有差异有的设备会把源IP转换为内网接口IP有的会保留真实源IP如果你需要在内网服务器上做基于源IP的访问控制就必须了解清楚再决定启用方式。从我的实际经验看最稳妥的方案是“方案一为主方案三为辅”在Linux环境中自己实现双NAT在商用硬件环境中开启厂商的Loopback功能并做好行为验证。如果条件允许直接在DNS上做视图分离从根上绕开这个坑。5. 常见问题速查表与我的独家避坑心得5.1 故障现象到根因速查表为了方便大家快速定位问题我把这四类故障的特征和排查方向整理成了一张速查表故障现象可能根因首要排查动作外网完全不通内网也不通DNAT规则未命中、上游防火墙拦截、服务未监听先测内网直连服务是否正常再看防火墙计数器外网通内网不通NAT回流未配置启用NAT Loopback或添加双NATSNAT规则小包通大包卡死/传输中断MTU黑洞、MSS未钳制在FORWARD链加TCPMSS规则并用ping -M do探测MTU握手超时抓包有去无回回包路由不对称、服务器多网卡弱主机模型检查后端服务器路由表必要时Full NAT时通时断连接数超高时断流conntrack表溢出dmesg查nf_conntrack报错调大nf_conntrack_max重启后映射失效iptables规则未持久化使用iptables-save和iptables-restore并配置开机自动加载这张表只能作为入口实际排查时建议组合使用比如“外网通内网不通”同时又伴随“大文件传输卡死”那可能就是回流和MTU两个问题同时存在需要分步解决。5.2 踩过几次坑之后我现在的习惯是……我想以这些年积累的几条真实经验作为最后的分享不算总结就是我日常做端口映射时必做的三件事。第一件事改配置前先做备份。Linux下执行iptables-save /root/iptables_backup_$(date %F).bak商用防火墙上把当前配置导出保存。这件事看似平淡但在你手误把自己SSH管理网段也NAT掉的时候它就是你唯一的救命稻草。我经历过一次凌晨三点改防火墙规则时把一条管理网段的DNAT规则写错了导致远程登录直接断开好在有备份才在物理控制台恢复。从此之后备份成了我的肌肉记忆。第二件事配置完成后主动验证三种访问模式。我以前只测外网访问后来发现内网访问也是高频需求。现在每配置完一个端口映射我一定会从三个视角各测一遍外网客户端访问公网IP、内网客户端访问公网IP、内网客户端直接访问内网IP。三角验证全都通过才敢说这个映射真正做完了。这也是“完整排查流程”的最后一环验证阶段。第三件事主动把日志打开。iptables没有原生的访问日志但可以通过在规则里加LOG来记录被DNAT的流量iptables -t nat -I PREROUTING -d 202.102.x.x -p tcp --dport 8080 -j LOG --log-prefix DNAT-HIT: 然后在/var/log/kern.log中观察日志。这个习惯在故障回溯源时价值巨大尤其是当业务方声称“一直有问题”而你想搞清楚问题是什么时候开始的时候日志中的时间点会给出明确的答案。日志会消耗一点性能但相比它在排障时的价值这点代价完全值得。最后再提醒一句所有NAT相关的规则一定要用版本管理工具管理起来哪怕只是把配置文件存到Git里。网络设备虽然是“一次配置常年运行”但运行得越久越容易在某次变更中留下隐患。有版本记录才能快速对比出“这次到底改了什么”这比记住几百行iptables规则要靠谱得多。
返回列表