本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2022年06月07日 统计字数: 10343字 阅读时间: 21分钟阅读 本文链接: https://soulteary.com/2022/06/07/correct-handling-of-ip-data-from-the-world-s-top-five-internet-registries.html ----- # 正确处理全球五大互联网注册机构的 IP 数据 如果我们不需要精准判断服务请求来源和用户 IP 归属地,使用全球五大 RIR 机构每日更新的 IP 分配数据,相比较使用商业 IP 数据库而言,会是一个低成本的方案(免费)。 本文将聊聊如何正确对 APNIC、ARIN、RIPE NCC、LACNIC 和 AFRINIC 这五个全球顶级互联网注册机构(RIR)的 IP 注册数据进行处理。 ## 写在前面 网上关于这几个顶级 RIR 的数据处理的文章和内容并不少,但其实其中不乏谬误。 相信有一些读者对于 RIR 机构的了解并不多,所以我简单介绍一下目前仅有的五个全球互联网注册机构,它们负责统筹和管理我们日常使用的 IP ,先来看一下这几个机构各自的管辖区域。 ![来自 RIR 的机构管理区域](https://attachment.soulteary.com/2022/06/07/rir-regions.jpg) 简单来说,在 90 年代之后,考虑到当前世界上的 IP 地址数量已经非常多,日常繁杂的 IP 地址分配等维护工作对于单一机构或者某几个学术院校来说,是一件很麻烦的事情。 各地区(亚太、南北美、欧洲、中东、非洲)的互联网发展速度和程度并不相同,语言、文化,甚至是时区都会对当时的维护者来说是一个麻烦的事情,为了更好的服务全球的用户,陆续出现了五个顶级机构: - [ARIN (American Registry for Internet Numbers)](https://www.arin.net/about/) - 目前该机构主要负责北美地区的 IP地址分配。同时也负责为全球 NSP (Network Service Providers) 分配地址。 - [RIPE (Reseaux IP Europeens)](https://www.ripe.net/about-us/what-we-do/regional-internet-registry) - 目前该机构主要负责欧洲、中东、中亚等地区的 IP 地址分配。 - [APNIC (Asia Pacific Network Information Center)](https://www.apnic.net/about-apnic/organization/) - 和我们关系最大的一个机构,该机构负责亚洲、太平洋地区的 IP 地址分配。 - [LACNIC (Latin America and the Caribbean Information Center)](https://www.lacnic.net/1004/2/lacnic/about-lacnic) - 目前该机构负责拉丁美洲和加勒比地区的 IP 地址分配。 - [AFRINIC (African Network Information Centre)](https://www.afrinic.net/about) - 目前该机构负责非洲区域的 IP 地址分配。 简单了解这些机构之后,我们来回到正题上,聊聊如何处理它们公开的 IP 数据,网络上常见的谬误,以及该如何正确的处理它们分享的数据。 ## 从顶级 IP 注册机构获取公开的 IP 数据 在聊如何下载数据之前,我们需要先了解这些数据有哪些“特点”,尽可能避开细节上的坑。 ### 公开数据的一些坑 上面提到的五个机构会定期更新 IP 注册数据,我们可以从下面几个地址获取数据(NRO 版本数据): ```bash https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest https://ftp.ripe.net/ripe/stats/delegated-ripencc-extended-latest https://ftp.apnic.net/stats/apnic/delegated-apnic-extended-latest https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest ``` 这里要注意的是,虽然五个机构是伙伴关系,有的亚太的数据文件我们可以在欧洲或者非洲等 RIR 的服务器上找到。 ```bash https://ftp.ripe.net/pub/stats/apnic/delegated-apnic-extended-latest https://ftp.apnic.net/stats/apnic/delegated-apnic-extended-latest ``` 但是,这里埋着第一个坑:**这些机构并不保证自己服务器上其他的 RIR 的数据是最新的版本。** 就比如美国区域的 ARIN 只会更新自己的数据,其他的数据版本停留在 2013 年的版本。所以,想要得到哪个区域的数据,最好还是从哪个区域的注册机构站点获取数据,避免获取到老的数据。 第二个坑是,我们常常在网上看到的下载文件名称是 `delegated-apnic-latest`、`delegated-ripencc-latest`,但是当我们想获取美国注册数据的时候,得到的结果会是 404: ```bash curl https://ftp.arin.net/pub/stats/arin/delegated-arin-latest 404 Not Found

Not Found

The requested URL /pub/stats/arin/delegated-arin-latest was not found on this server.

``` 这是因为美国的注册机构在 2013 年彻底废弃了老版本的数据格式,使用了新版本的 [NRO 格式来进行数据统计](https://ftp.arin.net/pub/stats/arin/ARIN-STATS-FORMAT-CHANGE.txt)。新版本的 NRO 格式相比较老格式,多了一列来表示 IP 分配给了那个组织(REG-ID),所以虽然地址变化了,但还是向前兼容的。 为了数据源格式能够保持一致,所以这里推荐使用本小节一开始推荐的几个数据地址,而非网络上各种转来转去的陈旧文章中使用的 `delegated-${region}-latest` 格式的文件。 此外,**美国的 ARIN 的数据还有一个小坑,它是唯一一个 MD5 校验值倒序存放的数据源**,所以后续在处理下载文件的完整性校验时,也需要额外进行处理。 当然,有问题的并不只有 ARIN 的数据,**亚太注册机构 APNIC 的数据里一枝独秀的添加了“注释内容”。这几个机构的数据也并没有完全按照各自“管辖地”来划分**,有一些古早的数据,可能还是散落在 ARIN 这些比较老的机构里,比如在 ARIN 里,你能够找到本应该存放在 APNIC 的我国 HK 地区的数据记录。 在使用这些数据的时候,还有一个小技巧,**使用合适的访问协议**。这些注册机构已经公开的提供了二十年的数据,所以考虑到向前兼容,数据支持通过 `ftp`、`http`、`https` 三种协议进行访问。 ```bash ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest http://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest https://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest ``` 一般情况下,我们使用 `https` 会获得更快的数据下载速度,如果你是在容器中使用,但不想安装 `ca-certificates` 软件包,那么也可以考虑使用 `http` 协议来访问数据。 聊完数据的问题,我们来聊聊如何编写下载脚本。 ### 编写数据下载程序 下载数据的方式有很多种,最简单的方式莫过于使用系统自带的 `curl` 或者 `wget` 啦,但是这两个命令不一定会同时存在于操作系统,所以我写了一个简单的兼容脚本,来按需调用程序,依次下载不同 RIR 机构的 IP 数据: ```bash #!/bin/sh # author soulteary command_exists() { command -v "$@" >/dev/null 2>&1 } use_curl() { command_exists curl || return 1 } use_wget() { command_exists wget || return 1 } declare -a files files[0]=https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest files[1]=https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest.md5 files[2]=https://ftp.ripe.net/ripe/stats/delegated-ripencc-extended-latest files[3]=https://ftp.ripe.net/ripe/stats/delegated-ripencc-extended-latest.md5 files[4]=https://ftp.apnic.net/stats/apnic/delegated-apnic-extended-latest files[5]=https://ftp.apnic.net/stats/apnic/delegated-apnic-extended-latest.md5 files[6]=https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest files[7]=https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest.md5 files[8]=https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest files[9]=https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest.md5 download() { if use_curl; then for file in ${files[*]} do curl -O "${file}" done else if use_wget; then for file in ${files[*]} do wget -c "${file}" done else echo "You need install `wget` or `curl` first." return 1 fi fi } download ``` 将上面的内容保存为 `download.sh`,然后执行 `bash download.sh` 就能够开始数据下载了。当程序执行完毕之后,在当前目录就能够看到来自各种 RIR 的数据和对应的文件校验值啦。 ```bash total 79056 drwxr-xr-x 12 soulteary staff 384 6 6 21:29 . drwxr-xr-x 15 soulteary staff 480 6 6 21:29 .. -rw-r--r-- 1 soulteary staff 711881 6 6 20:49 delegated-afrinic-extended-latest -rw-r--r-- 1 soulteary staff 74 6 6 20:49 delegated-afrinic-extended-latest.md5 -rw-r--r-- 1 soulteary staff 7570060 6 6 20:49 delegated-apnic-extended-latest -rw-r--r-- 1 soulteary staff 73 6 6 20:49 delegated-apnic-extended-latest.md5 -rw-r--r-- 1 soulteary staff 10999023 6 6 20:17 delegated-arin-extended-latest -rw-r--r-- 1 soulteary staff 67 6 6 20:17 delegated-arin-extended-latest.md5 -rw-r--r-- 1 soulteary staff 3969444 6 6 20:49 delegated-lacnic-extended-latest -rw-r--r-- 1 soulteary staff 74 6 6 20:49 delegated-lacnic-extended-latest.md5 -rw-r--r-- 1 soulteary staff 16367291 6 6 20:22 delegated-ripencc-extended-latest -rw-r--r-- 1 soulteary staff 74 6 6 20:22 delegated-ripencc-extended-latest.md5 ``` 完整的数据下载脚本,我已经上传到了 GitHub,如果你有需要,可以自取:[https://github.com/soulteary/Home-Network-Note/blob/master/scripts/ip-data-downloader.sh](https://github.com/soulteary/Home-Network-Note/blob/master/scripts/ip-data-downloader.sh)只需要将脚本程序保存到本地,然后执行 `bash ip-data-downloader.sh`,等待数据完成下载即可。 ### 编写数据校验程序 出于数据不时会更新,以及我们下载时的网络可能不会那么稳定两个原因,推荐对下载的数据进行校验。 考虑到 macOS 和 Linux 上针对数据进行 MD5 校验的方法并不相同,以及上文中提到的 ARIN 数据源校验文件格式和其他数据源完全不同,我编写了一个小脚本,来针对数据进行校验。 ```bash #!/bin/sh # author soulteary command_exists() { command -v "$@" >/dev/null 2>&1 } use_md5() { command_exists md5 || return 1 } use_md5sum() { command_exists md5sum || return 1 } files=$(ls *latest) download() { if use_md5; then for file in ${files[*]} do echo $file digst=$(md5 -r $file | awk '{print $1}') checksum=$(cat "$file".md5 | awk '{print $4}') if [ "$digst" != "$checksum" ]; then checksum=$(cat "$file".md5 | awk '{print $1}') if [ "$digst" != "$checksum" ]; then echo "$file data validation failed, please try downloading again." fi fi done else if use_md5sum; then for file in ${files[*]} do echo $file digst=$(md5sum $file | awk '{print $1}') checksum=$(cat "$file".md5 | awk '{print $4}') if [ "$digst" != "$checksum" ]; then checksum=$(cat "$file".md5 | awk '{print $1}') if [ "$digst" != "$checksum" ]; then echo "$file data validation failed, please try downloading again." fi fi done else echo "You need install `md5` or `md5sum` first." return 1 fi fi } download ``` 将上面的内容保存为 `verify.sh`,执行脚本,如果没有看到 `data validation failed, please try downloading again.` 的提示,那么就能够确认下载的数据是完整的,可以用于之后的处理啦。 上面的程序,我也已经上传到了 GitHub,如果你需要的话,可以从这里自取:[https://github.com/soulteary/Home-Network-Note/blob/master/scripts/ip-data-verify.sh](https://github.com/soulteary/Home-Network-Note/blob/master/scripts/ip-data-verify.sh) ## 正确处理公开的 IP 数据 当我们已经完成了数据的下载和校验之后,便可以开始着手进行数据的处理了。 ### 进行数据预处理 为了方便后续折腾,先创建一个数据目录,然后将刚刚下载好的数据移动到这个目录中。 ```bash mkdir data mv delegated-* data ``` 如果我们想根据地区编号来对数据进行分堆儿,可以考虑将数据文件先拼合到一起,并先进行去重操作。然后过滤掉前文中提到的注释内容。 ```bash cat data/delegated-*-latest | sort | uniq > data/all.txt cat data/all.txt | awk '$1 ~ /^[^;#]/' > data/pure.txt ``` ### 整理数据 IP 归属地列表 实际使用的时候,一般是以下两个场景: - 判断用户是否在或者不在某个或者某几个地区(比如是否是国内用户) - 判断要访问的目标网站/接口是否是海外网站 为了程序效率更高,这里推荐先梳理数据包含的地区清单,以方便后续根据地区清单来将数据进行分堆儿: ```bash cat data/pure.txt | awk -F '|' '{print $2}' | sort | uniq ``` 当我们执行完命令之后,可以看到类似下面的输出: ```bash * AD AE AF AG AI ... ... ZA ZM ZW ZZ afrinic apnic arin lacnic ripencc ``` 可以看到输出的内容包含了“空白内容”、“通配符(\*)”、“几个 RIR 机构名称”。**没错,这里是又一个坑,IP 数据除了包含各种地区的数据之外,还包含了一些“悬而未决”的数据,以及“各个机构的自留地”,需要在处理的时候去除掉。** 庆幸的是,我们可以通过强大的 AWK 表达式来过滤掉字符串长度不等于 2 的地区名称: ```bash cat data/pure.txt | awk -F '|' '{print $2}' | sort | uniq | awk '{ if (length($0) == 2) print }' > data/region.txt ``` 执行完命令,IP 归属地列表就搞定了。 ### 根据归属地整理 IP 数据 假设我们要做一个仅限国内用户访问网站的功能,需要筛选出国内的 IP 地址,以网上搜索到的命令为例,大概是这样操作: ```bash cat data/pure.txt | awk -F '|' '/CN/&&/ipv4/ {print $4 "/" 32-log($5)/log(2)}' cat data/pure.txt | awk -F '|' '/CN/&&/ipv6/ {print $4 "/" 32-log($5)/log(2)}' ``` 第一条命令的出处可能是这位博主在 2018 年写的[一篇博客](https://blog.dextercai.com/2018-06-14-11.html), 这条命令是没有问题的。 但是,第二条命令,一旦我们执行,我们将会得到类似下面的数据处理结果: ```bash 240f:4000::/27.415 240f:8000::/27.415 240f:c000::/27.415 2a03:f900::/27.142 2a07:f480::/27.142 2a09:3680::/27.142 2a0a:2840::/27.142 2a0d:280::/27.142 2a0e:7580::/27.142 2a10:1cc0::/27.142 ``` 好嘛,处理 CIDR 都搞出小数点了,这显然不对啊。**如果有人原封不动的使用这个命令,业务肯定出问题啊。** 而且,**这样的写法不够灵活**,如果我们需要同时允许某几个地区的话,难道要重复写几遍么?甚至极端情况下,如果我们要生成所有区域的数据,这个程序的行数恐怕会非常壮观。 解决上面这两个问题也不难,我们只要让程序能够动态读取列表,并修正 IPv6 相关数据的处理细节即可: ```bash #!/bin/sh # author soulteary mkdir -p result for region in $(cat data/region.txt) do echo "${region}" echo "cat data/pure.txt | awk -F '|' '/${region}/&&/ipv4/ {print \$4 \"/\" 32-log(\$5)/log(2)}' | cat > result/${region}-ipv4.txt" | bash - echo "cat data/pure.txt | awk -F '|' '/${region}/&&/ipv6/ {print \$4 \"/\" \$5}' | cat > result/${region}-ipv6.txt" | bash - done ``` 将上面的程序保存为 `split.sh`,然后使用 `bash split.sh` 执行程序。 稍等片刻,我们就能够在当前目录中看到一个新的目录 `result`,里面包含了若干以地区和 IP 类型命名的文件。 ```bash # ls result/ AD-ipv4.txt AZ-ipv6.txt BT-ipv4.txt CU-ipv6.txt EU-ipv4.txt GM-ipv6.txt IM-ipv4.txt KN-ipv6.txt MA-ipv4.txt MT-ipv6.txt NU-ipv4.txt QA-ipv6.txt SO-ipv4.txt TM-ipv6.txt VG-ipv4.txt AD-ipv6.txt BA-ipv4.txt BT-ipv6.txt CV-ipv4.txt EU-ipv6.txt GN-ipv4.txt IM-ipv6.txt KP-ipv4.txt MA-ipv6.txt MU-ipv4.txt NU-ipv6.txt RE-ipv4.txt SO-ipv6.txt TN-ipv4.txt VG-ipv6.txt AE-ipv4.txt ... ``` 如果我们使用命令查看文件: ```bash # head result/CN-ipv4.txt 1.0.1.0/24 1.0.2.0/23 1.0.32.0/19 ... # head result/CN-ipv6.txt 2001:250:2000::/35 2001:250:4000::/34 2001:250:8000::/33 ... ``` 就能够会看到已经处理完毕的各种数据啦。 ## 其他:免费数据的遗憾 在文章的开头,我曾提到,“对于不需要精准判断服务请求来源、归属地的场景下,使用全球五大 RIR 是一个低成本(免费)的方案”。 相信你只要按照上面分享的方法,不难得到每日更新的 IP 归属数据。这样的数据对于个人开发者来说,或许是足够使用的。**但是如果是生产场景,强烈建议考虑下基于路由表和 Ping 值检测等探测手段生成的商用 IP 库**。 因为 IP 数据每时每刻都在更新和变动,甚至在当前云服务时代还会出现跨地区“借用”的情况,造成判断非常不准确的情况发生。在本文写完的时候,我曾咨询国内头部 IP 数据供应商(就是知乎、微博、阿里等厂商都在用的那家)的创始人高春辉: “高总,如果使用 RIR 的数据做基础的归属地判断,和你们的数据差异有多少呢?” “严谨点的说,至少有 5%~10% 的差别,换算成数字,大概是 2~4 个亿个 IP 上会有出入,**国家量级的出入**。这是啥概念呢?我们按照 4 亿计算的话,恐怕把全球 IP 拥有量 TOP10 的后五名加一起才够这个数字。你知道这五个国家是哪儿么?是韩国、巴西、法国、加拿大、意大利...” 面对这个夸张的数据推测,我原本是半信半疑,直到我找到了两个表格,分别包含本文分享的 RIR 数据,和上面提到的、现在国内各大平台使用的 IP 数据: ![RIR 数据和国内主流 IP 数据对比](https://attachment.soulteary.com/2022/06/07/ip-data-table.jpg) 表格数据来源: - [https://impliedchaos.github.io/ip-alloc/](https://impliedchaos.github.io/ip-alloc/) - [https://www.ipip.net/support/data.html](https://www.ipip.net/support/data.html) ## 最后 写到这里,全球五大顶级注册机构的数据处理上的坑就分享完了。 下一篇相关的内容里,我会聊聊如何在 Golang 、Nginx 、WASM 中高效的使用这类 IP 数据,完成一些我们常见的互联网系统风控/产品策略功能。 --EOF