数据源:每个结论从哪来
这个工具不自带任何地理库、不打包任何 IP 数据库。屏幕上每一个"这个地址在哪、 是不是机房"的判断,都来自一个可以点开、可以自己复现的外部来源。
这一章把它们全列出来:要什么、给什么、多久更新一次、缓存多久、什么许可、 以及它能把结论撑到哪一档置信度。
一、三条选型原则
1. 优先用"当事人自己发布的"数据。 判断一个地址是不是 AWS 的机房,最硬的证据是 AWS 自己发布的 IP 段清单, 而不是第三方的推测。所以厂商官方清单排在所有第三方数据源前面, 命中它的结论标为确证。
2. 数据源给不了的维度,不假装能给。 免费 IP 情报普遍只有一个 hosting 布尔值,它只回答"是不是机房", 没有"住宅 vs 企业"这个维度。所以本工具那一档叫「非机房宽带」, 不叫「住宅」——叫住宅是在宣称一个数据源给不了的结论。
3. 缓存要短,并且把拉取时间显示出来。 "这个 IP 段属于 Google Cloud"是会变的。厂商清单缓存 24 小时, 界面上写明这份清单是什么时候拉的。拉一次用一年的缓存比没有缓存更危险 —— 它会给出一个看起来很确定的过期答案。
二、数据源清单
出口地址本身
| 来源 | 地址 | 给什么 | 时效 | 许可 |
|---|---|---|---|---|
| Cloudflare trace | https://<任一 CF 站点>/cdn-cgi/trace | ip= 出口地址、loc= 国家、colo= 边缘机房 | 实时,不缓存 | 公开端点,无需鉴权 |
这是整个工具的地基,值得单独说清楚。
cdn-cgi/trace 是 Cloudflare 在每一个它托管的站点上都开着的一个诊断端点。 它返回的 ip= 是 Cloudflare 那侧看到的来源地址 —— 也就是说, 这不是"本机某个网卡的地址",而是目的地眼里你是谁。风控看的正是这个值。
claude.ai、api.anthropic.com 都在 Cloudflare 后面,所以可以直接问它们自己:
curl -s https://claude.ai/cdn-cgi/trace
# ip=203.0.113.24
# loc=SG
# colo=SIN
colo 是三字母的 IATA 机场代码,表示你被接到了哪个边缘机房: SIN = 新加坡樟宜,NRT = 东京成田,LAX = 洛杉矶。 它和 loc(出口 IP 的注册国家)可以不一致 —— 那通常说明 anycast 把你接到了邻近国家的机房,是正常现象,不是问题。
本工具对每个入口用该入口自己的代理配置去问这个端点。所以得到的 不是"这台机器的出口",而是"这个入口的出口"——三个入口可以是三个答案。 原理见 03-routing.md。
厂商官方 IP 段(确证级)
| 来源 | 地址 | 规模 | 更新 |
|---|---|---|---|
| AWS | https://ip-ranges.amazonaws.com/ip-ranges.json | ~8000 段 | 每天多次 |
| Google Cloud | https://www.gstatic.com/ipranges/cloud.json | ~1400 段 | 不定期 |
| Cloudflare | https://www.cloudflare.com/ips-v4 · ips-v6 | ~30 段 | 很少 |
| DigitalOcean | https://www.digitalocean.com/geo/google.csv | ~2000 段 | 不定期 |
| Oracle Cloud | https://docs.oracle.com/en-us/iaas/tools/public_ip_ranges.json | ~700 段 | 不定期 |
一共约 两万条前缀,首次拉取加解析约 4 秒,之后缓存 24 小时到 data/cloud-ranges.json。
为什么值得为它花这 4 秒:命中厂商自己发布的清单,是唯一能把 "这是机房 IP"这个判断标成确证的证据。其余所有来源都只能给到"较可能"。
实测一例:Datadog US5 的接入点解析到 34.149.66.165,命中 Google Cloud 的 34.149.0.0/16 —— 于是可以确证 US5 跑在 GCP 上, 而不是"看起来像云"。
python3 -m cem probe --json | python3 -m json.tool | grep -A3 cloud_provider
拉不到就降级:清单缺失时这一档判断退回"较可能",其余功能不受影响。
ASN 与归属
| 来源 | 协议 | 给什么 | 限额 | 置信度 |
|---|---|---|---|---|
| Team Cymru | DNS TXT origin.asn.cymru.com | ASN、前缀、注册国、RIR | 无明确限额,走 DNS 很轻 | 确证(BGP 权威) |
| ip-api.com | HTTP JSON | 组织名、城市、hosting / mobile / proxy | 免费版 45 次/分钟 | 较可能 |
| ipinfo.io | HTTP JSON | 城市、anycast 标记 | 无 token 有日限额 | 较可能 |
| RDAP | HTTP JSON(RFC 7482/7483) | 注册机构、分配日期、备注 | 各 RIR 自定,通常宽松 | 确证(注册事实) |
| rDNS | DNS PTR | 主机名(常含机房代码) | — | 推测 |
Team Cymru 的 DNS 接口是这里最被低估的一个。它把 BGP 全表的 "这个地址属于哪个 AS"做成 DNS 查询,一次查询几十字节,没有 API key, 没有速率墙:
地址要四段倒着写再拼上 origin.asn.cymru.com。 以 13.107.42.16 为例,倒过来是 16.42.107.13:
dig +short TXT 16.42.107.13.origin.asn.cymru.com
# "8068 | 13.107.42.0/24 | US | arin | 2015-05-27"
# ↑ASN ↑前缀 ↑国 ↑RIR ↑分配日期
拿到 ASN 之后再查一次组织名(AS8068.asn.cymru.com)。 IPv6 走 origin6.asn.cymru.com,地址按 nibble 倒序。
RDAP 有一个坑必须知道:向错误的 RIR 查询时,它不会返回 404, 而是返回一条占位记录(IANA-NETBLOCK-34 之类,备注写着 "not allocated to APNIC")。看起来像有效答案,但内容是错的。 解法是先读 IANA 的引导文件确定该问谁:
curl -s https://data.iana.org/rdap/ipv4.json | head -c 400
本工具内置了占位记录识别,命中就丢弃并降级,不会把它当成注册信息展示。
DNS 对账
| 来源 | 地址 | 用途 |
|---|---|---|
| Cloudflare DoH | https://cloudflare-dns.com/dns-query | 拿"外面看到的"解析结果 |
| Google DoH | https://dns.google/resolve | 第二个独立来源,交叉验证 |
| 本机 | getaddrinfo() | 拿"进程真正会连的"地址 |
两个 DoH 来源必须互相独立才有交叉验证的意义 —— 一个被污染时另一个还在。 本机解析结果和它们不一致,就说明本机的 DNS 被改写了(fake-ip、 split-DNS、hosts 文件、企业 DNS)。判读方法见 03-routing.md。
路径质量(可选)
| 工具 | 用途 | 装法 |
|---|---|---|
mtr | 逐跳延迟与丢包 | brew install mtr,见 08-deploy.md |
没装就降级显示"未安装"并给出安装命令,主流程不受影响。
三、为什么不用 MaxMind GeoLite2
GeoLite2 是最常被推荐的免费 IP 地理库,本工具没有用它,三个原因:
- 要注册账号拿 license key。一个"clone 完就能跑"的工具不该要求 使用者先去注册一个第三方账号。
- 数据库文件几十 MB,要么打包进仓库(仓库体积翻几十倍, 而且一提交就过期),要么让使用者自己下载(回到第 1 条)。
- 它答不了最关键的那个问题。GeoLite2 给的是"这个 IP 在哪", 而这个工具真正要回答的是"目的地那侧看到的是哪个 IP" —— 后者只能问目的地,任何本地数据库都给不了。
需要离线运行、且能接受注册流程的场景,GeoLite2 是合理选择 —— 但那是另一个工具的定位。
四、几个自己拼出来的判断手段
下面这些不是某个数据源直接给的,是把几个来源组合出来的。 每一条都在界面上标了置信度,判不出就写判不出。
1. TUN 抢跑:用四段延迟的形状反推
TCP 握手 < 5ms 而 TLS 握手 > 50ms,是一个很特别的组合。 正常情况下这两段的量级应该接近(都要跨一次网络往返)。 TCP 快得不真实、TLS 却是真实的跨国耗时,说明 TCP 那一段根本没出本机 —— TUN 把它在本地就应答了,真正的连接是在 TLS 阶段才建立的。
这条判断标为推测:它是一个很强的信号,但不是直接观测。
2. fake-ip 反查域名
分流器给每个域名分配一个独立的 198.18.x.x 占位地址。 这是个坑(查它的归属毫无意义),但反过来有用:先把所有 Claude 域名解析一遍 建立"占位地址 → 域名"的表,之后在连接列表里看到占位地址就能反推是哪个域名。
这条标为确证:映射是本机自己刚建立的,不是猜的。
3. 出口一致性分五档,而不是"一致 / 不一致"
同一个国家不等于同一个出口。所以分成: 完全相同 → 双栈(v4/v6 同一出口)→ 同网络(同 ASN 不同地址)→ 多网络(不同 ASN)→ 跨国。
前两档是正常的,第三档常见于负载均衡,第四第五档才值得看。 二元的"一致/不一致"会把双栈报成问题 —— 那是一次假警报。
4. 动态还是静态 IP:需要观测窗口
单次采样答不了这个问题。本工具的规则是:观测不足 6 小时就明说 "还判断不了",而不是给一个看起来很确定的答案。够 6 小时之后, 按"这段时间里出口地址换过几次、换的是不是同一个网段"来分档。
5. 终点不回 ICMP ≠ 丢包 100%
路径质量面板里,终点一个包都没回时报"量不出丢包率",不报"丢包 100%"。 大量主机在防火墙上直接丢掉 ICMP echo,TCP 却完全正常。 报成丢包会让人去换节点,换完还是 100%。
五、置信度四档
| 档位 | 判据 | 例子 |
|---|---|---|
| 确证 | 权威来源直接说的 | 命中 AWS 官方发布的 IP 段 |
| 较可能 | 第三方数据源的判断 | ip-api 说 hosting: true |
| 推测 | 单一弱线索 | 组织名里有 "Telecom" 关键词 |
| 判不出 | 没有线索 | 上述全部缺失 |
判不出就写判不出。 尤其当猜错的方向不对称时 —— 把机房 IP 猜成家宽会让人以为风险更低,那是最危险的方向。
六、全部拉一遍看看
# 每个数据源各查一次,打印来源和耗时
python3 -m cem probe --all --json | python3 -m json.tool
# 只看厂商 IP 段的缓存状态
ls -lh data/cloud-ranges.json
界面上「诊断与建议」里每一条结论都带判据(算出它的实测数字) 和来源,可以逐条对着这一章核。