<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Network on 云淡风轻</title>
    <link>/tags/network/</link>
    <description>Recent content in Network on 云淡风轻</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <copyright>©2007</copyright>
    <lastBuildDate>Thu, 08 Jan 2026 10:00:00 +0800</lastBuildDate><atom:link href="/tags/network/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>TINC 源码解读 第三篇 - UDP 打洞</title>
      <link>/posts/20260108-tinc-udp-holepunching/</link>
      <pubDate>Thu, 08 Jan 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/20260108-tinc-udp-holepunching/</guid>
      <description>&lt;p&gt;本文是 TINC 源码系列分析的第三篇，重点探讨 TINC 如何在 NAT 环境下建立 P2P 直连。通过 UDP 打洞技术，TINC 能够让位于不同 NAT 后的节点直接通信，大幅提升性能。&lt;/p&gt;
&lt;h2 id=&#34;1-udp-打洞概览&#34;&gt;1. UDP 打洞概览&lt;/h2&gt;
&lt;h3 id=&#34;问题背景&#34;&gt;问题背景&lt;/h3&gt;
&lt;p&gt;在互联网上，大多数终端设备都位于 NAT（Network Address Translation）或防火墙后面。这意味着两个内网节点无法直接建立 P2P 连接——它们的内网地址在公网上不可见。传统的解决方案是使用中央服务器中继，但这会增加延迟和服务器成本。&lt;/p&gt;</description>
      <content>&lt;p&gt;本文是 TINC 源码系列分析的第三篇，重点探讨 TINC 如何在 NAT 环境下建立 P2P 直连。通过 UDP 打洞技术，TINC 能够让位于不同 NAT 后的节点直接通信，大幅提升性能。&lt;/p&gt;
&lt;h2 id=&#34;1-udp-打洞概览&#34;&gt;1. UDP 打洞概览&lt;/h2&gt;
&lt;h3 id=&#34;问题背景&#34;&gt;问题背景&lt;/h3&gt;
&lt;p&gt;在互联网上，大多数终端设备都位于 NAT（Network Address Translation）或防火墙后面。这意味着两个内网节点无法直接建立 P2P 连接——它们的内网地址在公网上不可见。传统的解决方案是使用中央服务器中继，但这会增加延迟和服务器成本。&lt;/p&gt;
&lt;h3 id=&#34;核心概念&#34;&gt;核心概念&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;UDP 打洞&lt;/strong&gt; 是一种 NAT 穿透技术，允许两个在 NAT 后面的节点建立直接的 UDP 连接。基本原理是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;两个节点都在 NAT 后&lt;/li&gt;
&lt;li&gt;无法直接建立 P2P 连接&lt;/li&gt;
&lt;li&gt;通过第三方（根节点）转发连接信息&lt;/li&gt;
&lt;li&gt;两个节点同时向对方发送 UDP 包&lt;/li&gt;
&lt;li&gt;NAT 记住出站连接，允许回复通过&lt;/li&gt;
&lt;li&gt;建立直接的 UDP 通道&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;优势&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ 避免 TCP 中继的开销&lt;/li&gt;
&lt;li&gt;✅ 降低延迟&lt;/li&gt;
&lt;li&gt;✅ 减少根节点流量&lt;/li&gt;
&lt;li&gt;✅ 提高可靠性和吞吐量&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2-tinc-中的-udp-打洞机制&#34;&gt;2. TINC 中的 UDP 打洞机制&lt;/h2&gt;
&lt;h3 id=&#34;节点状态位&#34;&gt;节点状态位&lt;/h3&gt;
&lt;p&gt;TINC 使用位字段记录节点状态，其中 &lt;code&gt;udp_confirmed&lt;/code&gt; 是关键标志位，表示 UDP 连接已双向确认可用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;union&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;node_status_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; validkey: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;           &lt;span style=&#34;color:#75715e&#34;&gt;// 有有效的加密密钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; waitingforkey: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// 等待密钥回复
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; visited: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;            &lt;span style=&#34;color:#75715e&#34;&gt;// Dijkstra 访问标记
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; reachable: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;          &lt;span style=&#34;color:#75715e&#34;&gt;// 在拓扑中可达
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; indirect: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;           &lt;span style=&#34;color:#75715e&#34;&gt;// 不可直接到达
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; sptps: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;              &lt;span style=&#34;color:#75715e&#34;&gt;// 支持 SPTPS 握手
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; udp_confirmed: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// ★ UDP 已确认可用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; send_locally: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;       &lt;span style=&#34;color:#75715e&#34;&gt;// 从本地网络发送
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; udppacket: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;          &lt;span style=&#34;color:#75715e&#34;&gt;// 最后收到 UDP 包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; validkey_in: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;        &lt;span style=&#34;color:#75715e&#34;&gt;// 已发送有效密钥给对方
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; has_address: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;        &lt;span style=&#34;color:#75715e&#34;&gt;// 知道对方公网地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; ping_sent: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;          &lt;span style=&#34;color:#75715e&#34;&gt;// 已发送 UDP 探针包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; value;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;node_status_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;关键状态变量&#34;&gt;关键状态变量&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// UDP 直连相关
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; udp_confirmed;             &lt;span style=&#34;color:#75715e&#34;&gt;// UDP 连接已确认双向
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;address_cache_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;address_cache; &lt;span style=&#34;color:#75715e&#34;&gt;// 最近地址缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 打洞探测相关
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; ping_sent;                 &lt;span style=&#34;color:#75715e&#34;&gt;// UDP 探针已发送
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; timeval udp_ping_sent;   &lt;span style=&#34;color:#75715e&#34;&gt;// 探针发送时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; udp_ping_rtt;              &lt;span style=&#34;color:#75715e&#34;&gt;// 往返时间 (微秒)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// MTU 探测
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mtuprobes;                  &lt;span style=&#34;color:#75715e&#34;&gt;// MTU 探针计数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16_t&lt;/span&gt; mtu;                   &lt;span style=&#34;color:#75715e&#34;&gt;// 当前 MTU
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16_t&lt;/span&gt; minmtu;                &lt;span style=&#34;color:#75715e&#34;&gt;// 最小 MTU
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16_t&lt;/span&gt; maxmtu;                &lt;span style=&#34;color:#75715e&#34;&gt;// 最大 MTU
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 包大小统计
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; maxrecentlen;          &lt;span style=&#34;color:#75715e&#34;&gt;// 最近最大包长
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;3-udp-打洞流程&#34;&gt;3. UDP 打洞流程&lt;/h2&gt;
&lt;h3 id=&#34;详细步骤&#34;&gt;详细步骤&lt;/h3&gt;
&lt;h4 id=&#34;步骤-1-tcp-握手连接&#34;&gt;步骤 1: TCP 握手连接&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A                  Root Server                Node B
  |                        |                          |
  |------ TCP CONNECT ----&amp;gt;|                          |
  |                        |&amp;lt;------ TCP CONNECT ------|
  |                        |                          |
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;步骤-2-交换拓扑信息&#34;&gt;步骤 2: 交换拓扑信息&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A                  Root Server                Node B
  |                        |                          |
  |- ADD_EDGE A, Root ----&amp;gt;|                          |
  |                        | (Root learns A&amp;#39;s addr)   |
  |                        |                          |
  |                        |-- ADD_EDGE B, Root -----&amp;gt;|
  |                        |                          |
  |&amp;lt;- ADD_EDGE B, Root ----|                          |
  |   (A learns B exists)  |                          |
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;步骤-3-密钥交换&#34;&gt;步骤 3: 密钥交换&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A                  Root Server                Node B
  |                        |                          |
  |-- REQ_KEY A-&amp;gt;B -------&amp;gt;|                          |
  | (request B&amp;#39;s key)      |                          |
  |                        |---- REQ_KEY A-&amp;gt;B -------&amp;gt;|
  |                        |                          |
  |                        |&amp;lt;---- ANS_KEY B-&amp;gt;A -------|
  |                        | (B&amp;#39;s key)                |
  |&amp;lt;-- ANS_KEY B-&amp;gt;A -------|                          |
  | (forwarded by Root)    |                          |
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;步骤-4-udp-打洞-关键阶段&#34;&gt;步骤 4: UDP 打洞 (关键阶段)&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A (LAN: 192.168.1.100:1194)        Node B (LAN: 192.168.2.50:1194)
  |                                       |
  | A knows B&amp;#39;s public: 203.0.113.20:5001 | B knows A&amp;#39;s public: 203.0.113.10:5000
  |                                       |
  +-- UDP Probe (TYPE=0) ----+            |
  |   NAT A opens hole:      |            |
  |   LAN1194 &amp;lt;-&amp;gt; WAN5000    |            |
  |                          +-----------&amp;gt;| Forward to B&amp;#39;s LAN
  |                                       | B&amp;#39;s NAT opened
  |                          +------ UDP Reply (TYPE=1) --+
  |                          |  NAT B: LAN1194 &amp;lt;-&amp;gt; WAN5001|
  |                          |                            |
  |&amp;lt;-- UDP Reply received &amp;lt;--+                            |
  |    From: 203.0.113.20:5001                            |
  |    NAT A allows reply (hole remains open)             |
  |                                       |
  v (Both sides confirm UDP reachable)
  
  udp_confirmed = true [SUCCESS]
  Both NAT holes established and verified
  Start direct UDP communication
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;步骤-5-直接-udp-通信&#34;&gt;步骤 5: 直接 UDP 通信&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A                                            Node B
  |                                                |
  |-------- Encrypted VPN packet (UDP) -----------&amp;gt;|
  | (No longer through Root, direct forward)       |
  |                                                |
  |&amp;lt;------- Encrypted VPN packet (UDP) ------------|
  |                                                |
  |--------- High-speed bidirectional flow -------&amp;gt;|
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;代码流程示意&#34;&gt;代码流程示意&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 1. 发送 UDP 探针
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_udp_probe_packet&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n, &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; len) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;vpn_packet_t&lt;/span&gt; packet;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;DATA&lt;/span&gt;(packet)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// 探针请求类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 发送到 n 的公网地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;send_udppacket&lt;/span&gt;(n, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;packet);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 标记已发送
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.ping_sent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;gettimeofday&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;udp_ping_sent, NULL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 2. 接收探针回复
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;udp_probe_h&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n, &lt;span style=&#34;color:#66d9ef&#34;&gt;vpn_packet_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;packet, &lt;span style=&#34;color:#66d9ef&#34;&gt;length_t&lt;/span&gt; len) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 收到对方的探针回复
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;  &lt;span style=&#34;color:#75715e&#34;&gt;// ✓ 双向 UDP 已建立
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 缓存这个 UDP 地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address_cache) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address_cache &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open_address_cache&lt;/span&gt;(n);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;add_recent_address&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address_cache, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connection&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;4-地址缓存机制&#34;&gt;4. 地址缓存机制&lt;/h2&gt;
&lt;h3 id=&#34;缓存结构&#34;&gt;缓存结构&lt;/h3&gt;
&lt;p&gt;为了避免每次都进行 DNS 查询，TINC 维护一个最近地址缓存。缓存采用 MRU（Most Recently Used）策略，保留最近 8 个已验证的 UDP 地址：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define MAX_CACHED_ADDRESSES 8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;address_cache_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;node;              &lt;span style=&#34;color:#75715e&#34;&gt;// 关联节点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;splay_tree_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;config_tree; &lt;span style=&#34;color:#75715e&#34;&gt;// 配置树
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;config_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;cfg;             &lt;span style=&#34;color:#75715e&#34;&gt;// 当前配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addrinfo &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;ai;              &lt;span style=&#34;color:#75715e&#34;&gt;// 地址信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addrinfo &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;aip;             &lt;span style=&#34;color:#75715e&#34;&gt;// 当前指针
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; tried;        &lt;span style=&#34;color:#75715e&#34;&gt;// 尝试计数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 最近 8 个已验证的 UDP 地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; version;  &lt;span style=&#34;color:#75715e&#34;&gt;// 缓存版本
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; used;     &lt;span style=&#34;color:#75715e&#34;&gt;// 已使用数量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;sockaddr_t&lt;/span&gt; address[&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;]; &lt;span style=&#34;color:#75715e&#34;&gt;// 地址数组 (最近优先)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;address_cache_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;缓存操作&#34;&gt;缓存操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;添加地址&lt;/strong&gt;：当成功通过 UDP 接收到来自某个地址的包时，将该地址加入缓存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MRU 策略&lt;/strong&gt;：最近使用的地址移到第 0 位，其他地址向后移动&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;获取地址&lt;/strong&gt;：优先尝试缓存的地址，避免 DNS 查询&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重置缓存&lt;/strong&gt;：新连接尝试时清空缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;5-nat-类型检测与适应&#34;&gt;5. NAT 类型检测与适应&lt;/h2&gt;
&lt;h3 id=&#34;常见-nat-类型&#34;&gt;常见 NAT 类型&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;NAT 类型&lt;/th&gt;
          &lt;th&gt;打洞成功率&lt;/th&gt;
          &lt;th&gt;特点&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Full Cone NAT&lt;/td&gt;
          &lt;td&gt;✅ 95-99%&lt;/td&gt;
          &lt;td&gt;任何外部地址都能回复&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Address-Restricted Cone&lt;/td&gt;
          &lt;td&gt;✅ 70-90%&lt;/td&gt;
          &lt;td&gt;只有之前通信过的源 IP 能回复&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Port-Restricted Cone&lt;/td&gt;
          &lt;td&gt;⚠️ 30-60%&lt;/td&gt;
          &lt;td&gt;只有特定的源 IP:端口能回复&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Symmetric NAT&lt;/td&gt;
          &lt;td&gt;❌ 0-5%&lt;/td&gt;
          &lt;td&gt;每个目标地址分配不同源端口，无法打洞&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;tinc-的适应机制&#34;&gt;TINC 的适应机制&lt;/h3&gt;
&lt;p&gt;TINC 在协议选项中编码节点的特性，根据选项决定通信策略：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define OPTION_INDIRECT    0x0001  &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 间接连接（已知是 Symmetric NAT）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define OPTION_TCPONLY     0x0002  &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 仅 TCP，不支持 UDP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define OPTION_PMTU_DISCOVERY 0x0004
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define OPTION_CLAMP_MSS   0x0008
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 根据选项决定使用策略:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(e&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;options &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; OPTION_TCPONLY) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 强制使用 TCP 中继
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;use_tcp_relay&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(e&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;options &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; OPTION_INDIRECT) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 尝试打洞，但可能需要中继
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;attempt_udp_hole_punch&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 尽量使用 UDP 直连
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;prefer_udp_direct&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;6-udp-数据传输优化&#34;&gt;6. UDP 数据传输优化&lt;/h2&gt;
&lt;h3 id=&#34;发送策略&#34;&gt;发送策略&lt;/h3&gt;
&lt;p&gt;TINC 发送包时会检查 UDP 连接状态，优先使用最快的路径：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_udppacket&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n, &lt;span style=&#34;color:#66d9ef&#34;&gt;vpn_packet_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;origpkt) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ✓ 已确认 UDP 可用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 1. 使用缓存的 UDP 地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sockaddr_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;sa &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_recent_address&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address_cache);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;sa) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// 缓存无有效地址，降级到 TCP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; use_tcp;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 2. 加密包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;encrypt_packet&lt;/span&gt;(n, origpkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 3. 直接通过 UDP 发送
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;sendto&lt;/span&gt;(udp_socket, pkt, len, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, sa, salen);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// UDP 未确认，使用 TCP 中继
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        use_tcp:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;send_tcppacket&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connection, origpkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;接收处理&#34;&gt;接收处理&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;receive_udppacket&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n, &lt;span style=&#34;color:#66d9ef&#34;&gt;vpn_packet_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;inpkt) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 收到 UDP 包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 1. 更新最近地址缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;add_recent_address&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;address_cache, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;peer_addr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 2. 解密
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;decrypt_packet&lt;/span&gt;(n, inpkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 3. 路由转发
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;route_packet&lt;/span&gt;(inpkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 4. 如果是首次 UDP 通信
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;logger&lt;/span&gt;(LOG_INFO, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;UDP direct connection established with %s&amp;#34;&lt;/span&gt;, n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;name);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;7-超时与降级处理&#34;&gt;7. 超时与降级处理&lt;/h2&gt;
&lt;h3 id=&#34;超时配置&#34;&gt;超时配置&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 参数配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;timeout_t&lt;/span&gt; udp_probe_timeout;    &lt;span style=&#34;color:#75715e&#34;&gt;// 探针超时
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define UDP_PROBE_INTERVAL 3    &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 3 秒发送一次
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define UDP_PROBE_TIMEOUT 10    &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 10 秒未响应即超时
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 打洞流程时间线
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;s   &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;├─&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;发送第&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;个&lt;/span&gt; UDP &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;探针&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;  status.ping_sent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;s   &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;├─&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;发送第&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;个&lt;/span&gt; UDP &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;探针&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;s   &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;├─&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;发送第&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;个&lt;/span&gt; UDP &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;探针&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;s  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;├─&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;超时检查&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (now &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; last_udp_reply &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;s)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;      status.udp_confirmed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; false
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; to TCP relay
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;├─&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;继续定期发送&lt;/span&gt; UDP &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;探针尝试重连&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;降级逻辑&#34;&gt;降级逻辑&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// TCP/UDP 混合转发 - 智能选择最优路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_packet_to_node&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n, &lt;span style=&#34;color:#66d9ef&#34;&gt;vpn_packet_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;pkt) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 优先级 1: UDP 直连（最快）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.udp_confirmed &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; is_recent_success) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_udppacket&lt;/span&gt;(n, pkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 优先级 2: SPTPS over UDP（新式，安全）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.sptps) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_sptps_udppacket&lt;/span&gt;(n, pkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 优先级 3: TCP 转发（可靠，较慢）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connection &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connection&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;status.active) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_tcppacket&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connection, pkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 优先级 4: 通过其他节点中继（绕行）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;via &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;via &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; n) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_via_relay&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;via, n, pkt);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 无法送达
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;8-mtu-探测与优化&#34;&gt;8. MTU 探测与优化&lt;/h2&gt;
&lt;h3 id=&#34;mtu-发现流程&#34;&gt;MTU 发现流程&lt;/h3&gt;
&lt;p&gt;建立 UDP 直连后，TINC 会自动探测最优 MTU 大小，避免包分片导致的性能下降。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. 初始 MTU = 1500 (以太网标准)

2. 发送递增大小的探针包
   ├─ 1500 字节 ─→ OK ✓
   ├─ 1450 字节 ─→ OK ✓
   ├─ 1400 字节 ─→ OK ✓
   ├─ 1350 字节 ─→ ICMP Fragmented (失败)
   │
   └─ 回退: MTU = 1350 + padding

3. 最终 MTU 确定后
   └─ 所有包都限制在该大小内
   └─ 避免分片，提高性能
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;代码实现：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mtu_probe_timeout_handler&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;node_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;mtuprobes &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// MTU 已确定，不需要探测
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;mtuprobes&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; MAX_PROBES) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 探针已发送足够次数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;try_fix_mtu&lt;/span&gt;(n);  &lt;span style=&#34;color:#75715e&#34;&gt;// 确定最终 MTU
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 继续发送 MTU 探针
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;maxmtu &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;mtuprobes &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;128&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;send_udp_probe_packet&lt;/span&gt;(n, len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;9-协议扩展-udp_info&#34;&gt;9. 协议扩展 (UDP_INFO)&lt;/h2&gt;
&lt;p&gt;TINC 通过 &lt;code&gt;UDP_INFO&lt;/code&gt; 协议消息来交换 UDP 地址信息。这个消息在元协议（Meta Protocol，基于 TCP）中传输。&lt;/p&gt;
&lt;h3 id=&#34;udp_info-消息格式&#34;&gt;UDP_INFO 消息格式&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: UDP_INFO originator_node address_info

流程:
1. Node A → Root: &amp;#34;UDP_INFO myself 203.0.113.10:5000&amp;#34;
   (告诉 Root 我的 UDP 地址)

2. Root → Node B: &amp;#34;UDP_INFO nodeA 203.0.113.10:5000&amp;#34;
   (告诉 B, A 的 UDP 地址)

3. Node B → Root: &amp;#34;UDP_INFO myself 203.0.113.20:5001&amp;#34;

4. Root → Node A: &amp;#34;UDP_INFO nodeB 203.0.113.20:5001&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;实现优势&#34;&gt;实现优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;内网节点无法自己检测公网地址，需要第三方帮助&lt;/li&gt;
&lt;li&gt;UDP_INFO 机制允许节点告诉其他节点自己的公网 UDP 地址&lt;/li&gt;
&lt;li&gt;这是打洞成功的必要条件&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;10-实际场景分析&#34;&gt;10. 实际场景分析&lt;/h2&gt;
&lt;h3 id=&#34;场景-a-双方都在-full-cone-nat-后&#34;&gt;场景 A: 双方都在 Full Cone NAT 后&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A (10.0.0.5:1194)          Node B (10.0.0.6:1194)
NAT: 203.0.113.10:5000          NAT: 203.0.113.20:5001

时间线:
0ms   A UDP 包 → Root → Root 转发给 B
      NAT A 记录: 内网 1194 ↔ 外网 5000 ↔ 203.0.113.20:5001

5ms   B 收到 A 的探针
      回复 UDP 包 → 203.0.113.10:5000
      NAT B 记录: 内网 1194 ↔ 外网 5001 ↔ 203.0.113.10:5000

10ms  A 收到 B 的回复
      udp_confirmed = true ✓
      后续所有包直接交换，不经过 Root

成功率: ✓ 100% (Full Cone NAT 最容易打洞成功)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;场景-b-一方在-symmetric-nat-后&#34;&gt;场景 B: 一方在 Symmetric NAT 后&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A (Symmetric NAT)          Node B (Full Cone NAT)

A 的 NAT 行为:
每次发送到不同地址时使用不同的源端口
├─ → 203.0.113.20 from 5000
├─ → 203.0.113.30 from 5001
└─ → 203.0.113.40 from 5002

问题:
Root 看到多个不同的源端口，无法告诉 B 一个确定的 UDP 地址

结果: UDP 打洞失败 ❌
降级: 使用 TCP 中继
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;11-性能指标&#34;&gt;11. 性能指标&lt;/h2&gt;
&lt;h3 id=&#34;典型延迟与吞吐量&#34;&gt;典型延迟与吞吐量&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;TCP 中继路径&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;延迟：100-500ms（往返）&lt;/li&gt;
&lt;li&gt;吞吐量：50-200 Mbps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;UDP 直连路径&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;延迟：10-100ms（往返，取决于网络）&lt;/li&gt;
&lt;li&gt;吞吐量：200-1000+ Mbps（接近原生网络）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;吞吐量提升&lt;/strong&gt;：UDP 直连相比 TCP 中继提升 &lt;strong&gt;5-10 倍&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;成功率统计&#34;&gt;成功率统计&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;NAT 场景&lt;/th&gt;
          &lt;th&gt;打洞成功率&lt;/th&gt;
          &lt;th&gt;降级方案&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Full Cone NAT&lt;/td&gt;
          &lt;td&gt;95-99%&lt;/td&gt;
          &lt;td&gt;TCP 中继&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Restricted Cone&lt;/td&gt;
          &lt;td&gt;70-90%&lt;/td&gt;
          &lt;td&gt;TCP 中继&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Port-Restricted&lt;/td&gt;
          &lt;td&gt;30-60%&lt;/td&gt;
          &lt;td&gt;TCP 中继&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Symmetric NAT&lt;/td&gt;
          &lt;td&gt;0-5%&lt;/td&gt;
          &lt;td&gt;TCP 中继（必须）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;12-调试与监控&#34;&gt;12. 调试与监控&lt;/h2&gt;
&lt;h3 id=&#34;查看打洞状态&#34;&gt;查看打洞状态&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 运行时查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tinc -n myvpn info &amp;lt;node&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 输出示例:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Node: remotenode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Address: 203.0.113.20 port &lt;span style=&#34;color:#ae81ff&#34;&gt;655&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Cipher: aes-256-cbc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Digest: sha512
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Magic: &lt;span style=&#34;color:#ae81ff&#34;&gt;12345678&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Status: active validkey udp_confirmed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# udp_confirmed 出现 → UDP 直连已建立 ✓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;调试日志&#34;&gt;调试日志&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 启用 UDP 调试&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUST_LOG&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;debug tincd -n myvpn -d DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 关键日志:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending UDP probe to nodeB &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;203.0.113.20:1194&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Got UDP probe reply from nodeB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; UDP direct connection confirmed with nodeB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Caching recent address &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; nodeB: 203.0.113.20:1194
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; MTU &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; nodeB: &lt;span style=&#34;color:#ae81ff&#34;&gt;1372&lt;/span&gt; bytes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;网络捕获&#34;&gt;网络捕获&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 捕获 UDP 直连包&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tcpdump -i eth0 -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;udp port 1194&amp;#39;&lt;/span&gt; -w vpn.pcap
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 分析包内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wireshark vpn.pcap
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 查看: 源/目标 IP:端口、包大小、发送频率&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;13-常见问题与解决&#34;&gt;13. 常见问题与解决&lt;/h2&gt;
&lt;h3 id=&#34;问题-1-udp-打洞失败始终显示-tcp&#34;&gt;问题 1: UDP 打洞失败，始终显示 TCP&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;诊断&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tinc -n myvpn info remotenode | grep -i udp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：无 &lt;code&gt;udp_confirmed&lt;/code&gt; 标志&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因可能&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;节点在 Symmetric NAT 后&lt;/li&gt;
&lt;li&gt;防火墙阻止 UDP&lt;/li&gt;
&lt;li&gt;ISP 限制 UDP&lt;/li&gt;
&lt;li&gt;路由策略问题&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确认两节点都支持 UDP&lt;/li&gt;
&lt;li&gt;检查防火墙规则：&lt;code&gt;sudo ufw allow 1194/udp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;尝试 UPnP 端口映射（如支持）&lt;/li&gt;
&lt;li&gt;确认 UDP_INFO 消息交换成功&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;问题-2-间歇性-udp-中断&#34;&gt;问题 2: 间歇性 UDP 中断&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：UDP 时而工作，时而不工作&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;网络波动导致超时&lt;/li&gt;
&lt;li&gt;NAT 刷新时间过短&lt;/li&gt;
&lt;li&gt;MTU 设置不当导致分片丢失&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;降低 UDP 探针超时：&lt;code&gt;udp_probe_timeout = 15&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;增加打洞频率：&lt;code&gt;udp_probe_interval = 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;调整 MTU：&lt;code&gt;minmtu = 1280&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;问题-3-mtu-不匹配&#34;&gt;问题 3: MTU 不匹配&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：大包通过 UDP 时丢失&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：中间网络 MTU &amp;lt; 节点配置的 MTU&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;启用 MTU 自动探测&lt;/li&gt;
&lt;li&gt;手动配置：&lt;code&gt;PMTU_SIZE 1280&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;启用 PMTU 发现：DF 位设置&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;TINC 的 UDP 打洞机制展示了如何在复杂的 NAT 环境下实现高效的 P2P 通信。关键设计包括：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心流程&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;TCP 握手 + 拓扑同步
    ↓
UDP_INFO 交换公网地址
    ↓
同时发送 UDP 探针（打洞）
    ↓
建立双向 UDP 连接
    ↓
优先使用 UDP 直连
    ↓
失败自动降级到 TCP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;关键特性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ 自适应 NAT 类型&lt;/li&gt;
&lt;li&gt;✅ 地址缓存优化&lt;/li&gt;
&lt;li&gt;✅ 动态 MTU 发现&lt;/li&gt;
&lt;li&gt;✅ 失败自动降级&lt;/li&gt;
&lt;li&gt;✅ 支持 IPv4 和 IPv6&lt;/li&gt;
&lt;li&gt;✅ 低延迟高吞吐量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;性能提升&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;连接方式&lt;/th&gt;
          &lt;th&gt;吞吐量&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;TCP 中继&lt;/td&gt;
          &lt;td&gt;50-200 Mbps&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;UDP 直连&lt;/td&gt;
          &lt;td&gt;200-1000 Mbps&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;提升倍数&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;5-10 倍&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这种精心设计的适应性系统使 TINC 在各种网络环境下都能提供最优的性能，是现代 VPN 软件的最佳实践。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;系列预告&#34;&gt;系列预告&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;第一篇：&lt;a href=&#34;../20260108-tinc-protocol-analysis&#34;&gt;TINC 协议深度解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;第二篇：&lt;a href=&#34;../20260108-tinc-module-architecture&#34;&gt;TINC 模块架构与代码组织&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;第三篇：UDP 打洞机制&lt;/li&gt;
&lt;li&gt;第四篇：&lt;a href=&#34;../20260108-tinc-modules-reference&#34;&gt;TINC 项目 - 模块依赖与数据流图&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>TINC 源码解读 第一篇 - 协议深度解析</title>
      <link>/posts/20260108-tinc-protocol-analysis/</link>
      <pubDate>Thu, 08 Jan 2026 08:00:00 +0800</pubDate>
      
      <guid>/posts/20260108-tinc-protocol-analysis/</guid>
      <description>&lt;p&gt;本文是 TINC 源码解读系列的第一篇，重点分析 TINC 的协议设计。TINC 采用分层架构，使用 Meta Protocol 处理控制平面，Packet Protocol 处理数据平面，SPTPS 提供额外的加密保证。&lt;/p&gt;</description>
      <content>&lt;p&gt;本文是 TINC 源码解读系列的第一篇，重点分析 TINC 的协议设计。TINC 采用分层架构，使用 Meta Protocol 处理控制平面，Packet Protocol 处理数据平面，SPTPS 提供额外的加密保证。&lt;/p&gt;
&lt;h2 id=&#34;1-协议概览&#34;&gt;1. 协议概览&lt;/h2&gt;
&lt;p&gt;TINC 使用两个独立的协议层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Meta Protocol&lt;/strong&gt; - TCP 连接上的文本协议（连接建立、拓扑交换）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Packet Protocol&lt;/strong&gt; - UDP 连接上的二进制协议（数据包转发）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;协议版本&#34;&gt;协议版本&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PROT_MAJOR &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;17&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 主版本号（不兼容时变化）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PROT_MINOR &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 次版本号（向后兼容）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;2-meta-协议---控制平面&#34;&gt;2. Meta 协议 - 控制平面&lt;/h2&gt;
&lt;h3 id=&#34;消息类型枚举&#34;&gt;消息类型枚举&lt;/h3&gt;
&lt;p&gt;Meta Protocol 定义了 20 多种消息类型，按功能分类：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 认证阶段
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ID              &lt;span style=&#34;color:#75715e&#34;&gt;// 连接标识
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;METAKEY         &lt;span style=&#34;color:#75715e&#34;&gt;// 元数据密钥交换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHALLENGE       &lt;span style=&#34;color:#75715e&#34;&gt;// 密码挑战
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHAL_REPLY      &lt;span style=&#34;color:#75715e&#34;&gt;// 挑战回复
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ACK             &lt;span style=&#34;color:#75715e&#34;&gt;// 确认
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 连接管理
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;STATUS          &lt;span style=&#34;color:#75715e&#34;&gt;// 状态信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR           &lt;span style=&#34;color:#75715e&#34;&gt;// 错误消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TERMREQ         &lt;span style=&#34;color:#75715e&#34;&gt;// 终止请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 保活
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PING            &lt;span style=&#34;color:#75715e&#34;&gt;// 心跳 Ping
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PONG            &lt;span style=&#34;color:#75715e&#34;&gt;// 心跳 Pong
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 拓扑交换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ADD_SUBNET      &lt;span style=&#34;color:#75715e&#34;&gt;// 添加子网
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DEL_SUBNET      &lt;span style=&#34;color:#75715e&#34;&gt;// 删除子网
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ADD_EDGE        &lt;span style=&#34;color:#75715e&#34;&gt;// 添加边/链路
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DEL_EDGE        &lt;span style=&#34;color:#75715e&#34;&gt;// 删除边/链路
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 密钥交换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;KEY_CHANGED     &lt;span style=&#34;color:#75715e&#34;&gt;// 密钥已更改
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REQ_KEY         &lt;span style=&#34;color:#75715e&#34;&gt;// 请求加密密钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ANS_KEY         &lt;span style=&#34;color:#75715e&#34;&gt;// 应答加密密钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 数据转发
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PACKET          &lt;span style=&#34;color:#75715e&#34;&gt;// TCP 转发数据包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Tinc 1.1+ 扩展
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REQ_PUBKEY      &lt;span style=&#34;color:#75715e&#34;&gt;// 请求公钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ANS_PUBKEY      &lt;span style=&#34;color:#75715e&#34;&gt;// 应答公钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SPTPS_PACKET    &lt;span style=&#34;color:#75715e&#34;&gt;// SPTPS 加密包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UDP_INFO        &lt;span style=&#34;color:#75715e&#34;&gt;// UDP 地址信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MTU_INFO        &lt;span style=&#34;color:#75715e&#34;&gt;// MTU 信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;核心消息格式&#34;&gt;核心消息格式&lt;/h3&gt;
&lt;h4 id=&#34;id-消息---连接起点&#34;&gt;ID 消息 - 连接起点&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: ID name version

示例: 0 mynode 17.7

含义:
├─ 0        - 消息类型标识
├─ mynode   - 节点名称
└─ 17.7     - 协议版本 (MAJOR.MINOR)
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;metakey-消息---加密参数协商&#34;&gt;METAKEY 消息 - 加密参数协商&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: METAKEY cipher digest keylength [加密的密钥]

示例: 1 aes-256-cbc sha512 32 [128字节加密数据]

含义:
├─ 1              - 消息类型
├─ aes-256-cbc    - 对称加密算法
├─ sha512         - HMAC 摘要算法
└─ 32             - 密钥长度（字节）

特点:
└─ 加密密钥使用对方的公钥进行 RSA 加密
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;challenge-消息---身份验证&#34;&gt;CHALLENGE 消息 - 身份验证&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: CHALLENGE &amp;lt;hash&amp;gt;

验证过程:
1. 接收方生成随机 challenge (16 字节)
2. 计算 hash = HMAC-SHA256(challenge, shared_key)
3. 发送 CHALLENGE hash
4. 发起方计算相同的 hash
5. 验证 hash 相等
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;add_edge-消息---拓扑通告&#34;&gt;ADD_EDGE 消息 - 拓扑通告&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: ADD_EDGE node_from node_to address port weight options

示例: ADD_EDGE nodeA nodeB 192.168.1.100 655 100 0

解析:
├─ ADD_EDGE        - 消息类型
├─ nodeA           - 源节点
├─ nodeB           - 目标节点
├─ 192.168.1.100   - 目标地址（实际网络地址）
├─ 655             - UDP 端口
├─ 100             - 权重（用于 Dijkstra）
└─ 0               - 选项标志

选项标志:
├─ 0x0001: OPTION_INDIRECT   (间接连接)
├─ 0x0002: OPTION_TCPONLY    (仅 TCP)
├─ 0x0004: OPTION_PMTU_DISCOVERY
└─ 0x0008: OPTION_CLAMP_MSS
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;add_subnet-消息---子网通告&#34;&gt;ADD_SUBNET 消息 - 子网通告&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: ADD_SUBNET owner_node network/prefixlength

IPv4 示例: ADD_SUBNET node1 192.168.1.0/24
IPv6 示例: ADD_SUBNET node1 2001:db8::/32
MAC  示例: ADD_SUBNET node1 ff:ff:ff:ff:ff:ff

用途:
└─ 通告该节点负责路由的子网地址
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;req_key-和-ans_key---数据加密密钥交换&#34;&gt;REQ_KEY 和 ANS_KEY - 数据加密密钥交换&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;REQ_KEY 格式: REQ_KEY originator destination
含义: originator 请求与 destination 的通信密钥

ANS_KEY 格式: ANS_KEY from to cipher_id digest_id key mac_length
示例: ANS_KEY nodeB nodeA 1 3 4ae0b0a82d6e0078 16

解析:
├─ nodeB         - 密钥所有者
├─ nodeA         - 请求方
├─ 1             - 对称加密算法 ID
├─ 3             - HMAC 摘要算法 ID
├─ 4ae0...78     - 128 位加密密钥
└─ 16            - MAC 长度
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;ping-和-pong---保活消息&#34;&gt;PING 和 PONG - 保活消息&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PING 格式: PING [随机数]
PONG 格式: PONG [随机数]

用途:
├─ 检测连接是否仍然活跃
├─ 防止连接因不活动而被关闭
├─ 检测单向连接失败
└─ 通过随机数防止已知明文攻击
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;3-meta-协议握手流程&#34;&gt;3. Meta 协议握手流程&lt;/h2&gt;
&lt;h3 id=&#34;完整的连接建立过程&#34;&gt;完整的连接建立过程&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;发起方                      响应方
─────────────────────────────────────

TCP 连接 ─────────────→

                  ← 接受连接

发送 ID ──────────→
                  
                  ✓ 验证版本

                  ← 发送 ID

✓ 验证版本

发送 METAKEY ─────→
(加密的元密钥)

                  ✓ 解密元密钥

                  ← 发送 METAKEY

✓ 解密元密钥

从此以后，所有通信使用
协商的加密密钥加密

发送 CHALLENGE ────→
(hash = HMAC(...))

                  ✓ 验证 hash

                  ← CHAL_REPLY

✓ 双向认证成功

发送 ACK ─────────→

                  ← ACK

✓ 连接完全建立
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;握手状态机&#34;&gt;握手状态机&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[TCP Connected]
    ↓
[ID Exchanged]
├─ 校验版本号
├─ 确定协议版本
└─ 准备加密
    ↓
[METAKEY Exchanged]
├─ 选择加密算法
├─ 交换元数据密钥
└─ 开始加密通信
    ↓
[CHALLENGE Issued]
├─ 单向认证
├─ 验证对方公钥
└─ 生成共享密钥
    ↓
[CHAL_REPLY Received]
├─ 双向认证完成
├─ 确认对方身份
└─ 建立信任
    ↓
[ACK Sent/Received]
├─ 连接活跃
├─ 准备数据交换
└─ 拓扑同步
    ↓
[ACTIVE]
├─ 双向 PING/PONG
├─ 转发 ADD_EDGE
├─ 转发 ADD_SUBNET
├─ 转发 REQ_KEY/ANS_KEY
└─ 转发数据包
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;4-packet-protocol---数据平面&#34;&gt;4. Packet Protocol - 数据平面&lt;/h2&gt;
&lt;h3 id=&#34;udp-数据包格式&#34;&gt;UDP 数据包格式&lt;/h3&gt;
&lt;h4 id=&#34;加密前的包结构&#34;&gt;加密前的包结构&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Byte:  0-3         4-...           ...      ...+16
      +----------+---------------+--------+--------+
      | seqno    | payload       | padding| MAC    |
      | (32 bit) | (VPN data)    |        |(256bit)|
      +----------+---------------+--------+--------+
      +-----------------------------------+--------+
              Covered by HMAC
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;加密后的完整-udp-包&#34;&gt;加密后的完整 UDP 包&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;+-------------------------------------------------+
| UDP Header (20 bytes)                           |
| +--- Source IP                                  |
| +--- Dest IP                                    |
| +--- Source Port (1194 or configured)           |
| +--- Dest Port                                  |
+-------------------------------------------------+
| Encrypted Payload (variable length)             |
| +--- Nonce/IV (optional)                        |
| +--- Ciphertext:                                |
| |    +--- seqno (sequence number)               |
| |    +--- VPN data (virtual network packet)     |
| |    +--- HMAC (message authentication code)    |
| +--- Authentication Tag (AEAD encryption)       |
+-------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;vpn-数据包结构&#34;&gt;VPN 数据包结构&lt;/h3&gt;
&lt;p&gt;虚拟网络上传输的是标准 Ethernet Frame：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;+----------+----------+----------+---------+-----+
| Dest MAC | Src MAC  | EtherType| Payload | CRC |
| (6 Byte) | (6 Byte) | (2 Byte) |(var len)| (4) |
+----------+----------+----------+---------+-----+

EtherType:
+-- 0x0800: IPv4
+-- 0x86DD: IPv6
+-- 0x0806: ARP
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;支持的加密算法&#34;&gt;支持的加密算法&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;对称加密:
├─ ChaCha20-Poly1305  (推荐，AEAD)
├─ AES-256-GCM        (AEAD)
├─ AES-256-CBC        (需要 HMAC)
└─ null cipher        (调试用，无加密)

消息摘要:
├─ SHA512             (推荐)
├─ SHA256
├─ SHA1
└─ MD5 (遗留，不安全)

密钥长度:
├─ 256 bit (标准)
└─ 128 bit (弱密钥，不推荐)
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;5-sptps-协议---安全的点对点通信&#34;&gt;5. SPTPS 协议 - 安全的点对点通信&lt;/h2&gt;
&lt;h3 id=&#34;sptps-设计目的&#34;&gt;SPTPS 设计目的&lt;/h3&gt;
&lt;p&gt;SPTPS（Simple Peer-to-Peer Security）是一个轻量级的加密层，用于在 UDP 上建立直接的加密通道，不需要依赖 Meta Protocol 的握手。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;与 Meta Protocol 的区别&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meta Protocol: 文本协议，TCP，用于控制平面&lt;/li&gt;
&lt;li&gt;SPTPS: 二进制协议，UDP，用于数据平面持久化会话&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;sptps-记录类型&#34;&gt;SPTPS 记录类型&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define SPTPS_HANDSHAKE 128   &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 密钥交换和认证
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define SPTPS_ALERT 129       &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 警告或错误消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define SPTPS_CLOSE 130       &lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 应用程序关闭连接
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sptps-握手状态&#34;&gt;SPTPS 握手状态&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;enum&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sptps_state_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SPTPS_KEX &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,           &lt;span style=&#34;color:#75715e&#34;&gt;// 等待第一个密钥交换记录
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SPTPS_SECONDARY_KEX &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// 准备接收二次密钥交换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SPTPS_SIG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;,           &lt;span style=&#34;color:#75715e&#34;&gt;// 等待签名记录
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SPTPS_ACK &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;,           &lt;span style=&#34;color:#75715e&#34;&gt;// 等待确认记录
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;sptps_state_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sptps-密钥交换结构&#34;&gt;SPTPS 密钥交换结构&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sptps_kex_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8_t&lt;/span&gt; version;           &lt;span style=&#34;color:#75715e&#34;&gt;// 版本号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8_t&lt;/span&gt; nonce[&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;];         &lt;span style=&#34;color:#75715e&#34;&gt;// 随机数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8_t&lt;/span&gt; pubkey[&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;];        &lt;span style=&#34;color:#75715e&#34;&gt;// 临时 ECDH 公钥 (Curve25519)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 总大小: 65 字节
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sptps-握手流程&#34;&gt;SPTPS 握手流程&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Initiator                   Responder
─────────────────────────────────────

生成临时密钥对
(Curve25519 ECDH)

发送 HANDSHAKE ────────────→
  payload: kex1
  (version, nonce, pubkey)

                           收到 kex1
                           生成临时密钥对
                           计算共享密钥

                           ← 发送 HANDSHAKE
                             payload: kex2

收到 kex2
计算共享密钥

验证: ECDH(A_priv, B_pub)
    == ECDH(B_priv, A_pub) ✓

使用 PRF 派生会话密钥

发送签名记录 ───────────────→
  签署握手数据

                           验证签名

                           ← 发送 ACK 记录

验证 ACK
握手完成 ✓

双向 AEAD 加密通信开始
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;密钥派生&#34;&gt;密钥派生&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Shared_Secret = ECDH(my_private_key, peer_public_key)

Session_Keys = PRF-SHA512(
    Shared_Secret,
    label = &amp;#34;SPTPS key expansion&amp;#34; 
          + initiator_pubkey 
          + responder_pubkey
)

派生密钥 (共 64 字节):
├─ Key_A (32 字节): Initiator → Responder
├─ Key_B (32 字节): Responder → Initiator
└─ 每个方向使用不同密钥 (前向保密)
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;6-身份验证与安全机制&#34;&gt;6. 身份验证与安全机制&lt;/h2&gt;
&lt;h3 id=&#34;签名与验证&#34;&gt;签名与验证&lt;/h3&gt;
&lt;p&gt;每个节点都有持久的 ECDSA/EdDSA 密钥对：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;公钥位置: hosts 目录 ($TINC_HOME/hosts/nodename)
私钥位置: 配置目录 ($TINC_HOME/rsa_key.priv, ed25519_key.priv)

认证流程:
1. 交换 ID → 校验版本
2. 交换 METAKEY → 协商加密算法
3. 计算 challenge hash
4. 发送 CHALLENGE + CHAL_REPLY
5. 验证签名：数据已被对方的私钥签署
6. 发送 ACK

验证公钥:
├─ 从 hosts 目录读取对方的公钥
├─ 验证签名: Verify(signature, data, peer_pubkey)
├─ 若验证失败，拒绝连接
└─ 若验证成功，建立信任
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;重放保护&#34;&gt;重放保护&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Meta Protocol 重放保护&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;├─ past_request_tree 跟踪已处理的请求
├─ 相同请求在一定时间内被忽略
└─ 防止攻击者重复发送拓扑更新
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;数据包重放保护&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;├─ 序列号 (seqno): 递增计数器
├─ 验证 seqno &amp;gt; last_seqno
├─ 丢弃重复包
└─ 使用滑动窗口防止乱序问题
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;7-拓扑同步协议&#34;&gt;7. 拓扑同步协议&lt;/h2&gt;
&lt;h3 id=&#34;完整的拓扑交换过程&#34;&gt;完整的拓扑交换过程&lt;/h3&gt;
&lt;p&gt;当两个节点建立连接后，会进行完整的拓扑交换：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Node A ──────TCP──────→ Node B

1. 身份验证完成
   (ID, METAKEY, CHALLENGE, ACK)

2. Node A 发送其知道的拓扑:
   
   ADD_EDGE nodeA nodeC 10.0.0.3 655 50 0
   ADD_EDGE nodeA nodeD 10.0.0.4 655 100 0
   ADD_SUBNET nodeA 172.16.1.0/24
   ADD_SUBNET nodeC 172.16.2.0/24

3. Node B 发送其知道的拓扑:
   
   ADD_EDGE nodeB nodeE 10.0.0.5 655 50 0
   ADD_SUBNET nodeB 172.16.4.0/24
   ADD_SUBNET nodeE 172.16.5.0/24

4. 每当拓扑变化时：
   └─ 通过 ADD_EDGE/ADD_SUBNET 发布增量更新

5. 当边或节点失败时：
   └─ 广播 DEL_EDGE/DEL_SUBNET
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;拓扑维护机制&#34;&gt;拓扑维护机制&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;变化检测:
├─ 新节点加入 → ADD_EDGE + ADD_SUBNET
├─ 节点离开 → DEL_EDGE + DEL_SUBNET
├─ 链路权重变化 → ADD_EDGE (新权重)
└─ 密钥更新 → KEY_CHANGED

广播机制:
├─ 每个连接都转发其收到的拓扑消息
├─ 防环：记录消息源，不回复给源
└─ 定期同步：每个连接建立时交换完整拓扑
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;8-协议扩展-tinc-11&#34;&gt;8. 协议扩展 (Tinc 1.1+)&lt;/h2&gt;
&lt;h3 id=&#34;新增消息类型&#34;&gt;新增消息类型&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;REQ_PUBKEY / ANS_PUBKEY&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: REQ_PUBKEY from to [timestamp]
      ANS_PUBKEY from to [key_data]

用途:
├─ 请求/应答节点的公钥
├─ 用于拓展的密钥交换
└─ 支持在线密钥更新
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;UDP_INFO&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: UDP_INFO originator address:port

用途:
├─ 通告节点可用的 UDP 地址
├─ 支持多地址（IPv4, IPv6）
└─ 加速 UDP 直连建立
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;MTU_INFO&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式: MTU_INFO node mtu

用途:
├─ 通告路径的最大传输单元
├─ 支持 PMTU 发现
└─ 优化包大小
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SPTPS_PACKET&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;用途:
├─ 使用 SPTPS 加密的 TCP 包
├─ 在 meta 协议上层的加密
└─ 增强安全性
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;9-安全性分析&#34;&gt;9. 安全性分析&lt;/h2&gt;
&lt;h3 id=&#34;已实现的安全特性&#34;&gt;已实现的安全特性&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;特性&lt;/th&gt;
          &lt;th&gt;实现&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;前向保密 (PFS)&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;ECDH 临时密钥，会话独立&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;数据完整性&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;HMAC/AEAD 认证&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;数据机密性&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;对称加密 (AES/ChaCha20)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;重放保护&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;序列号 + 滑动窗口&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;身份认证&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;ECDSA/EdDSA 签名&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;密钥交换&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;ECDH (P-256/Curve25519)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;已知明文保护&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;✓&lt;/td&gt;
          &lt;td&gt;Ping/Pong 随机值&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;潜在的风险和对策&#34;&gt;潜在的风险和对策&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;风险&lt;/th&gt;
          &lt;th&gt;严重度&lt;/th&gt;
          &lt;th&gt;现状&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;密钥重用&lt;/td&gt;
          &lt;td&gt;中&lt;/td&gt;
          &lt;td&gt;支持密钥轮换 (KEY_CHANGED)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;时钟偏差&lt;/td&gt;
          &lt;td&gt;低&lt;/td&gt;
          &lt;td&gt;支持时钟偏差检测&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;实现漏洞&lt;/td&gt;
          &lt;td&gt;高&lt;/td&gt;
          &lt;td&gt;定期安全审计&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;算法选择&lt;/td&gt;
          &lt;td&gt;低&lt;/td&gt;
          &lt;td&gt;支持现代算法 (ChaCha20)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;密钥缓存&lt;/td&gt;
          &lt;td&gt;中&lt;/td&gt;
          &lt;td&gt;支持定期过期&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;10-连接到完整通信的时间线&#34;&gt;10. 连接到完整通信的时间线&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;时间   事件
──────────────────────────────────────────────
0ms    TCP 连接建立
       ↓
10ms   交换 ID (版本协商)
       ↓
20ms   交换 METAKEY (加密参数)
       ↓
30ms   发送 CHALLENGE (单向认证)
       ↓
40ms   接收 CHAL_REPLY (双向认证)
       ↓
50ms   从此以后所有通信加密
       ↓
60ms   交换 ADD_EDGE (拓扑)
       ↓
100ms  交换 ADD_SUBNET (子网)
       ↓
150ms  定期 PING/PONG (保活)
       ↓
∞      数据转发 &amp;amp; 拓扑维护
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;11-协议调试&#34;&gt;11. 协议调试&lt;/h2&gt;
&lt;h3 id=&#34;启用协议日志&#34;&gt;启用协议日志&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 启用详细的协议调试输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tincd -n myvpn -d DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 典型输出示例:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending ID to node1: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0 mynode 17.7&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending METAKEY to node1: cipher&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; digest&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; maclength&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending CHALLENGE to node1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending ACK to node1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending ADD_EDGE node1 node2 192.168.1.2 &lt;span style=&#34;color:#ae81ff&#34;&gt;655&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending ADD_SUBNET node2 172.16.2.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Sending PING to node1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;DEBUG&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Received PONG from node1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;网络包捕获&#34;&gt;网络包捕获&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 捕获 Meta Protocol (TCP 655)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tcpdump -i eth0 -n tcp port &lt;span style=&#34;color:#ae81ff&#34;&gt;655&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 捕获 Packet Protocol (UDP 1194)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tcpdump -i eth0 -n udp port &lt;span style=&#34;color:#ae81ff&#34;&gt;1194&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 使用 Wireshark 分析&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wireshark -i eth0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;TINC 协议是一个分层的、设计精良的 VPN 协议体系：&lt;/p&gt;
&lt;h3 id=&#34;核心设计&#34;&gt;核心设计&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Meta Protocol&lt;/strong&gt; (控制平面)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP 文本协议，用于控制和管理&lt;/li&gt;
&lt;li&gt;握手、认证、拓扑交换&lt;/li&gt;
&lt;li&gt;可靠但相对低频&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Packet Protocol&lt;/strong&gt; (数据平面)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UDP 二进制协议，用于数据转发&lt;/li&gt;
&lt;li&gt;高效、低延迟&lt;/li&gt;
&lt;li&gt;支持快速加密/解密&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SPTPS&lt;/strong&gt; (加密层)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;额外的 UDP 加密层&lt;/li&gt;
&lt;li&gt;前向保密和轻量级握手&lt;/li&gt;
&lt;li&gt;支持直接 P2P 加密通信&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;安全特性&#34;&gt;安全特性&lt;/h3&gt;
&lt;p&gt;✅ 现代密钥交换 (ECDH)&lt;br&gt;
✅ 强力数字签名 (ECDSA/EdDSA)&lt;br&gt;
✅ 前向保密 (PFS)&lt;br&gt;
✅ 重放保护&lt;br&gt;
✅ 完整性认证 (HMAC/AEAD)&lt;br&gt;
✅ 灵活的算法选择&lt;/p&gt;
&lt;h3 id=&#34;性能考虑&#34;&gt;性能考虑&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Meta Protocol 低频交互，控制开销小&lt;/li&gt;
&lt;li&gt;Packet Protocol 用 UDP，转发延迟低&lt;/li&gt;
&lt;li&gt;数据密钥缓存，避免频繁协商&lt;/li&gt;
&lt;li&gt;支持直接 P2P（避免中继）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种精心设计的两层协议架构使 TINC 成为一个既可靠又高效的 VPN 解决方案，是现代 VPN 软件的最佳实践。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;系列预告&#34;&gt;系列预告&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;第一篇：TINC 协议深度解析&lt;/li&gt;
&lt;li&gt;第二篇：&lt;a href=&#34;../20260108-tinc-module-architecture&#34;&gt;TINC 模块架构与代码组织&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;第三篇：&lt;a href=&#34;../20260108-tinc-udp-holepunching&#34;&gt;UDP 打洞机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;第四篇：&lt;a href=&#34;../20260108-tinc-modules-reference&#34;&gt;TINC 项目 - 模块依赖与数据流图&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
  </channel>
</rss>
