<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>网络排查 on 猫猫鱼的小窝</title>
    <link>https://cat.fjh1997.top/tags/%E7%BD%91%E7%BB%9C%E6%8E%92%E6%9F%A5/</link>
    <description>Recent content from 猫猫鱼的小窝</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    
    <managingEditor>xxx@example.com (catcatyu)</managingEditor>
    <webMaster>xxx@example.com (catcatyu)</webMaster>
    
    <copyright>本博客所有文章除特别声明外，均采用 BY-NC-SA 许可协议。转载请注明出处！</copyright>
    
    <lastBuildDate>Wed, 10 Jun 2026 12:40:15 +0800</lastBuildDate>
    
    
    <atom:link href="https://cat.fjh1997.top/tags/%E7%BD%91%E7%BB%9C%E6%8E%92%E6%9F%A5/atom.xml" rel="self" type="application/rss&#43;xml" />
    

    
    

    <item>
      <title>阿里云 ECS 控制台远程连接失败、云助手不响应、OSS 内网超时——Tailscale 与阿里云 100.x 段冲突排查全过程</title>
      <link>https://cat.fjh1997.top/posts/56136.html</link>
      <pubDate>Wed, 10 Jun 2026 12:40:15 &#43;0800</pubDate>
      <author>xxx@example.com (catcatyu)</author>
      <guid>https://cat.fjh1997.top/posts/56136.html</guid>
      <description>
        <![CDATA[<h1>阿里云 ECS 控制台远程连接失败、云助手不响应、OSS 内网超时——Tailscale 与阿里云 100.x 段冲突排查全过程</h1><p>作者：catcatyu（xxx@example.com）</p>
        
          <h2 id="一现象从控制台远程连接打不开开始">
<a class="header-anchor" href="#%e4%b8%80%e7%8e%b0%e8%b1%a1%e4%bb%8e%e6%8e%a7%e5%88%b6%e5%8f%b0%e8%bf%9c%e7%a8%8b%e8%bf%9e%e6%8e%a5%e6%89%93%e4%b8%8d%e5%bc%80%e5%bc%80%e5%a7%8b"></a>
一、现象：从控制台远程连接打不开开始
</h2><p>某天准备登一台跑在杭州地域的阿里云 ECS 改点东西，结果发现三件事同时挂了：</p>
<ol>
<li><strong>阿里云控制台的「远程连接」（Workbench / VNC）打不开</strong>，点击之后转圈，最后报 &ldquo;连接失败&rdquo;；</li>
<li><strong>「云助手」（Cloud Assistant / ECS Run Command）也不响应</strong>，下发任何命令都一直显示「执行中」，永远不返回结果；</li>
<li>业务侧报错：访问 <code>oss-cn-hangzhou-internal.aliyuncs.com</code>（OSS 内网域名）超时，对象读写全部 hang 住。</li>
</ol>
<p>但是奇怪的是：</p>
<ul>
<li><strong>SSH（外网公网 IP）还能正常登录</strong>；</li>
<li><code>ping 8.8.8.8</code>、<code>ping baidu.com</code> 都通；</li>
<li>业务里访问外网 API 也都正常。</li>
</ul>
<p>这就说明 ECS 本身和外网都没问题，<strong>坏的是阿里云自己的内网链路</strong>——控制台远程连接、云助手、OSS 内网域名，全都走阿里云内网 100.x 段。</p>
<blockquote>
<p>这台机器装了 Tailscale 自建 Headscale，这是个非常重要的伏笔。</p>
</blockquote>
<h2 id="二最小化复现定位到内网域名超时">
<a class="header-anchor" href="#%e4%ba%8c%e6%9c%80%e5%b0%8f%e5%8c%96%e5%a4%8d%e7%8e%b0%e5%ae%9a%e4%bd%8d%e5%88%b0%e5%86%85%e7%bd%91%e5%9f%9f%e5%90%8d%e8%b6%85%e6%97%b6"></a>
二、最小化复现：定位到内网域名超时
</h2><p>先确认 OSS 内网到底是 DNS 挂了还是网络挂了：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ nslookup oss-cn-hangzhou-internal.aliyuncs.com
</span></span><span class="line"><span class="cl">Name:   oss-cn-hangzhou-internal.aliyuncs.com
</span></span><span class="line"><span class="cl">Address: 100.118.28.52
</span></span><span class="line"><span class="cl">Address: 100.118.28.43
</span></span></code></pre></td></tr></table>
</div>
</div><p>DNS 没问题，解析出来两个 100.118.x.x 的地址。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ timeout <span class="m">10</span> telnet 100.118.28.52 <span class="m">443</span>
</span></span><span class="line"><span class="cl">Trying 100.118.28.52...
</span></span><span class="line"><span class="cl">（10 秒之后超时）
</span></span></code></pre></td></tr></table>
</div>
</div><p>TCP 不通。再 ping 一下 ECS metadata 服务器（这也是阿里云内部接口）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ping -c <span class="m">3</span> 100.100.2.136
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">0</span> received, 100% packet loss
</span></span></code></pre></td></tr></table>
</div>
</div><p>也不通。再试一下 metadata HTTP 接口：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl --connect-timeout <span class="m">5</span> http://100.100.100.200/latest/meta-data/region-id
</span></span><span class="line"><span class="cl">（超时，无输出）
</span></span></code></pre></td></tr></table>
</div>
</div><p>也不通。<strong>结论</strong>：凡是阿里云内网 <code>100.x.x.x</code> 段，全部访问不到。</p>
<p>到这里就明确了：阿里云控制台远程连接挂掉、云助手不响应，本质上跟 OSS 内网挂掉是<strong>同一个问题</strong>——它们都需要走 ECS 到 100.x 阿里云内网管控面的链路。</p>
<h2 id="三排查防火墙">
<a class="header-anchor" href="#%e4%b8%89%e6%8e%92%e6%9f%a5%e9%98%b2%e7%81%ab%e5%a2%99"></a>
三、排查防火墙
</h2><p>第一反应是 ufw 或者 iptables 把内网段拦了。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo ufw status verbose
</span></span><span class="line"><span class="cl">Status: inactive
</span></span></code></pre></td></tr></table>
</div>
</div><p>ufw 是关的。再看 iptables：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo iptables -L -v -n
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">Chain ts-input <span class="o">(</span><span class="m">1</span> references<span class="o">)</span>
</span></span><span class="line"><span class="cl"> pkts bytes target  prot opt in        out  <span class="nb">source</span>            destination
</span></span><span class="line"><span class="cl">    <span class="m">0</span>     <span class="m">0</span> ACCEPT  <span class="m">0</span>   --  lo         *    100.64.0.1        0.0.0.0/0
</span></span><span class="line"><span class="cl">    <span class="m">0</span>     <span class="m">0</span> RETURN  <span class="m">0</span>   --  !tailscale0 *   100.115.92.0/23   0.0.0.0/0
</span></span><span class="line"><span class="cl"> 113K 5983K DROP    <span class="m">0</span>   --  !tailscale0 *   100.64.0.0/10     0.0.0.0/0
</span></span><span class="line"><span class="cl"> 189K   26M ACCEPT  <span class="m">0</span>   --  tailscale0  *   0.0.0.0/0         0.0.0.0/0
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>罪魁祸首找到了</strong>。</p>
<p><code>ts-input</code> 是 Tailscale 自己装的 iptables 链，最关键的是这条：</p>
<pre tabindex="0"><code>DROP  !tailscale0  100.64.0.0/10  0.0.0.0/0
</code></pre><p>翻译一下：<strong>从不是 <code>tailscale0</code> 的网卡进来的包，只要源地址在 <code>100.64.0.0/10</code> 这个段里，全部丢掉。</strong></p>
<p>而这条 DROP 已经累积了 <strong>113000+ 个被丢的包，将近 6MB 流量</strong>——所有访问阿里云内网失败的包，都被这条规则吞了。</p>
<h2 id="四根因cgnat-段撞车">
<a class="header-anchor" href="#%e5%9b%9b%e6%a0%b9%e5%9b%a0cgnat-%e6%ae%b5%e6%92%9e%e8%bd%a6"></a>
四、根因：CGNAT 段撞车
</h2><p>为啥 Tailscale 要装这条 DROP？</p>
<p>Tailscale（包括自建 Headscale）默认使用 <strong><code>100.64.0.0/10</code> 这个 CGNAT（运营商级 NAT）地址段</strong>给 tailnet 里的节点分配虚拟 IP。这条 DROP 是 Tailscale 的反伪造防御：从外部网卡进来的包，源 IP 不可能是 tailnet 内部地址，如果有那就是欺骗，丢掉。</p>
<p><strong>但是！</strong> 阿里云华东 1（杭州）的内网管控面，<strong>也用了 100.x 段</strong>：</p>
<table>
  <thead>
      <tr>
          <th>资源</th>
          <th>地址段</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Tailscale CGNAT（虚拟）</td>
          <td><strong><code>100.64.0.0/10</code></strong>（即 <code>100.64.0.0</code> ～ <code>100.127.255.255</code>）</td>
      </tr>
      <tr>
          <td>阿里云 ECS metadata</td>
          <td><code>100.100.100.200</code>、<code>100.100.2.136</code></td>
      </tr>
      <tr>
          <td>阿里云 OSS 内网（杭州）</td>
          <td><code>100.118.28.0/24</code> 等</td>
      </tr>
      <tr>
          <td>阿里云云助手/Workbench 回调</td>
          <td>部分 100.x 段</td>
      </tr>
  </tbody>
</table>
<p>它们<strong>全在 <code>100.64.0.0/10</code> 区间内</strong>。</p>
<p>链路是这样的：</p>
<ol>
<li>ECS 发起对 <code>100.118.28.52</code>（OSS 内网）的连接，包从 eth0 出去，OSS 服务回包；</li>
<li>回包从 eth0 进来，源 IP 是 <code>100.118.28.52</code>；</li>
<li>进了 <code>INPUT</code> 链 → <code>ts-input</code> 链；</li>
<li>源 IP 在 <code>100.64.0.0/10</code> 里 + 进来的网卡不是 <code>tailscale0</code> → <strong>DROP</strong>；</li>
<li>应用层看到的就是连接超时。</li>
</ol>
<p>控制台远程连接打不开、云助手不响应，也是同样的机制：阿里云管控面发到这台 ECS 的回调走的就是这条链路，全被丢了。</p>
<h2 id="五修复方案">
<a class="header-anchor" href="#%e4%ba%94%e4%bf%ae%e5%a4%8d%e6%96%b9%e6%a1%88"></a>
五、修复方案
</h2><h3 id="方案-a关掉-tailscale-的-netfilter-接管推荐">
<a class="header-anchor" href="#%e6%96%b9%e6%a1%88-a%e5%85%b3%e6%8e%89-tailscale-%e7%9a%84-netfilter-%e6%8e%a5%e7%ae%a1%e6%8e%a8%e8%8d%90"></a>
方案 A：关掉 Tailscale 的 netfilter 接管（推荐）
</h3><p>最干净的办法是让 Tailscale 不要再托管 iptables，让默认 ACCEPT 策略接管。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo tailscale <span class="nb">set</span> --netfilter-mode<span class="o">=</span>off
</span></span><span class="line"><span class="cl">Warning: <span class="nv">netfilter</span><span class="o">=</span>off<span class="p">;</span> configure iptables yourself.
</span></span></code></pre></td></tr></table>
</div>
</div><p>这条命令告诉 tailscaled：「我不要你的 iptables 规则」。之后 tailscaled 会把它自己加的 <code>ts-input</code>、<code>ts-forward</code> 等链清掉。</p>
<p>立刻验证：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl --connect-timeout <span class="m">5</span> http://100.100.100.200/latest/meta-data/region-id
</span></span><span class="line"><span class="cl">cn-hangzhou
</span></span></code></pre></td></tr></table>
</div>
</div><p>通了！再试 OSS：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl -sI http://oss-cn-hangzhou-internal.aliyuncs.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">404</span> Not Found
</span></span><span class="line"><span class="cl">Server: AliyunOSS
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>Server: AliyunOSS</code> 头就证明已经打到 OSS 了（404 是因为我们没指定 bucket，正常）。同时阿里云控制台的远程连接和云助手也都恢复。</p>
<p><strong>代价</strong>：失去了 Tailscale 自己装的「外部网卡上源 IP 伪造成 CGNAT 段的包丢掉」这条防御。但是在阿里云 VPC 内部，攻击者要伪造这种源 IP 包到你的 ECS，几乎做不到（VPC 自身的 underlay 会先把它清掉）。所以这个防御<strong>在 VPC 场景下几乎没有实际作用</strong>，关掉是安全的。</p>
<p>而且对 Tailscale 的实际功能没有影响：</p>
<ul>
<li><code>tailscale0</code> 接口还在；</li>
<li>节点间互相访问还能走；</li>
<li>路由还是好的；</li>
<li>唯一不工作的就是那条没意义的 DROP。</li>
</ul>
<h3 id="方案-b保留-netfilter加豁免规则不推荐">
<a class="header-anchor" href="#%e6%96%b9%e6%a1%88-b%e4%bf%9d%e7%95%99-netfilter%e5%8a%a0%e8%b1%81%e5%85%8d%e8%a7%84%e5%88%99%e4%b8%8d%e6%8e%a8%e8%8d%90"></a>
方案 B：保留 netfilter，加豁免规则（不推荐）
</h3><p>如果非要保留 Tailscale 的完整 iptables 规则，可以加一条豁免，让阿里云 OSS 段不被 DROP：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在 ts-input 链的 DROP 规则之前插入豁免</span>
</span></span><span class="line"><span class="cl">sudo iptables -I ts-input <span class="m">3</span> -s 100.118.0.0/16 -j RETURN
</span></span></code></pre></td></tr></table>
</div>
</div><p>但是这条规则<strong>不持久</strong>：</p>
<ul>
<li>tailscaled 重启时会重建 <code>ts-input</code> 链，豁免被冲掉；</li>
<li>tailscaled 运行时如果对账（比如对端变化、ACL 推送），也可能重建。</li>
</ul>
<p>要让它持久得写 systemd drop-in：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo mkdir -p /etc/systemd/system/tailscaled.service.d/
</span></span><span class="line"><span class="cl">sudo tee /etc/systemd/system/tailscaled.service.d/aliyun-exception.conf &gt; /dev/null <span class="s">&lt;&lt;&#39;EOF&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">[Service]
</span></span></span><span class="line"><span class="cl"><span class="s">ExecStartPost=/bin/sh -c &#39;sleep 3 &amp;&amp; \
</span></span></span><span class="line"><span class="cl"><span class="s">  /usr/sbin/iptables -C ts-input -s 100.118.0.0/16 -j RETURN 2&gt;/dev/null || \
</span></span></span><span class="line"><span class="cl"><span class="s">  /usr/sbin/iptables -I ts-input 3 -s 100.118.0.0/16 -j RETURN&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo systemctl restart tailscaled
</span></span></code></pre></td></tr></table>
</div>
</div><p>但是 tailscaled 启动到 ExecStartPost 跑完之间仍然有几秒的窗口期，OSS 会短暂断连。所以一般场景下我还是推荐方案 A。</p>
<blockquote>
<p>还要注意：上面的豁免段 <code>100.118.0.0/16</code> 只覆盖了<strong>杭州</strong>地域的 OSS。如果你的 ECS 在别的地域、或者要访问别的内网服务（如 RDS、Redis 内网），需要查到对应的 100.x 段一起豁免。最稳的查法是直接 <code>nslookup</code> 对应内网域名拿 IP，再确定它的 /16 或 /24。</p>
</blockquote>
<h3 id="方案-c换-tailscale-的-ip-段理论上可行但复杂">
<a class="header-anchor" href="#%e6%96%b9%e6%a1%88-c%e6%8d%a2-tailscale-%e7%9a%84-ip-%e6%ae%b5%e7%90%86%e8%ae%ba%e4%b8%8a%e5%8f%af%e8%a1%8c%e4%bd%86%e5%a4%8d%e6%9d%82"></a>
方案 C：换 Tailscale 的 IP 段（理论上可行，但复杂）
</h3><p>Headscale 配置文件里可以改 <code>prefixes.v4</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">prefixes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">v4</span><span class="p">:</span><span class="w"> </span><span class="m">100.64.0.0</span><span class="l">/10 </span><span class="w"> </span><span class="c"># 默认</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>如果改成不和阿里云冲突的段（比如自建一个 RFC1918 段），就能彻底避开。但是：</p>
<ul>
<li>所有已注册节点都要重新分配 IP；</li>
<li>部分 Tailscale 客户端对非 CGNAT 段的支持有限制；</li>
<li>改完 ACL、DNS 都要重写。</li>
</ul>
<p>对于个人/小团队的 Tailscale 部署，这个改动成本远大于方案 A。<strong>不推荐</strong>。</p>
<h2 id="六为什么阿里云控制台和云助手也会挂">
<a class="header-anchor" href="#%e5%85%ad%e4%b8%ba%e4%bb%80%e4%b9%88%e9%98%bf%e9%87%8c%e4%ba%91%e6%8e%a7%e5%88%b6%e5%8f%b0%e5%92%8c%e4%ba%91%e5%8a%a9%e6%89%8b%e4%b9%9f%e4%bc%9a%e6%8c%82"></a>
六、为什么阿里云控制台和云助手也会挂
</h2><p>很多人不理解：我自己访问 OSS 失败可以理解，<strong>控制台远程连接、云助手为什么会跟着挂？</strong></p>
<p>是因为这两个东西的工作机制：</p>
<ul>
<li>
<p><strong>云助手</strong>（Cloud Assistant）：ECS 里跑了一个 <code>AliyunAssistClient</code> 守护进程，它需要<strong>主动连接阿里云内网的管控接口</strong>拉取要执行的命令。这个接口的接入点同样是 100.x 内网地址，被 DROP 之后客户端连不上服务器，控制台下发的命令就永远是「执行中」。</p>
</li>
<li>
<p><strong>控制台远程连接（Workbench/VNC）</strong>：浏览器走的是阿里云控制台 → 阿里云内网中转 → ECS metadata/agent 通道。中转回来的握手包源 IP 在 100.x 段，被同一条 DROP 吞掉。</p>
</li>
</ul>
<p>所以这是个<strong>典型的「打开了 Tailscale 之后阿里云任何依赖内网管控的功能都坏」综合症</strong>，看到一个症状要联想到一片。</p>
<h2 id="七检查清单">
<a class="header-anchor" href="#%e4%b8%83%e6%a3%80%e6%9f%a5%e6%b8%85%e5%8d%95"></a>
七、检查清单
</h2><p>如果你在阿里云上跑了 Tailscale / Headscale 并且遇到下面任何一个症状，都先去看 <code>iptables -L ts-input -n -v</code> 那条 DROP 的 pkts 计数：</p>
<ul>
<li><input disabled="" type="checkbox"> 控制台「远程连接」打不开；</li>
<li><input disabled="" type="checkbox"> 「云助手」下发的命令永远不返回；</li>
<li><input disabled="" type="checkbox"> <code>curl http://100.100.100.200/latest/meta-data/</code> 超时（元数据服务）；</li>
<li><input disabled="" type="checkbox"> OSS 内网域名 <code>oss-*-internal.aliyuncs.com</code> 超时；</li>
<li><input disabled="" type="checkbox"> RDS、Redis 内网连接超时；</li>
<li><input disabled="" type="checkbox"> SLS 日志服务内网接口超时；</li>
<li><input disabled="" type="checkbox"> 系统初始化时 cloud-init 卡很久；</li>
<li><input disabled="" type="checkbox"> ECS 自动续费、自动伸缩等管控操作异常。</li>
</ul>
<p>如果 DROP 计数在涨，基本就是这个问题，按方案 A 关掉 netfilter 即可。</p>
<h2 id="八复盘">
<a class="header-anchor" href="#%e5%85%ab%e5%a4%8d%e7%9b%98"></a>
八、复盘
</h2><p>这个坑挺典型，关键学习点：</p>
<ol>
<li><strong>DROP 链的统计计数是最快的指纹</strong>。<code>iptables -L -v -n</code> 看到一条 DROP 规则上有大量 pkts/bytes 累积，就要立刻怀疑它。</li>
<li><strong>CGNAT 段 100.64.0.0/10 是公开标准段</strong>（RFC 6598），任何使用方都不能假定自己独占。阿里云、Tailscale、容器网络、Kubernetes 的 Cilium 等等都可能占用这个段，多个一起装就容易撞车。</li>
<li><strong>「我看到的症状」和「真正的根因」之间通常隔了几层</strong>。最初看到的是控制台远程连接失败，最后定位到的是 Tailscale 的 iptables 规则——中间隔着 OSS 超时、metadata 超时、DROP 计数三层证据。</li>
<li><strong>本地 SSH 还能用的时候不要急着重启</strong>。这种 iptables 规则问题，重启不会修，只会让你失去能继续排查的入口。</li>
</ol>
<hr>
<p>排查工具备忘：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># DNS</span>
</span></span><span class="line"><span class="cl">nslookup oss-cn-hangzhou-internal.aliyuncs.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># TCP 探测</span>
</span></span><span class="line"><span class="cl">timeout <span class="m">10</span> telnet &lt;IP&gt; &lt;PORT&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 看防火墙状态</span>
</span></span><span class="line"><span class="cl">sudo ufw status verbose
</span></span><span class="line"><span class="cl">sudo iptables -L -v -n
</span></span><span class="line"><span class="cl">sudo iptables -L ts-input -v -n   <span class="c1"># 直接看 Tailscale 链</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Tailscale 状态</span>
</span></span><span class="line"><span class="cl">tailscale debug prefs <span class="p">|</span> grep -i netfilter
</span></span><span class="line"><span class="cl">tailscale status
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Aliyun metadata 自检</span>
</span></span><span class="line"><span class="cl">curl --connect-timeout <span class="m">5</span> http://100.100.100.200/latest/meta-data/region-id
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 关 Tailscale netfilter（修复）</span>
</span></span><span class="line"><span class="cl">sudo tailscale <span class="nb">set</span> --netfilter-mode<span class="o">=</span>off
</span></span></code></pre></td></tr></table>
</div>
</div>
        
        <hr><p>本文2026-06-10首发于<a href='https://cat.fjh1997.top/'>猫猫鱼的小窝</a>，最后修改于2026-06-10</p>]]>
      </description>
      
    </item>
    
    

    <item>
      <title>解决 Google Play 在 v2rayNG 下卡在等待下载的问题</title>
      <link>https://cat.fjh1997.top/posts/20260605.html</link>
      <pubDate>Fri, 05 Jun 2026 20:30:00 &#43;0800</pubDate>
      <author>xxx@example.com (catcatyu)</author>
      <guid>https://cat.fjh1997.top/posts/20260605.html</guid>
      <description>
        <![CDATA[<h1>解决 Google Play 在 v2rayNG 下卡在等待下载的问题</h1><p>作者：catcatyu（xxx@example.com）</p>
        
          <h2 id="问题现象">
<a class="header-anchor" href="#%e9%97%ae%e9%a2%98%e7%8e%b0%e8%b1%a1"></a>
问题现象
</h2><p>一台 OnePlus / ColorOS 设备上，Google Play 可以打开，也能搜索应用，但是安装应用时一直显示“等待中”或者“正在等待下载”。设备已经 root，可以在 Termux 里使用 <code>su</code> 执行系统命令。</p>
<p>当时环境大致如下：</p>
<ul>
<li>Android 16</li>
<li>Google Play 商店版本：<code>40.1.20-23</code></li>
<li>Google Play 服务版本：<code>26.19.34</code></li>
<li>v2rayNG：<code>2.0.18</code></li>
<li>当前代理节点：<code>TLS-Vision-Unlimited-IPv4</code></li>
</ul>
<h2 id="先排除常见问题">
<a class="header-anchor" href="#%e5%85%88%e6%8e%92%e9%99%a4%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98"></a>
先排除常见问题
</h2><p>先看空间：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">df -h /data /storage/emulated/0
</span></span></code></pre></td></tr></table>
</div>
</div><p>结果 <code>/data</code> 可用空间还有几百 GB，所以不是空间不足。</p>
<p>再确认几个核心包是否存在：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pm path com.android.vending
</span></span><span class="line"><span class="cl">pm path com.google.android.gms
</span></span><span class="line"><span class="cl">pm path com.google.android.gsf
</span></span><span class="line"><span class="cl">pm path com.android.providers.downloads
</span></span></code></pre></td></tr></table>
</div>
</div><p>这些包都存在。root 下进一步看包状态：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;/system/bin/dumpsys package com.android.vending | grep -E &#34;versionName|lastUpdateTime|installerPackageName|User 0&#34;&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;/system/bin/dumpsys package com.google.android.gms | grep -E &#34;versionName|lastUpdateTime|installerPackageName|User 0&#34;&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;/system/bin/dumpsys package com.android.providers.downloads | grep -E &#34;versionName|User 0&#34;&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>结论：</p>
<ul>
<li>Play 商店、Play 服务、GSF、下载管理器都没有被禁用。</li>
<li>Play 商店版本比较旧，最后更新时间停在 2025-03-12。</li>
<li>下载管理器正常启用。</li>
</ul>
<h2 id="清掉旧的卡死下载任务">
<a class="header-anchor" href="#%e6%b8%85%e6%8e%89%e6%97%a7%e7%9a%84%e5%8d%a1%e6%ad%bb%e4%b8%8b%e8%bd%bd%e4%bb%bb%e5%8a%a1"></a>
清掉旧的卡死下载任务
</h2><p>一开始在下载管理器数据库里发现了一个旧的 Play 下载任务：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;content query --uri content://downloads/all_downloads&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>其中有一条记录类似：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">notificationpackage=com.android.vending
</span></span><span class="line"><span class="cl">allowed_network_types=2
</span></span><span class="line"><span class="cl">status=194
</span></span><span class="line"><span class="cl">errorMsg=Binding socket to network 182 failed: EPERM (Operation not permitted)
</span></span><span class="line"><span class="cl">uri=https://rr5---sn-a5mekn6r.gvt1.com/play-apps-download-default/download/by-id/...
</span></span></code></pre></td></tr></table>
</div>
</div><p>这里有两个重点：</p>
<ul>
<li><code>allowed_network_types=2</code> 表示任务只允许 Wi-Fi。</li>
<li><code>Binding socket to network 182 failed</code> 表示它还绑到了一个已经失效的网络。</li>
</ul>
<p>删除这条旧任务：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;content delete --uri content://downloads/all_downloads/103519&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;am force-stop com.android.vending; am force-stop com.android.providers.downloads; am force-stop com.google.android.gms&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;cmd package clear --user 0 --cache-only com.android.providers.downloads; cmd package clear --user 0 --cache-only com.android.vending&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这一步可以解决旧任务残留导致的“等待中”，但本次问题还没有完全解决。</p>
<h2 id="抓-play-下载服务日志">
<a class="header-anchor" href="#%e6%8a%93-play-%e4%b8%8b%e8%bd%bd%e6%9c%8d%e5%8a%a1%e6%97%a5%e5%bf%97"></a>
抓 Play 下载服务日志
</h2><p>重新点下载后，继续抓日志：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;logcat -d -t 3000 | grep -Ei &#34;Finsky|DownloadService|CronetDownloader|CANNOT_CONNECT|ERR_QUIC|ERR_CONNECTION|WAITING_FOR_RETRY|RUNNING|QUEUED|gvt1|CronetUrlRequest&#34;&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>关键日志：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">CronetDownloader: onFailed
</span></span><span class="line"><span class="cl">Exception in CronetUrlRequest: net::ERR_CONNECTION_CLOSED
</span></span><span class="line"><span class="cl">Download Service Error: CANNOT_CONNECT (8)
</span></span><span class="line"><span class="cl">Updating listeners of &lt;... QUEUED with WAITING_FOR_RETRY ...&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>中间还出现过：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">net::ERR_QUIC_PROTOCOL_ERROR
</span></span></code></pre></td></tr></table>
</div>
</div><p>这说明 Play 不是真的单纯排队，而是下载服务连接失败后进入等待重试。</p>
<p>为了排除 QUIC/HTTP3 影响，我临时对 Play 和 GMS 禁了 UDP 443：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;iptables -I OUTPUT -p udp --dport 443 -m owner --uid-owner 10248 -j REJECT&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;iptables -I OUTPUT -p udp --dport 443 -m owner --uid-owner 10242 -j REJECT&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>其中：</p>
<ul>
<li><code>10248</code> 是 Play 商店 UID。</li>
<li><code>10242</code> 是 Google Play 服务 UID。</li>
</ul>
<p>禁掉 UDP 443 后，QUIC 错误减少，但仍然出现 TCP 层的 <code>ERR_CONNECTION_CLOSED</code>，所以问题不只是 QUIC。</p>
<h2 id="找到真正失败的域名">
<a class="header-anchor" href="#%e6%89%be%e5%88%b0%e7%9c%9f%e6%ad%a3%e5%a4%b1%e8%b4%a5%e7%9a%84%e5%9f%9f%e5%90%8d"></a>
找到真正失败的域名
</h2><p>Play 的下载 URL 不一定直接出现在 logcat 里，可以复制它的本地数据库出来读：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;cp /data/data/com.android.vending/databases/download_service /tmp/play_download_service.db; chmod 644 /tmp/play_download_service.db&#39;</span>
</span></span><span class="line"><span class="cl">python3 - <span class="s">&lt;&lt;&#39;PY&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">import pathlib, re
</span></span></span><span class="line"><span class="cl"><span class="s">data = pathlib.Path(&#39;/tmp/play_download_service.db&#39;).read_bytes()
</span></span></span><span class="line"><span class="cl"><span class="s">urls = sorted(set(m.decode(&#39;utf-8&#39;, &#39;ignore&#39;) for m in re.findall(rb&#39;https?://[^\x00\s&#34;\&#39;]+&#39;, data)))
</span></span></span><span class="line"><span class="cl"><span class="s">for u in urls:
</span></span></span><span class="line"><span class="cl"><span class="s">    print(u[:500])
</span></span></span><span class="line"><span class="cl"><span class="s">PY</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>查到当前 Play 下载库里实际使用的是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://services.googleapis.cn/download/by-token/download?token=...
</span></span></code></pre></td></tr></table>
</div>
</div><p>旧任务里还出现过：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://rr5---sn-a5mekn6r.gvt1.com/play-apps-download-default/download/by-id/...
</span></span></code></pre></td></tr></table>
</div>
</div><p>因此重点域名是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">services.googleapis.cn
</span></span><span class="line"><span class="cl">redirector.gvt1.com
</span></span><span class="line"><span class="cl">*.gvt1.com
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="本机连通性测试">
<a class="header-anchor" href="#%e6%9c%ac%e6%9c%ba%e8%bf%9e%e9%80%9a%e6%80%a7%e6%b5%8b%e8%af%95"></a>
本机连通性测试
</h2><p>直接在 Termux 里测试：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://redirector.gvt1.com
</span></span><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://rr5---sn-a5mekn6r.gvt1.com
</span></span><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://play.googleapis.com
</span></span></code></pre></td></tr></table>
</div>
</div><p>一开始结果是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">services.googleapis.cn      TLS connect error: unexpected eof while reading
</span></span><span class="line"><span class="cl">redirector.gvt1.com         TLS connect error: unexpected eof while reading
</span></span><span class="line"><span class="cl">rr5---sn-a5mekn6r.gvt1.com  HTTP/1.1 404 Not Found
</span></span><span class="line"><span class="cl">play.googleapis.com         HTTP/2 404
</span></span></code></pre></td></tr></table>
</div>
</div><p>404 在这里反而说明 TLS 和 HTTP 都是通的，只是访问根路径不是有效接口。真正有问题的是 <code>unexpected eof while reading</code>，也就是 TLS 握手阶段被断开。</p>
<p>为了判断是不是域名本身的问题，又用 check-host 从第三方节点测：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s <span class="s1">&#39;https://check-host.net/check-http?host=https://services.googleapis.cn&amp;max_nodes=8&#39;</span> -H <span class="s1">&#39;Accept: application/json&#39;</span>
</span></span><span class="line"><span class="cl">curl -s <span class="s1">&#39;https://check-host.net/check-http?host=https://redirector.gvt1.com&amp;max_nodes=8&#39;</span> -H <span class="s1">&#39;Accept: application/json&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>外部节点访问 <code>services.googleapis.cn</code> 和 <code>redirector.gvt1.com</code> 基本都返回 404，说明域名本身没有挂，是本机当前代理线路有问题。</p>
<h2 id="检查-v2rayng-路由配置">
<a class="header-anchor" href="#%e6%a3%80%e6%9f%a5-v2rayng-%e8%b7%af%e7%94%b1%e9%85%8d%e7%bd%ae"></a>
检查 v2rayNG 路由配置
</h2><p>v2rayNG 2.0.18 的配置存在 MMKV 里：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;strings /data/data/com.v2ray.ang/files/mmkv/SETTING | grep -Ei &#34;routing|geosite|gvt1|googleapis|services.googleapis|direct|proxy&#34; -C2&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>当时配置里能看到类似规则：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;domain&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;services.googleapis.cn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;*.gvt1.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;redirector.gvt1.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;*.googleusercontent.com&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;enabled&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;outboundTag&#34;</span><span class="p">:</span> <span class="s2">&#34;proxy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;remarks&#34;</span><span class="p">:</span> <span class="s2">&#34;playstore&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>而且它的位置在 <code>geosite:cn -&gt; direct</code> 前面。分应用代理里也包含：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">com.android.vending
</span></span><span class="line"><span class="cl">com.google.android.gms
</span></span><span class="line"><span class="cl">com.android.providers.downloads
</span></span><span class="line"><span class="cl">com.termux
</span></span></code></pre></td></tr></table>
</div>
</div><p>所以表面看路由顺序没问题。</p>
<p>为了进一步排除“VPN 路由没生效”，我直接强制使用 v2rayNG 的本地 socks 端口测试：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl --socks5-hostname 127.0.0.1:10808 -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span><span class="line"><span class="cl">curl --socks5-hostname 127.0.0.1:10808 -I --connect-timeout <span class="m">10</span> https://redirector.gvt1.com
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果强制 socks 也失败，说明不是路由规则顺序问题，而是当前节点或服务端出站本身连不上这些域名。</p>
<p>当时测试结果就是强制 socks 也失败：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">TLS connect error: unexpected eof while reading
</span></span></code></pre></td></tr></table>
</div>
</div><p>因此定位为当前节点对 <code>services.googleapis.cn</code> 和 <code>redirector.gvt1.com</code> 的 TLS 握手不正常。</p>
<h2 id="修复办法">
<a class="header-anchor" href="#%e4%bf%ae%e5%a4%8d%e5%8a%9e%e6%b3%95"></a>
修复办法
</h2><p>最终调整 v2rayNG 路由，让 Play 下载相关域名明确走代理，并确保规则在 <code>geosite:cn</code> 直连规则前面：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">full:services.googleapis.cn
</span></span><span class="line"><span class="cl">full:redirector.gvt1.com
</span></span><span class="line"><span class="cl">domain:gvt1.com
</span></span><span class="line"><span class="cl">domain:googleapis.cn
</span></span><span class="line"><span class="cl">domain:googleusercontent.com
</span></span><span class="line"><span class="cl">geosite:google
</span></span></code></pre></td></tr></table>
</div>
</div><p>出站选择 <code>proxy</code>。</p>
<p>如果用的是简单路由界面，逻辑应该是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">代理：
</span></span><span class="line"><span class="cl">full:services.googleapis.cn
</span></span><span class="line"><span class="cl">full:redirector.gvt1.com
</span></span><span class="line"><span class="cl">domain:gvt1.com
</span></span><span class="line"><span class="cl">domain:googleapis.cn
</span></span><span class="line"><span class="cl">domain:googleusercontent.com
</span></span><span class="line"><span class="cl">geosite:google
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">直连：
</span></span><span class="line"><span class="cl">geosite:cn
</span></span><span class="line"><span class="cl">geoip:cn
</span></span></code></pre></td></tr></table>
</div>
</div><p>注意：<code>services.googleapis.cn</code> 虽然是 <code>.cn</code> 域名，但它是 Google Play 下载入口，不能让它被 <code>geosite:cn</code> 提前命中走直连。</p>
<p>调整后重新测试：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://redirector.gvt1.com
</span></span><span class="line"><span class="cl">curl --socks5-hostname 127.0.0.1:10808 -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span><span class="line"><span class="cl">curl --socks5-hostname 127.0.0.1:10808 -I --connect-timeout <span class="m">10</span> https://redirector.gvt1.com
</span></span></code></pre></td></tr></table>
</div>
</div><p>正常结果：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">services.googleapis.cn   HTTP/2 404
</span></span><span class="line"><span class="cl">redirector.gvt1.com      HTTP/2 404
</span></span></code></pre></td></tr></table>
</div>
</div><p>出现 404 没关系，说明 TLS/HTTPS 已经通了。</p>
<p>最后强停 Play 商店和下载管理器，再重新下载：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;am force-stop com.android.vending; am force-stop com.android.providers.downloads; am force-stop com.google.android.gms&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>然后打开 Google Play，取消旧的等待任务，重新点安装。</p>
<h2 id="总结">
<a class="header-anchor" href="#%e6%80%bb%e7%bb%93"></a>
总结
</h2><p>这次问题有三层：</p>
<ol>
<li>下载管理器里曾经残留一个只允许 Wi-Fi 且绑定旧网络的 Play 下载任务，需要删除。</li>
<li>Play 下载服务使用 Cronet，失败时界面会显示“等待中”，日志里实际是 <code>CANNOT_CONNECT (8)</code>。</li>
<li>真正导致下载失败的域名是 <code>services.googleapis.cn</code>，它虽然属于 <code>.cn</code>，但应该走代理，不能被 <code>geosite:cn</code> 规则直连。</li>
</ol>
<p>最有用的几条排查命令：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su -c <span class="s1">&#39;content query --uri content://downloads/all_downloads&#39;</span>
</span></span><span class="line"><span class="cl">su -c <span class="s1">&#39;logcat -d -t 3000 | grep -Ei &#34;Finsky|DownloadService|CronetDownloader|CANNOT_CONNECT|ERR_CONNECTION|ERR_QUIC&#34;&#39;</span>
</span></span><span class="line"><span class="cl">curl -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span><span class="line"><span class="cl">curl --socks5-hostname 127.0.0.1:10808 -I --connect-timeout <span class="m">10</span> https://services.googleapis.cn
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果 <code>curl --socks5-hostname 127.0.0.1:10808</code> 都连不上，那就不是 v2rayNG 路由写法的问题，而是节点或服务端出站本身的问题。</p>

        
        <hr><p>本文2026-06-05首发于<a href='https://cat.fjh1997.top/'>猫猫鱼的小窝</a>，最后修改于2026-06-05</p>]]>
      </description>
      
    </item>
    
  </channel>
</rss>
