关于 169.254.0.0/16 地址的一点笔记

一年之前

年轻的 Nova 在为语音楼计算机教室配置网络的时候,由于配置失误,所有的计算机都无法访问到网络,挑了几台计算机 ipconfig 了一下发现清一色的都是 169.254 开头的 IP,且做 tracert 的时候全部断在第一跳,于是我在未经求证的情况下武断的认为:在 Windows 系统下,所有没有分配到 IP 的计算机不会像 Linux 一样不显示 IP,而是显示成 169.254 开头的一个 IP.

昨天

在程序设计大赛团队赛 打酱油 的时候,老师在投影上给出了 pc2server 的 IP 地址竟然也是一个 169.254 的 IP 地址.“机智的我” 一下子就看出了 “其中的问题”,并立刻举起了手向全班宣布了这个 IP 是个不可达的 IP 的消息,老师倒也不惊讶,淡淡地说道:我刚刚测试的一台电脑是可以连接的,你那边是不是网线没插上?

不行,我必须解释这一切,说着我打开了 cmd 开始一边输入 ping <那个 IP 地址> 一边解释这个 IP 是微软的系统在没有分到 IP 的时候自动给自己分的一… 话还没说完,ping 的结果就出来了,4 个包全部到达,延迟 < 1ms.

“哦哦,没问题,了,老师…”

然后就看到身旁的妹子 (然而并不是我的) 和其他队的成员诧异看着我…

169.254.0.0/16 的来历

有了昨天被快速打脸的经历后,我决定探索一下这个 IP 的来历和昨天事件的本质.

首先很显然,169.254 这个 IP 是一个保留地址,如果你去 ip.cn 上面查询的话,会告诉这个是 “非 Internet 地址”,当然,这个不是我们想要的结果.

根据 RFC 3927,如果一个网络接口被配置了 DHCP 但是 DHCP 服务器并没有为其分配 IP 的话,这个接口就会自动给自己分配这个称为 link-local IPv4 address 的地址,或者用微软的叫法称为 Automatic Private Internet Protocol Addressing (APIPA).

高中自学 CCNA 的时候听说过令牌环网这么个东西,不过一直没有见到过实际应用场景,就没有去深究,现在想想 RFC 3927 定义的这个 link-local 地址不正是一个令牌环网的原型么?如果一个子网内的机器都找不到 DHCP 服务器,那干脆自成一派,全部给自己分配成一个网段(也就是 169.254 开头的 IP),在实际场景,比如学校的机房中,所有的计算机都是连接在一个 L2 交换机上,这样在一个被隔离的网段中的计算机通过计算发现对方 IP 与自身的子网掩码相同从而将自己的数据包交给交换机进行转发,达到内网互通的效果.

然后我就在考虑一个新的问题:既然机房的计算机全部是一键安装的,那初始化的 169 开头 IP 应该会是同一个而导致冲突才对啊?

仔细阅读了 RFC 之后才发现自己 naïve 了,原文如下:

When a host wishes to configure an IPv4 Link-Local address, it selects an address using a pseudo-random number generator with a uniform distribut on in the range from 169.254.1.0 to 169.254.254.255 inclusive.
If the host has access to persistent information that is different for each host, such as its IEEE 802 MAC address, then the pseudo-random number generator SHOULD be seeded using a value derived from this information.

这样看来有两重保证,首先这个 / 16 的 B 类地址段可以保证有一个 65536 的池供选择,其次可以通过几乎全球唯一的标识符(比如 MAC 地址,UTC 时间)来生成自己 IP 地址的后两位做到冲突的避免,除此之外,为了保险起见,每个地址生成之后还需要有一个 check 的操作,RFC 原文如下:

A host probes to see if an address is already in use by broadcasting an ARP Request for the desired address. The client MUST fill in the ‘sender hardware address’ field of the ARP Request with the hardware address of the interface through which it is sending the packet. The ‘sender IP address’ field MUST be set to all zeroes, to avoid polluting ARP caches in other hosts on the same link in the case where the address turns out to be already in use by another host.

通过一个广播 IP 地址的 ARP 广播包来判断自己生成的 IP 是否被使用过,嗯,这个设计简直妙不可言~

总结 & 思考

  • 这件事情说明了什么?
    千万不要想当然地 “理解” 一个事物的本质,否则很容易自打脸
  • 为什么正常工作的机房网络会突然全部获取不到 IP 地址?
    可能是为了防止有人比赛的时候登录 Drcom 去网上查询吧, 但是他们都有手机啊!!还有自己之前写过的代码

  • 是不是真的只有 Windows 才会有 169.254 这个 IP 呢?
    其实并不是,这个是 RFC 规范,所有的系统都会按照这个规范来操作,对于 Linux 而言,如果在这样的网络环境中,这个情况会以 route 的形式表现出来:

    1
    2
    3
    4
    5
    Kernel IP routing table  
    Destination Gateway Genmask Flags Metric Ref Use Iface
    ... (bunch of things) ...
    169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 eth0
    192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

参考资料

  1. Why 169.254.0.0 appears by default in the routing table?
  2. 169.254.0.0/16 addresses explained

我的博客使用了Disqus评论框,如果你看不到评论框,那么多半Disqus服务在你所在的地区被墙,请使用代理访问。