摘要

  • RFC 2186 中的 ICPv2 HIT 只说明:在响应发生的那个时刻,邻居缓存表示它拥有对应 URL,并允许该请求者尝试取回;对象通常仍需通过 HTTP 获取,因此 HIT 不是对象交付、HTTP 新鲜度或来源权威的证明。
  • ICPv2 把多个复杂状态压缩成快速选择信号:MISS_NOFETCH、DENIED、无响应乃至 source RTT 都能影响“现在选不选这个邻居”,但这些信号的设计目的不是诊断故障,也不是建立认证、完整性或未来可用性保证。
  • RFC 2187 明确暴露了这种窄证据模型的安全代价:ICPv2 没有认证字段,伪造 HIT 可以诱导错误选择,伪造 MISS_NOFETCH 或 DENIED 可以压制合法选择,而 HIT_OBJ 还会把选择提示进一步推向对象交付,因此受到更严格限制。

RFC 2186 由 D. Wessels 和 K. Claffy 于 1997 年 9 月发布,状态为 Informational。它描述的 Internet Cache Protocol version 2 并不是一个完整的对象传输协议,而是相邻 Web 缓存之间交换“这个 URL 在我这里是什么状态”的轻量协议。缓存节点借此选择下一步尝试取回对象的来源;在通常情况下,真正的对象传输仍由 HTTP 完成。

这种分工决定了 ICPv2 的证据强度。它回答的是“此刻值得向哪个邻居继续尝试”,而不是“哪个节点能够对对象做出权威声明”。两者表面接近,工程意义却完全不同。

ICPv2 的固定消息头为 20 octet,整个消息总长不得超过 16,384 octet。协议强调快速决策:查询与响应通常需要在大约 1–2 秒内完成。实践中它使用 UDP,因此一个查询没有得到响应时,原因可以是网络拥塞、路径故障、系统故障或其他无法从这一信号本身区分的情况。对选择算法而言,这些不同原因却可以被压缩成一个足够实用的结论:此刻不要选择这个邻居。

这不是故障诊断,而是决策降维。ICPv2 并不要求发送方在做选择之前找出无响应的根因。相反,它允许缓存把暂时无法使用的邻居快速从候选路径中排除。协议的价值来自这种有限性:只获取足以支持下一步动作的证据,而不等待更完整的解释。

HIT 最容易被过度理解。按照 RFC 2186,HIT 表示被查询的 URL 存在于缓存中,而且当前请求者被允许从该缓存取回它。但通常情况下,请求者接下来仍要通过 HTTP 请求对象。这意味着 HIT 是一次“可以尝试这里”的指示,而不是对象已经成功交付的凭据。

它不能单独证明对象具有权威来源,不能证明缓存副本仍满足 HTTP 的新鲜度条件,也不能证明响应者具有经过认证的身份。它同样不证明稍后进行 HTTP 请求时对象仍然存在、连接仍可建立、返回字节完整无误或应用最终能够安全使用这些内容。RFC 9111 所规范的 HTTP 缓存语义属于另一证据层;ICP 邻居提示不能替代这些语义。

Request Number 也体现了类似边界。它是查询中的不透明值,由响应复制回来,用于把响应与请求关联起来。URL 必须精确匹配,这能够帮助请求方知道某个响应对应哪个查询。然而,关联关系不是认证。一个值被正确复制,并不自动证明是谁生成了响应,也不把一次 ICP 交换升级为对象交付证明。

Sender Host Address 更进一步提醒实现者不要对字段名称赋予超出协议规定的意义。RFC 2186 将其视为不可信,并指出实践中并未使用它。查询中的 Requester Host Address 如果为零,则表示未指定。也就是说,即使协议头部包含与主机有关的字段,字段存在本身也不能被解释成经过认证的身份声明。

其他响应码同样是局部状态提示,而不是全局事实。MISS 表示本地没有所查询对象,但它并不必然意味着请求者不应经过这个邻居继续取回;邻居仍可能邀请请求通过它完成。MISS_NOFETCH 则更明确地说:缓存本身是活着的,但暂时不愿处理 miss,例如它可能正在重建对象存储。对于选择算法,这个区别很有价值;对于系统诊断,它仍然远远不够。

DENIED 的含义也必须保持局部。它针对的是特定请求者、特定 URL 和特定时刻。若 DENIED 比例异常升高,可能反映配置错误,但单个 DENIED 并不能证明某个资源普遍禁止访问,更不能证明资源在其他身份、时间或策略条件下不可获取。

SECHO 和 DECHO 的边界更加直接:它们只能证明 echo 机制可达。缓存软件本身可能根本没有运行。因此,把 echo 成功直接解释为缓存服务健康,就是把网络层面的可达信号错误扩展成应用状态证明。

source RTT 也遵循相同原则。它可以利用已有测量结果,也可以为零或缺失,但响应方不得为了获取一项新的 RTT 测量而延迟 ICP 响应。这个限制说明协议优先维护的是选择时效,而不是测量完整度。ICPv2 宁愿接受缺失的性能证据,也不愿让测量本身拖慢一次需要迅速完成的邻居选择。

最特殊的操作码是 HIT_OBJ。它允许响应直接携带对象,因此看起来像是把“选择提示”和“对象交付”合并起来。但 RFC 2186 明确不推荐这种方式,并要求显式 opt-in。原因并非只是实现复杂度:HIT_OBJ 会绕过正常 HTTP 授权和 age validation,还可能因为消息超过路径 MTU 而发生 IP 分片。如果不能把完整对象装入允许的消息范围,协议要求退回普通 HIT,而不是发送一个不完整对象。

这实际上构成了一条重要的架构边界。普通 HIT 可以很弱,因为后续 HTTP 流程仍然承担更强的对象语义;HIT_OBJ 一旦试图跨越这条边界,就立即面对授权、新鲜度、报文大小和完整交付等原本不属于 ICP 简单选择模型的问题。

RFC 2187 对安全问题的讨论进一步说明了为什么不能把 ICP 响应当作凭据。ICPv2 没有认证字段。攻击者如果能够伪造 HIT,可以诱导请求者选择一个本不该选择的来源;伪造 MISS_NOFETCH 或 DENIED,则可能阻止某个邻居被选择。在 multicast 环境中,来自未配置邻居的响应应被忽略,因为仅仅收到响应并不能建立可信邻居关系。

地址欺骗与 HIT_OBJ 的组合还可能造成缓存污染。这一风险再次证明,协议返回“我有这个 URL”的消息,与接收方能够建立可靠的内容来源链之间存在明显距离。响应可以驱动下一步选择,却不能凭自身承担身份认证和内容可信性的责任。

因此,ICPv2 的成功不应被理解为它提供了大量证据,而应理解为它准确限定了自己需要提供多少证据。一个 HIT 可以足够强,强到让缓存决定“先试这个邻居”;同时又必须足够弱,弱到不能被写成“这个邻居已经证明自己能正确、安全、及时地交付这个对象”。

这也是阅读历史协议时值得保留的区别。一个信号是否有价值,不能只看它能不能证明最终结果。某些基础设施信号的价值恰恰在于,它们以很低的成本支持一个范围狭窄、时间敏感、可逆的下一步动作。问题出现在证据被提升等级的时候:当 URL 存在性提示被当成来源权威,当请求关联被当成身份认证,当 echo 被当成应用健康,当 HIT 被当成 HTTP 新鲜度,当一次成功响应被当成未来持续可用性。

RFC 2186 没有要求 ICPv2 承担这些责任。保持这种边界,才是理解该协议最重要的历史经验。