为什么协议字段是 IP 报头的一部分?

网络工程 ipv4 ip 协议理论
2021-07-18 19:51:26

请注意,这不是为什么 IP 层知道网络堆栈中的更高层的重复

在基于数据包的通信中对协议标识符(例如 IP 标头的协议字段)的需求是显而易见的:要么是这种,要么是某种计算密集型推理算法。问题是:为什么它必须作为 IP 标头的一部分而不是封装协议的标头中的一部分存在?

在我看来,这是理论清晰度满足实际考虑的情况之一(又名“Haskell 满足 Go”...):一方面,在 IP 标头中放置“协议”字段打破了概念上的利益分离,例如. OSI 模型旨在;另一方面,强制协议栈更高的协议以一致的方式声明它们的类型要困难得多,并且最终会导致类似的情况(例如,如果堆栈更高的每个协议都使用它的第一个头字节来声明它的类型,看起来好像 IP 使用其最后一个标头字节来做同样的事情)。

所以我的问题是 - 将“协议”字段放置在 IP 的数据包标头内而不是其他任何地方的原因是什么?

编辑:在写这个问题我考虑是否要加上“原创”,“推理”之前,该设计团队即推理IP,但估摸这是因为这个问题在过去时措辞(“什么多余的的推理...”)。尽管如此,这似乎是必要的,因为没有一个回复实际上回答了这个问题。一些注意事项:

  • @immibis 建议任何其他形式都会破坏其他协议的模型(例如,加密通信协议必须有一个明文识别字段)
  • @Eddie 本质上说原因是约定(接受协议链设计,尽管为什么这是约定仍然是个谜)
  • @Ricky 强调实用性是首要考虑因素
  • @Claudio 建议,如果协议字段是封装标头的一部分,则需要额外的标头识别步骤,在当前模型中,在 IP 标头解析期间发生

所以我会改写:模型有什么问题,其中每个标头不是每个标头标识下一个标头的类型,而是在预定位置(例如,在第一个标头字节中)标识自己的类型?为什么这样的模型比当前的模型更不受欢迎?

编辑#2:似乎答案是给出的几个答案的组合(主要是上面提到的那些以及@Eddie 的第二个附录):

  1. 简单性:这种特殊情况下打破层不可知论的原则意味着整个堆栈(或模型)可以更简单:

    • 没有“协议识别”阶段,既不隐含也不明确
    • 层独立性得到改进(例如,加密的通信处理程序不必与任何帮助协议共享层)

    监管也大大简化,无需对客户端协议强制执行任何要求。

  2. 性能:在数据包本身之前声明封装数据包的协议允许将几种类型的快速路由协议(数据包过滤、QOS、直通交换)集成到网络(互联网)层本身;然后,它们可以像访问哈希表一样快速做出决定,考虑到该协议设计用于运行的有限硬件,这一点更为重要。

这个模型有它的缺点,但对于常见的用例,它似乎比替代方案更适合。

4个回答

请记住,位以一系列 1 和 0 的形式到达 NIC。必须存在某些东西来指示应如何解释下一个系列的 1 和 0。

Ethernet2 是 L2 的事实标准,因此假设将前 56 位解释为前导码,将接下来的 8 位解释为前导码,接下来的 48 位作为目标 MAC,接下来的 48 位作为源MAC,等等等等

唯一的变化可能是有些过时的802.3 L2 标头,它早于当前的 Ethernet2 标准,但也包括一个用于相同目的SNAP 标头但是,我离题了。

标准的 Ethernet2 L2 报头有一个 Type 字段,它告诉接收节点如何解释后面的 1 和 0: 突出显示类型字段的以太网报头

如果没有这个,接收实体如何知道 L3 标头是 IP 还是 IPv6?(或 AppleTalk、或 IPX、或 IPv8 等...)

L3 报头(与上述相同的帧)具有协议字段,它告诉接收节点如何解释 IP 报头后面的下一组 1 和 0: 突出显示协议字段的 IP 标头

同样,如果没有这个,接收实体如何知道将这些位解释为 ICMP 数据包?可能也是TCP,UDP或者,或GRE或其他IP报头,或他人的太多了。

这创建了一种协议链,以向接收实体指示如何解释下一组位。如果没有这个,接收端将不得不使用启发式(或其他类似策略)首先识别报头的类型,然后解释和处理位。这将在每一层增加大量开销,并在数据包处理中显着延迟。

在这一点上,查看TCP 标头UDP 标头并指出这些标头没有类型或协议字段很诱人……但是请记住,一旦 TCP/UDP 解释了这些位,它就会将其有效载荷传递给应用程序。这无疑可能有某种标记来至少识别 L5+ 协议的版本。例如,HTTP 在 HTTP 请求中内置了一个版本号:(1.0 vs 1.1)。


编辑以与原始海报的编辑交谈:

模型有什么问题,而不是每个标头标识下一个标头的类型,而是每个标头在预定位置(例如,在第一个标头字节中)标识自己的类型?为什么这样的模型比当前的模型更不受欢迎?

在尝试回答之前,我认为值得注意的是,关于为什么一种方式更好或另一种方式可能没有明确的百万美元答案。在这两种情况下,协议识别自身与协议识别它封装的内容,接收实体将能够正确解释这些位。

也就是说,我认为识别下一个标头的协议更有意义有几个原因:

#1

如果标准是让每个标头的第一个字节来标识自己,那么这将一层的每个协议中设置一个标准这意味着如果只有一个字节是专用的,我们只能有 256 个协议。即使您指定了两个字节,您的上限也为 65536。无论哪种方式,它都对可以开发的协议数量设置了任意上限。

而如果一个协议只负责解释下一个协议,即使每个协议标识字段只有一个字节专用于,至少您可以将 256 个最大值“缩放”到每一层。

#2

以允许接收实体仅检查最低限度的选项来对其字段进行排序的协议,仅当下一个协议字段存在于前一个标头中时才存在。

以太网2 和“直通”切换浮现在脑海中。如果前(几个)字节被强制为协议标识块,这将是不可能的。

#3

最后,我不想邀功,但我认为@reirab在评论答案在原来问题的意见是非常可行的:

因为那么它实际上只是 IPv4(或任何较低级别的协议)标头的最后一个字节,除了名称。这是一个“先有鸡还是先有蛋”的问题。如果您不知道它是什么协议,则无法解析标头。

经 Reirab 许可引用

您可能会问为什么以太网头有一个 Ether Type 字段。网络堆栈需要知道下一个更高层的哪个协议获得当前层的有效载荷。

编辑1:

每个数据报都有下一个上层协议的原因是为了创建层独立性。每一层都不关心有效载荷中的内容,并且不需要查看有效载荷来确定将有效载荷传送到何处。将标头中的协议编号视为要传送有效负载的地址。就像 TCP 端口号是 TCP 地址一样,它们告诉 TCP 将其有效负载传送到何处。

目标 MAC 地址告诉网络交换机哪个交换机接口传送帧。Ether Type 字段告诉第 2 层将其有效载荷传送到何处,IP 标头中的协议字段告诉第 3 层将其有效载荷传送到何处,TCP 和 UDP 中的端口号告诉第 4 层将其有效载荷传送到何处。

想想一个 18 轮卡车司机,他连接到拖车上将其运送到某个地方。他不需要担心预告片里有什么,或者它会被用来做什么;他只是看着他的文书工作并将其交付给文书工作的地方。

您需要记住,每个协议都是独立开发的,而不知道将使用哪些新兴的上层协议。长期以来,以太网上使用的主要第 3 层协议是 IPX。如果以太网是专门为 IPX 创建的,它今天会如此普遍吗?以太网通过具有以太网类型字段来承载任何第 3 层协议,网络堆栈可以使用该字段来决定以太网负载的去向。IP 做同样的事情,TCP 和 UDP 也是如此。这是一种简单且合乎逻辑的方法,这就是为什么网络堆栈中的每个独立开发的层都有一个等效的原因。您和其他任何感兴趣的人都可以自由地为任何层开发自己的协议,因此可以轻松插入网络堆栈。

编辑2:

它允许不同的第 3 层协议向第 2 层注册。您可以同时运行 IPX (0x8137)、IPv4、(0x0800)、ARP (0x0806)、IPv6 (0x86DD) 等协议,第 2 层将知道哪些协议已向其注册并将有效负载传递给适当的层 - 3 协议而不知道任何有关有效负载的信息(或丢弃任何没有注册协议的数据包)。您不希望必须为您拥有的每个第 3 层协议组合安装不同的第 2 层协议,如果第 2 层协议必须更多地了解第 3 层协议,则需要这样做) 以便能够读取数据包头。甚至 IPv4 和 IPv6 数据包标头也大不相同。

这是可能在第 2 层注册的不同第 3 层协议的不完整列表

第 4 层协议也注册到各种第 3 层协议,应用程序注册到第 4 层协议。

您最初的问题假设层应该相互独立,这种事情实际上促进了层独立,而不是像您建议的那样破坏它。第 2 层不知道有效载荷是 IPv4,它只知道以太类型是 0x0800,它应该将有效载荷传递给注册了该以太类型的第三层协议。

不是直接回答您的问题,而是:

一方面,在 IP 报头中放置一个“协议”字段打破了 OSI 所针对的利益概念分离。

TCP/IP 的开发没有参考 OSI 模型。虽然它们有一些共同点,但这是一项单独的开发工作。

最简单的原因是帮助解析何时接收到数据包。

如果您知道遵循哪个协议,则可以制定更严格的约束。IP 数据包中唯一的动态方面是大小(IP 选项的存在以四字节的倍数增加此大小)。

在解析阶段,您可以检查 ip 头长度、协议。然后,在低级数据包验证中,通过数据结构(通常是 icmp、tcp、udp 标头)读取数据包数据,并使用简单的数据包进行验证。