bigywater.tech
网络协议 · 20

私有地址、链路本地(169.254)、回环与保留地址段

10.x、192.168.x、169.254.x、127.x、100.64.x、198.51.100.x……这些地址到底有什么区别?发给它们的包在本机会怎么走?is_private 又准不准?这篇先把特殊地址段列成表,再往每类地址发 UDP 包看第一个帧去了哪里,最后看真实公网路径上的私有地址。

13.80%
特殊用途地址占整个 IPv4 空间的比例(592,708,608 / 2^32)
100.64.1.1
Python ipaddress 的 is_private 和 is_global 都为 False,是 16 个样本里唯一一个
默认网关
私有、文档、保留、公网地址的第一个帧的目的 MAC 都是它,路由表只看路由,不看地址属于哪类

地址段表

实测 · 本机执行输出
    地址段               个数         用途
    0.0.0.0/8           16,777,216    “本网络”,只能作源地址(如 0.0.0.0 用于启动时)
    10.0.0.0/8          16,777,216    私有地址
    100.64.0.0/10        4,194,304    共享地址空间(运营商级 NAT 用,RFC 6598)
    127.0.0.0/8         16,777,216    回环
    169.254.0.0/16          65,536    链路本地(自动配置)
    172.16.0.0/12        1,048,576    私有地址
    192.0.0.0/24               256    IETF 协议分配
    192.0.2.0/24               256    文档用 TEST-NET-1
    192.168/16              65,536    私有地址
    198.18.0.0/15          131,072    网络基准测试
    198.51.100.0/24            256    文档用 TEST-NET-2
    203.0.113.0/24             256    文档用 TEST-NET-3
    224.0.0.0/4        268,435,456    组播
    240.0.0.0/4        268,435,456    保留(含 255.255.255.255 受限广播)
  合计 592,708,608 个地址,占整个 IPv4 空间(2^32 = 4,294,967,296)的 13.80%;其中私有地址三段共 17,891,328 个

0/8、10/8、100.64/10、127/8、169.254/16、172.16/12、192.0.0/24、三个文档 /24、192.168/16、198.18/15、224/4、240/4 一共约 5.9 亿个地址,占 IPv4 空间的 13.80%。私有地址三段合计 17,891,328 个。

程序里怎么判断

实测 · 本机执行输出
  Python 3.14.8
    样本地址            private global loopback linklocal multicast reserved
    0.0.0.1                  是       -       -       -       -       -
    10.1.2.3                 是       -       -       -       -       -
    100.64.1.1               -       -       -       -       -       -
    127.0.0.1                是       -       是       -       -       -
    169.254.1.1              是       -       -       是       -       -
    172.16.5.5               是       -       -       -       -       -
    192.0.0.9                -       是       -       -       -       -
    192.0.2.1                是       -       -       -       -       -
    198.18.0.1               是       -       -       -       -       -
    198.51.100.1             是       -       -       -       -       -
    203.0.113.1              是       -       -       -       -       -
    224.0.0.1                -       是       -       -       是       -
    240.0.0.1                是       -       -       -       -       是
    255.255.255.255          是       -       -       -       -       是
    8.8.8.8                  -       是       -       -       -       -
  既不是 private 也不是 global 的样本:['100.64.1.1']——“私有”和“全球可达”并不是互补的两类;100.64/10 就是这样一个例子

ipaddress 模块的 is_private 与 is_global 并不互补;100.64/10 两个都是否,所以用 is_private 筛“内网地址”会漏掉运营商级 NAT 的地址。

包去了哪里

实测 · 本机执行输出
  回环 127.0.0.1:发送成功;en0 上 0 个帧:无;lo0 上 1 个包
  回环段内的 127.0.0.2:发送成功;en0 上 0 个帧:无;lo0 上 1 个包
  0.0.0.0:发送失败 [Errno 65] No route to host;en0 上 0 个帧:无;lo0 上 0 个包
  私有 10/8:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  私有 172.16/12:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  私有 192.168/16 里本网段之外的地址:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  共享地址 100.64/10:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  链路本地 169.254/16:发送成功;en0 上 1 个帧:先发 ARP 请求(广播,问这个地址是谁);lo0 上 0 个包
  文档 192.0.2.1:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  基准测试 198.18/15:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  组播 224.0.0.251(mDNS):发送成功;en0 上 1 个帧:IP 包,目的 MAC = 组播 01:00:5e:…;lo0 上 1 个包
  组播 239.255.255.250(SSDP):发送成功;en0 上 1 个帧:IP 包,目的 MAC = 组播 01:00:5e:…;lo0 上 0 个包
  保留 240.0.0.1:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包
  受限广播 255.255.255.255:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 广播;lo0 上 1 个包
  本网段的广播地址:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 广播;lo0 上 1 个包
  本网段里一个没人用的地址:发送成功;en0 上 1 个帧:先发 ARP 请求(广播,问这个地址是谁);lo0 上 0 个包
  公网 8.8.8.8:发送成功;en0 上 1 个帧:IP 包,目的 MAC = 默认网关;lo0 上 0 个包

本机的路由表不关心地址是不是私有 / 保留:没有更具体的路由就走默认网关;直连网段上的地址(169.254、本网段)先 ARP;组播和广播用特殊的目的 MAC,广播和组播还会在 lo0 上有一个本机副本。

bind 与 connect

实测 · 本机执行输出
  UDP bind 0.0.0.0       成功
  UDP bind 127.0.0.1     成功
  UDP bind 127.0.0.2     失败:Can't assign requested address
  UDP bind 本机 en0 地址     成功
  UDP bind 文档 192.0.2.1  失败:Can't assign requested address
  UDP bind 私有 10.99.99.99失败:Can't assign requested address
  UDP bind 组播 224.0.0.251成功
  UDP bind 255.255.255.255失败:Can't assign requested address
  UDP bind 本网段广播地址       成功
  TCP connect 127.0.0.2:50578 → TimeoutError:timed out,用时 1501 ms
  TCP connect 0.0.0.0:50581 → 连上了(进了本机监听的端口),用时 0 ms
  TCP connect 255.255.255.255:50583 → OSError:Address family not supported by protocol family,用时 0 ms
  TCP connect 240.0.0.1:50584 → TimeoutError:timed out,用时 1501 ms

只有配置在本机接口上的地址能 bind;127.0.0.2 在 macOS 上既不能绑也连不上;连 0.0.0.0 会连到本机监听的端口;对 255.255.255.255 做 TCP connect 立刻失败。

出现在路径上

实测 · 本机执行输出
  前 12 跳里有 8 跳回应了,其余无应答;各类别个数:私有 10/8 1;私有 172.16/12 1;公网地址 6
  各跳类别:2:私有 3:私有 4:公网地址 6:公网地址 8:公网地址 10:公网地址 11:公网地址 12:公网地址
  路径上的私有地址是路由器的接口地址:运营商 / 校园网内部可以用私有地址编号,包仍然能被转发;这些地址在互联网上不全局唯一,所以只能出现在各自的网络里(超时报文却会把它们带出来)

公网路径上有 10/8 和 172.16/12 地址的路由器接口,说明私有地址只是不能全球路由,并不会从路径中消失。

分步演示

特殊地址段
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一节是查表加计算(IANA 特殊用途地址登记),第二节是 Python 3.14.8 的 ipaddress 实际输出,第三、四节是本机 en0 / lo0 上的抓包与套接字调用,第五节是一次 traceroute 的地址类别统计(路由器地址不写出);默认网关与本机地址做了遮盖
    • 没有验证:192.88.99/24 等已废弃的段;ipaddress 在其它 Python 版本里的分类差异(这里只测了 3.14.8);发往这些地址的包被默认网关之后如何处理(丢弃还是回 ICMP)只在 w15 里测了一部分;IPv6 的特殊地址(见 IPv6 地址一篇);各路由器 / 运营商是否过滤 bogon 地址
    京ICP备2026067502号-1京公网安备11010502063487号