尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Linux重新路由和首次路由的区别

Linux重新路由和首次路由的区别 shixudong163.comLinux在发包前进行首次路由查找在《TPROXY与Wireguard》文末小插曲有提到unconnect方式只需一次路由查找connect方式需要两次路由查找然后根据路由查找结果设置数据包路由并修改待发送数据包的源IP初始源IP一般为0如匹配local路由将其修改为目标IP非local路由则修改为外出网卡IP和目标IP如初始目标IP为0将其修改为127.0.0.1。数据包在经过iptables的OUTPUT链时如在mangle表打上了mark或者在nat表通过DNAT修改了目标IP则需要调用ip_route_me_harder重新路由。Linux的重新路由机制使用同样的路由查找算法并根据路由查找结果重置数据包路由但不对数据包本身进行任何改变。下面通过3个例子来进一步考察两者之间的区别。第一个例子近日因某些原因需要block树莓派网关上外出的quic端口并且希望在nat表完成丢包动作然而nat表已不再支持DROP操作只能通过DNAT --to blackhole变相实现由于ipv4没有明确定义blackhole根据网上建议可用0.0.0.0或127.0.0.1代替。在使用127.0.0.1作为DNAT的blackhole时具体又可分为OUTPUT/DNAT和PREROUTING/DNAT。对于OUTPUT/DNAT本机外发数据包将直接进入本机接收环节如本机没有对应侦听端口UDP/TCP分别返回icmp-port-unreachable/tcp-reset可起到block作用发送端能接收并处理返回所以不是严格意义上的blackhole。如本机存在对应侦听端口对于TCP将和本机侦听端口建立连接起不到blackhole作用。至于UDP因属于无连接协议情况稍有点复杂取决于UDP服务的不同实现如UDP服务仅绑定了127.0.0.1或使用IP_PKTINFO获取了接收包的目标IP此时返回包源IP为127.0.0.1可被conntrack还原能和发送端双向通信同样起不到blackhole作用。否则UDP服务将根据路由查找算法选择返回包的源IP必然不是127.0.0.1后续不能被conntrack还原无法被发送端认可此种情形下仍可以起到blackhole作用。至于PREROUTING/DNAT由于默认没有启用route_localnet外来数据包将在ip_route_input_slow环节丢包完美实现了blackhole。如启用了route_localnet数据包可进入本机接收环节其行为和OUTPUT/DNAT的本机接收情况基本一致。在使用0.0.0.0作为DNAT的blackhole时对于PREROUTING/DNAT外来数据包也将在ip_route_input_slow环节丢包并且没有额外的开关控制可以完美实现blackhole。顺便提一下上述ip_route_input_slow丢包的两种情形都可以用sudo sysctl -w net.ipv4.conf.all.log_martians1验证也能在入口网卡处抓到相应的数据包。根据《也谈tcpdump抓包》一文在入口网卡处抓到的目标IP并非DNAT后的目标IP仍然是DNAT前的目标IP。对于OUTPUT/DNAT当在lo网卡接收环节抓到目标IP为0.0.0.0的数据包时令人感到很意外因为在mkroute_output环节不允许出现目标IP全0的情形。此外当本机直接向0.0.0.0发送数据包时如没有经过OUTPUT/DNAT环节目标IP将被替换为127.0.0.1也不会出现目标IP全0的情形。反复研读了几遍源码终于意识到了Linux重新路由和首次路由之间的区别两者共同调用的路由查找算法在发现目标IP全0时将其更改为源IP如源IP也全0则将目标IP和源IP一并更改为127.0.0.1并用更改后的数据去调用mkroute_output因此对于mkroute_output来说不会出现目标IP全0的情形。Linux在首次路由查找结束后除了设置数据包路由外还将上述临时更改结果同步到待发送数据包然而Linux重新路由只是根据路由查找结果重置数据包路由不会对数据包本身进行任何更改。基于上述原因目标IP为0.0.0.0的数据包只能出现在OUTPUT/DNAT后的重路由环节并且因为带有路由信息通过lo网卡自发自收时顺利跳过了ip_route_input_slow环节不会被内核丢包可以进入本机接收环节。如本机没有对应侦听端口对于UDP能返回源IP为0.0.0.0的icmp-port-unreachable经过conntrack还原后能原路返回UDP发送端实现block作用对于TCP由于返回的tcp-reset包源IP不是0.0.0.0不能被conntrack还原导致返回包不被TCP发送端认可虽然可起到blackhole作用但TCP发送端将多次重试本机增加了不必要的响应资源。如本机存在对应侦听端口除了TCP返回的SYNACK包源IP为0.0.0.0可被conntrack还原外其他TCP/UDP的返回包源IP都不是0.0.0.0不能被conntrack还原无法被发送端认可可以起到blackhole作用但也同样让本机增加了不必要的响应资源。综上PREROUTING/DNAT建议使用0.0.0.0作为blackhole由内核丢包无需考虑本机是否存在相应的侦听端口。OUTPUT/DNAT建议使用127.0.0.1作为blackhole同时本机不能侦听相应端口可以起到block端口的作用并且不会浪费本机资源。第二个例子以《TPROXY与Wireguard》文末小插曲为例该处有个小结论网关自身应用程序要想使用TPROXY功能只能通过OUTPUT链打mark的方式将数据包重新路由到TPROXY。如寄希望于通过应用程序自身打mark直接路由到TPROXY由于内核路由查找算法的瑕疵会引发各种稀奇古怪的路由问题导致无法使用TPROXY功能。当时由于没有意识到linux重新路由和首次路由的区别就没有进一步分析其成因只是简单地将其归类为内核路由查找算法的瑕疵实际上造成上述问题的根因就在于重新路由和首次路由的差异。Linux在发包前首次路由查找后必须根据路由查找结果修改数据包的源IP初始源IP一般为0如匹配local路由将其修改为目标IP非local路由则修改为外出网卡IP而Linux重路由机制虽然使用同样的路由查找算法但不对数据包进行任何改变。网关自身应用程序使用本机TPROXY功能恰恰就是充分利用了首次路由和重新路由的各自特点首次路由时无mark匹配非local路由将外出网卡IP作为源IP。重新路由时带mark匹配local路由此时local路由表能匹配源IP数据包可顺利使用该源IP和本机TPROXY之间双向通信。最后一个例子比较常见在《关于PREROUTING处理自发自收包的新认识》中就提到一个场景Linux主机在使用127.0.0.1访问Bridge模式Docker时需要通过OUTPUT链实现DNAT此处属于常规DNAT而非例一中的blackhole。还有一个多运营商线路接入的双网卡场景则需要通过OUTPUT链打mark、并使用策略路由将数据包导向不同的运营商。在这两种场景下外出数据包在首次路由时已根据初始目标IP选定了相应的外出网卡IP作为源IP但在OUTPUT链打mark或DNAT后重新路由时如实际外出网卡发生了变化将和数据包首次路由时获取的源IP不匹配导致该源IP并不是目标网络期望的源IP很有可能被目标网络丢包。由于重新路由并不能修改数据包因此还需要在POSTROUTING链添加配套的MASQUERADE规则修改源IP以匹配真实发送网卡确保数据包可顺利到达目标并能顺利返回。此外对于最后一例中的DNAT场景因初始目标IP为127.0.0.1故首次路由后源IP也被改为127.0.0.1然而经过DNAT转换后目标IP不再是本机IP此种情形源IP为127.0.0.1外出网卡非lo网卡还需要启用route_localnet否则重新路由时外出数据包将在mkroute_output环节返回错误压根起不到DNAT作用。最后顺便提一下路由查找算法主要任务是确定下一跳副产品是还可确定源IP和外出网卡源IP在首次路由后同步更新到待发送数据包但数据包的外出网卡信息无论是首次路由还是重新路由都不会被同步更新一直保持为null直到数据包进入ip_output环节才根据最新的下一跳信息设置其外出网卡可通过pwru --output-meta验证。由于数据包本身只带有单方向进入或外出的网卡信息因此用于匹配iptables规则所需的数据包外出网卡信息并不是来自数据包自带的网卡信息而是根据进入hook时数据包的下一跳信息获取到外出网卡信息。对于遍历OUTPUT链nat和filter表的数据包,总是使用进入hook点NF_INET_LOCAL_OUT时通过首次路由获得的外出网卡去匹配iptables规则。因此即使在OUTPUT链打mark或DNAT后立即重新路由并更改了外出网卡实际并未更改只是更改了下一跳也仍然使用首次路由获得的外出网卡而非重新路由后更改的外出网卡去匹配OUTPUT链/filter表规则。至于重新路由后更改的外出网卡信息何时作用于iptables规则需要到ip_output环节数据包进入hook点NF_INET_POST_ROUTING时才会根据最新的下一跳信息去获取更改后的外出网卡用来匹配数据包外出前最后的POSTROUTING链规则。
返回列表