摘要

  • RFC 1048 把 99.130.83.99 放在 BOOTP 厂商字段最前面,用来选择统一的解释语法;普通选项随后以“标签、长度、值”排列,旧客户端因此可以越过不认识的新选项。
  • cookie 只说明字节应怎样解析,不认证服务器,也不保证路由器、DNS 或启动文件信息正确。64 字节总长、Pad、End 与代码分配解决的是互操作和容量问题,不是信任问题。

RFC 951 设计 BOOTP 时,面对的是一台尚未完成网络身份配置的机器。它可以知道网卡硬件地址,也可以发送一个小型引导程序构造的请求,却还不知道自己的 IP、可用服务器或要下载的文件。BOOTP 先回答这些问题,随后再由其他协议传输启动文件。

报文里有一个名为 vend 的 64 字节厂商专用字段。这个空间让不同设备可以携带更多启动参数,也埋下了解释冲突:同一串字节,在两家实现中可能代表完全不同的配置。RFC 951 已建议在字段开头放四字节“magic number”,用来辨认数据类型,但其后仍没有一套共同结构。

1988 年 2 月发布的 RFC 1048 没有扩大这个字段,而是为它建立一种可以扩展的小语法。不同厂商不必先共享全部选项,也能找到自己认识的部分。这个设计的重要之处正在于克制:它明确“怎样读”,却不越权回答“谁说的”与“该不该信”。

Cookie 识别解释方式,不识别说话者

RFC 1048 规定前四个字节为十进制 99.130.83.99,即十六进制 63.82.53.63,按网络字节序发送。规范给它的作用很具体:标识后续数据采用哪一种解释模式。

客户端看到这个值,便可以选择 RFC 1048 解析器,而不是某家厂商的私有布局。这只是一条表示证据。四个字节是公开常量,任何发送者都能复制;它们不表明制造商身份,不认证 BOOTP 服务器,不证明中继没有改动报文,也不证明其中的网关归合法管理者控制。

BOOTP 还有事务号、硬件地址和若干 IP 地址字段。它们可以帮助客户端把回答同正在等待的请求关联起来,并把报文送到合适位置。但“像是这一次请求的回答”与“来自获准回答的人”不是同一判断。事务相关性不能把公开 cookie 变成密钥。

RFC 1542 后来把后果说得更直白:BOOTP 没有合理的认证机制,未经授权的服务器可能给客户端提供虚假的 IP 地址、路由器或 DNS 服务器。攻击者无需破坏格式;一份结构完全正确的错误配置就足以产生影响。

长度字段把“不认识”限制在一个边界内

cookie 之后,普通选项依次包含一字节标签、一字节长度,以及相应数量的值字节。长度只计算值,不包含标签和长度自身。多字节数字采用网络字节序。

这套安排最有价值的地方不是名称,而是跳过未知项的能力。旧客户端若不认识某个标签,可以依据长度前移到下一项,不必猜测未知数据在哪里结束。新选项因此不会天然破坏后面的旧选项;双方可以在并不拥有完全相同词典时继续交换一部分信息。

这是一种受控的无知。接收端诚实承认“不懂这一项”,同时保持对整个字段边界的把握。它不需要把未知代码误认成错误,也不必把整份报文当成不可读。

但长度只证明一段值占多少字节,不证明值是什么。一个本地专用标签在两个网络里可能含义不同;一个四字节地址可以过期或被恶意填入;解析器即使正确移动,也可能在实现中出现越界缺陷。RFC 1084 与 RFC 1497 更新选项目录时继续沿用这种结构,说明扩展边界有用,却不能证明所有实现都同样可靠。

Pad 与 End 规定何时不要再读

标签 0 是 Pad,只占一个字节,没有长度字段,用来对齐或填充空位。标签 255 是 End,也只占一个字节,表示选项序列结束;后面剩余空间以零补齐。

这两个例外属于语法控制,而非普通的零长度数据。若解析器在 Pad 后错误地读取长度,后续位置都会错位;若在 End 后继续寻找标签,填充零就会被虚构成配置。可扩展格式不仅要规定“怎样增加”,还必须规定“怎样停止”。

选项之间也并非永远无序。RFC 1048 要求当子网掩码与网关同时出现时,掩码必须在前。客户端要先理解本地网络范围,才能正确解释网关。标签让每项可定位,却没有消除语义依赖。

所以,这 64 字节实际承载的是一套很小但完整的阅读纪律:选择语法、读取一般项、识别两个控制例外、遵守必要次序、在 End 处停止。只说它是 TLV,会漏掉决定互操作是否成立的边角规则。

容量上限把扩展变成取舍

cookie 先占四字节。一个普通选项在值之外还要消耗两字节。一条 IPv4 地址再占四字节;地址列表则成倍增加。RFC 1048 明确要求内容不得超过 vend 的 64 字节,并建议删去非必要信息,改由其他发现服务取得。

这使“应当先告诉客户端什么”成为实际决策。名称服务器、时间服务器、日志服务器、网关与其他参数都可能有用,却不能无限塞入启动阶段。标准定义了容器,运营者决定本地优先级,客户端决定自己采取哪些值。

代码号同样稀缺。可普遍使用的通用字段应经过登记,以免多个公开定义争用同一个数字;128 至 254 保留给站点专用含义。后者允许本地试验,但只在本地约定中有效。共享标签和长度并不会自动把私有语义变成全球语义。

这种结构既降低扩展成本,也产生锁定。固件、服务器和运维流程一旦共同依赖同一语法,新增代码通常比更换整个外壳便宜。局部演进越方便,64 字节边界及其历史例外就越可能长期保留。

集中维护不等于形成信任根

RFC 1048 比较了集中维护的 BOOTP 数据与更分散的发现方式。若一个回答就能提供启动所需的全部信息,效率很高;客户端收到后即可继续。但规范也承认集中数据可能不完整或陈旧。

维护 BOOTP 数据库的人有权决定本地服务器发布哪些启动配置。这种控制不等于文件服务器身份已经得到证明,更不等于启动镜像可靠。RFC 1048 特别指出,BOOTP 数据库本身不足以为文件服务器提供强认证;那必须由对应服务在自己的层次解决。

因此,一次看似成功的启动至少含有几条不同记录:报文可以按某种语法解析;回答同请求相关;某个服务器路径提供了配置;后续服务完成了自己的认证和传输。把这些都叫作“cookie 有效”,会让故障与责任无法定位。

RFC 1542 建议客户端即使没有选项数据,也要发送 cookie、End 和零填充,以便服务器知道回答应使用哪种格式。这几乎是一份“只有语法、没有内容”的声明,恰好证明 cookie 的职责不是授权。

DHCP 继承了格式,没有继承一份保证

RFC 1533 规定 DHCP 选项继续使用 BOOTP 厂商扩展格式:除 Pad 与 End 外,均由标签、长度和值组成;magic cookie、网络字节序和站点专用范围也得以保留。RFC 2132 又在这条谱系上整理了更成熟的 DHCP 选项目录。

这是文档明确支持的格式沿革,不表示 1988 年的 BOOTP 已拥有后来 DHCP 的所有操作习惯,也不表示每个选项语义从未变化。RFC 1533 甚至直接说明不讨论安全问题。容器延续了,来源权威并没有因此进入容器。

RFC 1048 真正留下的是一条可执行的共同边界:四个字节选择解释规则,长度允许越过未知项,Pad 与 End 控制读取,代码空间和 64 字节上限约束扩展。身份、时效、授权和最终服务结果则继续由别的机制负责。

来源与证据边界

RFC 951 给出原始 BOOTP、64 字节厂商字段和 magic number 建议;RFC 1048 定义通用 cookie、选项语法、顺序、分配和容量规则;RFC 1084 与 RFC 1497 记录后续 BOOTP 修订;RFC 1533、RFC 1542 和 RFC 2132 说明其进入 DHCP 的格式沿革与 BOOTP 安全边界。

这些资料能证明规范内容和文档演变,不能测量历史部署比例,也不能证明某个具体网络、服务器或实现曾正确采用这些规则。