1.WireGuard 教程:使用 DNS-SD 进行 NAT-to-NAT 穿透
2.从Linux源码看TIME_WAIT状态的动态持续时间
3.netfilter 链接跟踪机制与NAT原理
4.通过 NAT TCP 打洞使 qBittorrent 获得公网 IPv4 的连接性体验
WireGuard 教程:使用 DNS-SD 进行 NAT-to-NAT 穿透
原文链接: fuckcloudnative.io/post...
WireGuard 是由 Jason A. Donenfeld 等人创建的下一代开源 *** 协议,旨在解决许多困扰 IPSec/IKEv2、源原理Open*** 或 L2TP 等其他 *** 协议的码动问题。 年 1 月 日,动态WireGuard 正式合并进入 Linux 5.6 内核主线。源原理
利用 WireGuard 我们可以实现很多非常奇妙的码动青龙面板源码解析功能,比如跨公有云组建 Kubernetes 集群,动态本地直接访问公有云 Kubernetes 集群中的源原理 Pod IP 和 Service IP,在家中没有公网 IP 的码动情况下直连家中的设备,等等。动态
如果你是源原理第一次听说 WireGuard,建议你花点时间看看我之前写的码动 WireGuard 工作原理。然后可以参考下面两篇文章来快速上手:
如果遇到某些细节不太明白的动态,再去参考 WireGuard 配置详解。源原理
本文将探讨 WireGuard 使用过程中遇到的码动一个重大难题:如何使两个位于 NAT 后面(且没有指定公网出口)的客户端之间直接建立连接。
WireGuard 不区分服务端和客户端,大家都是客户端,与自己连接的所有客户端都被称之为Peer。
1. IP 不固定的 Peer
WireGuard 的核心部分是 加密密钥路由(Cryptokey Routing),它的工作原理是将公钥和 IP 地址列表(AllowedIPs)关联起来。每一个网络接口都有一个私钥和一个 Peer 列表,每一个 Peer 都有一个公钥和 IP 地址列表。发送数据时,可以把 IP 地址列表看成路由表;接收数据时,可以把 IP 地址列表看成访问控制列表。
公钥和 IP 地址列表的关联组成了 Peer 的必要配置,从隧道验证的版权吧iapp源码角度看,根本不需要 Peer 具备静态 IP 地址。理论上,如果 Peer 的 IP 地址不同时发生变化,WireGuard 是可以实现 IP 漫游的。
现在回到最初的问题:假设两个 Peer 都在 NAT 后面,且这个 NAT 不受我们控制,无法配置 UDP 端口转发,即无法指定公网出口,要想建立连接,不仅要动态发现 Peer 的 IP 地址,还要发现 Peer 的端口。
找了一圈下来,现有的工具根本无法实现这个需求,本文将致力于不对 WireGuard 源码做任何改动的情况下实现上述需求。
2. 中心辐射型网络拓扑
你可能会问我为什么不使用 中心辐射型(hub-and-spoke)网络拓扑?中心辐射型网络有一个 *** 网关,这个网关通常都有一个静态 IP 地址,其他所有的客户端都需要连接这个 *** 网关,再由网关将流量转发到其他的客户端。假设 Alice 和 Bob 都位于 NAT 后面,那么 Alice 和 Bob 都要和网关建立隧道,然后 Alice 和 Bob 之间就可以通过 *** 网关转发流量来实现相互通信。
其实这个方法是如今大家都在用的方法,已经没什么可说的了,缺点相当明显:
本文想探讨的是Alice 和 Bob 之间直接建立隧道,中心辐射型(hub-and-spoke)网络拓扑是无法做到的。
3. NAT 穿透
要想在Alice 和 Bob 之间直接建立一个 WireGuard 隧道,找源码的论坛就需要它们能够穿过挡在它们面前的 NAT。由于 WireGuard 是通过 UDP 来相互通信的,所以理论上 UDP 打洞(UDP hole punching) 是最佳选择。
UDP 打洞(UDP hole punching)利用了这样一个事实:大多数 NAT 在将入站数据包与现有的连接进行匹配时都很宽松。这样就可以重复使用端口状态来打洞,因为 NAT 路由器不会限制只接收来自原始目的地址(信使服务器)的流量,其他客户端的流量也可以接收。
举个例子,假设Alice 向新主机 Carol 发送一个 UDP 数据包,而 Bob 此时通过某种方法获取到了 Alice 的 NAT 在地址转换过程中使用的出站源 IP:Port,Bob 就可以向这个 IP:Port(2.2.2.2:) 发送 UDP 数据包来和 Alice 建立联系。
其实上面讨论的就是完全圆锥型 NAT(Full cone NAT),即一对一(one-to-one)NAT。它具有以下特点:
大部分的 NAT 都是这种 NAT,对于其他少数不常见的 NAT,这种打洞方法有一定的局限性,无法顺利使用。
4. STUN
回到上面的例子,UDP 打洞过程中有几个问题至关重要:
RFC 关于 STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)的详细描述中定义了一个协议回答了上面的一部分问题,这是一篇内容很长的 RFC,所以我将尽我所能对其进行总结。先提醒一下,STUN 并不能直接解决上面的问题,它只是个扳手,你还得拿他去打造一个称手的js源码修改扩展工具:
STUN 本身并不是 NAT 穿透问题的解决方案,它只是定义了一个机制,你可以用这个机制来组建实际的解决方案。 — RFC
STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的 NAT 之后以及 NAT 为某一个本地端口所绑定的公网端口。这些信息被用来在两个同时处于 NAT 路由器之后的主机之间建立 UDP 通信。该协议由 RFC 定义。
STUN 是一个客户端-服务端协议,在上图的例子中,Alice 是客户端,Carol 是服务端。Alice 向 Carol 发送一个 STUN Binding 请求,当 Binding 请求通过 Alice 的 NAT 时,源 IP:Port 会被重写。当 Carol 收到 Binding 请求后,会将三层和四层的源 IP:Port 复制到 Binding 响应的有效载荷中,并将其发送给 Alice。Binding 响应通过 Alice 的 NAT 转发到内网的 Alice,此时的目标 IP:Port 被重写成了内网地址,但有效载荷保持不变。Alice 收到 Binding 响应后,就会意识到这个 Socket 的公网 IP:Port 是 2.2.2.2:。
然而,STUN 并不是郴州离娄底源码一个完整的解决方案,它只是提供了这么一种机制,让应用程序获取到它的公网 IP:Port,但 STUN 并没有提供具体的方法来向相关方向发出信号。如果要重头编写一个具有 NAT 穿透功能的应用,肯定要利用 STUN 来实现。当然,明智的做法是不修改 WireGuard 的源码,最好是借鉴 STUN 的概念来实现。总之,不管如何,都需要一个拥有静态公网地址的主机来充当信使服务器。
5. NAT 穿透示例
早在 年 8 月...
从Linux源码看TIME_WAIT状态的持续时间
对于Linux系统中TIME_WAIT状态的Socket,长久以来,人们普遍认为其持续时间大约是秒。然而,在实际线上环境中,Socket的TIME_WAIT状态有时会超过秒。这个问题源于一个复杂Bug的分析,促使我深入Linux源码进行探究。
首先,了解下我们的Linux环境配置,特别是tcp_tw_recycle参数,这对TIME_WAIT状态的处理至关重要。我们设定了tcp_tw_recycle为0,以避免NAT环境下的特定问题。
接下来,让我们通过TCP状态转移图来理解TIME_WAIT状态。理论上,它会保持2MSL(Maximum Segment Lifetime,即最长报文段寿命)的时间。但具体时长并未在图中明确指出。在源码中,我发现了一个关键的宏定义TCP_TIMEWAIT_LEN,它定义了秒的销毁时间。
尽管之前我坚信秒的TIME_WAIT状态会被系统回收,但实际遇到的秒案例促使我重新审视内核对TIME_WAIT状态的处理。这个疑问将通过后续的博客分享答案。
深入源码,我们找到了TIME_WAIT定时器,它负责销毁过期的Socket。当Socket进入TIME_WAIT状态时,会触发特定的函数处理,如在不启用tcp_tw_recycle时,处理函数会直接调用inet_twsk_schedule。
内核通过时间轮机制管理TIME_WAIT状态,每个slot处理大约7.5秒的Socket。如果所有slot都被TIME_WAIT状态占用,可能会导致处理滞后。如果一个slot中的TIME_WAIT数量超过个,剩余的任务将交给work_queue处理,这会导致处理时间延长。
通过模拟,我们发现即使在slot处理完成后,整个周期可能已经过去了.5秒,这在NAT环境下可能导致问题。PAWS(Protection Against Wrapped Sequences)的保护机制可能会延长TIME_WAIT状态,使得Socket在特定情况下可以复用。
总的来说,对TIME_WAIT状态的深入理解需要避免刻板印象,因为实际情况可能因为复杂的机制而超出预想。在解决问题时,必须质疑既有的观点,这虽然艰难,但也是学习和成长的过程。
netfilter 链接跟踪机制与NAT原理
内核版本:2.6.
在Linux内核的网络过滤框架中,conntrack是关键组件,它通过5个主要的处理链来管理数据包:NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,和NF_IP_POST_ROUTING。这些链对应着数据包的不同生命周期阶段。此外,还有4个操作表:filter,nat,mangle和raw,其中filter用于常规过滤,nat则负责地址转换等。
数据包的流程从进入防火墙开始,经过一系列处理,根据目的地决定是转发、接收或丢弃。对于本地数据包,其流程分为接收和发送两个方向;对于远程目的地,处理涉及到转发。conntrack通过跟踪连接状态,记录每个数据包的源和目的,这对于SNAT和DNAT功能至关重要。
连接跟踪的核心是ip_conntrack结构,它维护连接记录,每个连接对应一个ip_conntrack_tuple_hash,存储源和目的地址信息。连接跟踪表是一个散列结构,存储所有连接记录。不同协议的处理由ip_conntrack_protocol数组管理,通过ip_conntrack_in函数,数据包进入时会进行连接跟踪的检查和初始化。
以SNAT为例,当数据包从内网到公网,通过源地址转换,netfilter首先查找转换规则,然后使用连接跟踪信息更新数据包的源地址,同时维护状态跟踪,确保应答数据包能正确发送。NAT的实现依赖于conntrack,如FTP和ICMP等复杂协议可能需要额外模块处理。
总的来说,conntrack与NAT密切相关,前者是后者实现的基础,它们共同确保了网络数据包的正确路由和转换。深入理解这些机制需要查阅源代码和相关资料。
通过 NAT TCP 打洞使 qBittorrent 获得公网 IPv4 的连接性体验
在国内,许多用户因运营商限制无法获取公网 IPv4,对 PT 玩家的连接性构成困扰。此时,TCP 打洞成为解决问题的有效途径。客户端与Tracker的通信过程依赖于客户端上报的IP地址和监听端口。正常情况下,Tracker通过TCP报文获取客户端的IPv4地址,但当客户端位于NAT环境时,情况复杂化。
如果BT客户端在路由器上运行,可以直接通过公网地址和端口被连接。然而,如客户端在子网设备上,必须通过TCP打洞技术。在这种情况下,客户端需要配置路由器以实现NAT穿透,让其他客户端通过运营商的公网地址连接到私有子网的端口。
当公网IPv4不可得时,情况更为棘手。运营商设备的NAT会限制外部连接,无法直接穿透。此时,仅完全圆锥形NAT(Full Cone NAT)允许TCP打洞。通过理解NAT类型,我们可以利用Natter或NATMap这类工具,建立并保持NAT映射关系,以实现连接性。
在qBittorrent场景中,关键在于上报给Tracker的正确端口。由于客户端受限,可能需要修改源码或采用间接策略。比如,通过NATMap获取NAT映射,然后用iptables规则将本地端口转发到实际用于服务的端口。在我的GitHub仓库有详细的教程和脚本,供参考和下载。
注意,如果NATMap和qBittorrent运行在不同设备上,还需要调整iptables转发和qBittorrent API中的本地地址配置。