游客发表
那天深夜,通过我在机房排查一个莫名其妙的端口的过连接超时故障。数据包明明已经到达服务器,重新却像石沉大海,发送完全没有响应。出去程抓包工具里显示的通过TCP握手请求,三次握手只完成了一次,端口的过SYN发出去了,重新SYN-ACK却迟迟不回来。发送
后来才发现,出去程问题出在防火墙的通过NAT规则上。数据包从内网进来,端口的过被DNAT规则改写了目标地址,重新转发到后端真实服务器。发送但后端服务器处理完,出去程把响应包丢回网关时,源地址还是内网地址。网关一看,这包不是自己发出去的连接,直接就给丢了。
这个转发的过程,专业术语叫“端口转发”,但更准确的说法是——通过某个端口重新发送出去的过程。你可能会问,这不就是个路由转发吗?还真不是。路由转发只管IP层,改的是MAC地址,IP地址原封不动。而端口转发要动传输层的东西,把目标端口从80改成8080,或者把源地址整个换掉。
我见过最典型的场景是公司内网部署了一台旧的业务系统,只监听在8080端口。但外部客户访问习惯用80端口。运维图省事,就在防火墙上做了条映射:外部80端口进来的流量,全部转给内网那台服务器的8080。数据包从防火墙的WAN口进来,目标端口被改写,然后从LAN口扔出去。整个过程,数据包在防火墙内部完成了“换乘”——从一个端口进来,从另一个端口重新发出去。
这里面有个特别容易踩的坑。你以为只是改了端口号,实际上NAT还要同时改写源地址。不然内网服务器收到请求,响应包的目标地址会写成防火墙的外网IP,它根本不知道怎么路由回去。所以防火墙得维护一张连接跟踪表,记录每个内网连接对应的外网映射关系。响应包回来时,再逆向改写一次。
有一次我调试一个视频会议系统,声音正常,画面卡成PPT。查了半天,发现是UDP端口映射超时时间设得太短。默认的UDP连接跟踪条目300秒就过期,而视频流的端口映射一旦过期,后续的数据包就会被当作新连接重新处理,导致端口映射频繁重建,丢包严重。把超时时间改成3600秒后,问题立刻消失。
说回那个“重新发送”的动作。其实在Linux内核里,这就是netfilter框架干的事。数据包经过PREROUTING链,DNAT规则修改目标地址和端口,然后走FORWARD链,最后从POSTROUTING出去。每一步都有钩子函数在检查规则。你要是用iptables的nat表配置过,应该对这几个链不陌生。
有意思的是,端口转发和代理服务器看起来功能相似,但本质完全不同。代理是应用层的,它接收客户端的请求,自己作为客户端去连接目标服务器,再把响应返回给客户端。端口转发则完全在传输层操作,对通信双方来说,它们以为自己在直接通信,中间人只是默默改了数据包的地址信息。这种透明性让端口转发在很多场景下比代理更高效,但也更隐蔽——出了问题更难排查,因为通信双方都看不到中间环节。
后来我养成了个习惯:遇到连接问题,先看防火墙的conntrack表,看看端口映射的条目是否正常建立,超时时间是否合理。很多你以为的“网络故障”,其实就是端口转发过程中,某个参数没配好。数据包在端口之间兜兜转转,最后迷失在NAT规则的缝隙里。
友情链接