网络和 Linux 这两个术语几乎是同义词。从某种意义上说,Linux 是互联网或万维网 (WWW) 的产物。其开发者和用户使用 Web 交换信息、想法、代码,而 Linux 本身通常用于支持组织的网络需求。本章描述了 Linux 如何支持统称为 TCP/IP 的网络协议。
TCP/IP 协议旨在支持连接到 ARPANET(由美国政府资助的美国研究网络)的计算机之间的通信。ARPANET 开创了诸如分组交换和协议分层之类的网络概念,其中一个协议使用另一个协议的服务。ARPANET 于 1988 年退役,但其继任者(NSF1 NET 和 Internet)甚至变得更大。现在被称为万维网的网络是从 ARPANET 发展而来的,并且它本身也受 TCP/IP 协议的支持。Unix TM 在 ARPANET 上被广泛使用,并且发布的第一个网络版 Unix TM 是 4.3 BSD。Linux 的网络实现以 4.3 BSD 为模型,因为它支持 BSD 套接字(带有一些扩展)和全范围的 TCP/IP 网络。选择此编程接口是因为它很受欢迎,并且有助于应用程序在 Linux 和其他 Unix TM 平台之间移植。
通常,IP 地址很难记住。名称更容易记住。linux.acme.com比16.42.0.9更容易记住,但是必须存在某种机制将网络名称转换为 IP 地址。这些名称可以静态地在/etc/hosts文件中指定,或者 Linux 可以要求分布式域名服务器(DNS 服务器)为其解析该名称。在这种情况下,本地主机必须知道一个或多个 DNS 服务器的 IP 地址,这些地址在/etc/resolv.conf.
中指定。无论何时连接到另一台计算机,例如阅读网页时,都会使用其 IP 地址与该计算机交换数据。此数据包含在 IP 数据包中,每个数据包都有一个 IP 标头,其中包含源和目标计算机的 IP 地址、校验和以及其他有用的信息。校验和是从 IP 数据包中的数据派生的,并允许 IP 数据包的接收者判断 IP 数据包在传输过程中是否损坏,可能是由于噪声电话线造成的。应用程序传输的数据可能已被分解为更小的数据包,这些数据包更容易处理。IP 数据包的大小取决于连接介质;以太网数据包通常比 PPP 数据包大。目标主机必须在将数据提供给接收应用程序之前重新组装数据包。如果您通过速度较慢的串行链接访问包含大量图形图像的网页,则可以直观地看到数据的这种分段和重新组装。
连接到同一 IP 子网的主机可以直接相互发送 IP 数据包,所有其他 IP 数据包都将发送到特殊主机,即网关。网关(或路由器)连接到多个 IP 子网,它们会将在一个子网上接收到的但要发送到另一个子网的 IP 数据包转发。例如,如果子网16.42.1.0和16.42.0.0通过网关连接在一起,那么从子网0发送到子网1的任何数据包都必须定向到网关,以便它可以路由它们。本地主机建立路由表,使其可以将 IP 数据包路由到正确的计算机。对于每个 IP 目标,路由表中都有一条条目,告诉 Linux 将 IP 数据包发送到哪台主机才能到达其目标。这些路由表是动态的,并且随着应用程序使用网络以及网络拓扑结构的变化而随时间变化。
IP 协议是一个传输层,其他协议可以使用它来承载其数据。传输控制协议 (TCP) 是一种可靠的端到端协议,它使用 IP 来发送和接收其自身的数据包。就像 IP 数据包有自己的标头一样,TCP 也有自己的标头。TCP 是一种基于连接的协议,其中两个网络应用程序通过单个虚拟连接连接,即使它们之间可能存在许多子网、网关和路由器。TCP 可靠地在两个应用程序之间传输和接收数据,并保证不会丢失或重复数据。当 TCP 使用 IP 传输其数据包时,IP 数据包中包含的数据就是 TCP 数据包本身。每个通信主机上的 IP 层负责发送和接收 IP 数据包。用户数据报协议 (UDP) 也使用 IP 层来传输其数据包,与 TCP 不同,UDP 不是可靠的协议,而是提供数据报服务。其他协议对 IP 的这种使用意味着,当接收到 IP 数据包时,接收 IP 层必须知道将此 IP 数据包中包含的数据提供给哪个上层协议。为了方便这一点,每个 IP 数据包标头都有一个字节,其中包含协议标识符。当 TCP 要求 IP 层传输 IP 数据包时,该 IP 数据包的标头声明它包含 TCP 数据包。接收 IP 层使用该协议标识符来决定将接收到的数据传递给哪一层,在本例中为 TCP 层。当应用程序通过 TCP/IP 通信时,它们不仅必须指定目标的 IP 地址,还必须指定应用程序的端口地址。端口地址唯一标识一个应用程序,并且标准网络应用程序使用标准端口地址;例如,Web 服务器使用端口 80。这些注册的端口地址可以在/etc/services.
中看到。这种协议分层不会随着 TCP、UDP 和 IP 而停止。IP 协议层本身使用许多不同的物理介质将 IP 数据包传输到其他 IP 主机。这些介质本身可能会添加自己的协议标头。一个这样的例子是以太网层,但是 PPP 和 SLIP 也是其他例子。以太网网络允许许多主机同时连接到单根物理电缆。每个传输的以太网帧都可以被所有连接的主机看到,因此每个以太网设备都有一个唯一的地址。发送到该地址的任何以太网帧都将被寻址的主机接收,但会被连接到网络的所有其他主机忽略。这些唯一的地址在制造时内置于每个以太网设备中,并且通常保存在以太网卡上的 SROM2 中。以太网地址长 6 个字节,一个例子是08-00-2b-00-49-A4。某些以太网地址保留用于多播目的,并且发送到这些目标地址的以太网帧将被网络上的所有主机接收。由于以太网帧可以承载许多不同的协议(作为数据),因此与 IP 数据包一样,它们在其标头中包含协议标识符。这允许以太网层正确接收 IP 数据包并将其传递到 IP 层。
为了通过诸如以太网这样的多连接协议发送 IP 数据包,IP 层必须找到 IP 主机的以太网地址。这是因为 IP 地址仅仅是一个寻址概念,而以太网设备本身拥有自己的物理地址。另一方面,IP 地址可以由网络管理员随意分配和重新分配,但网络硬件仅响应带有自身物理地址或所有机器必须接收的特殊多播地址的以太网帧。Linux 使用地址解析协议(ARP)来允许机器将 IP 地址转换为真实的硬件地址,例如以太网地址。希望知道与 IP 地址关联的硬件地址的主机,通过将其发送到多播地址,将包含其希望转换为网络上所有节点的 IP 地址的 ARP 请求数据包。拥有该 IP 地址的目标主机,会回复一个包含其物理硬件地址的 ARP 答复。ARP 不仅仅限于以太网设备,它还可以解析其他物理介质的 IP 地址,例如 FDDI。那些无法进行 ARP 的网络设备会被标记,以防止 Linux 尝试进行 ARP。还有反向功能,反向 ARP 或 RARP,可以将物理网络地址转换为 IP 地址。网关使用此功能代表远程网络中的 IP 地址响应 ARP 请求。
就像网络协议本身一样,图 10.2 显示 Linux 将互联网协议地址族实现为一系列连接的软件层。BSD 套接字由仅关注 BSD 套接字的通用套接字管理软件支持。支持它的是 INET 套接字层,它管理基于 IP 的协议 TCP 和 UDP 的通信端点。UDP(用户数据报协议)是一种无连接协议,而 TCP(传输控制协议)是一种可靠的端到端协议。当 UDP 数据包传输时,Linux 既不知道也不关心它们是否安全到达目的地。TCP 数据包被编号,并且 TCP 连接的两端都确保正确接收传输的数据。IP 层包含实现互联网协议的代码。此代码将 IP 标头添加到传输的数据,并了解如何将传入的 IP 数据包路由到 TCP 或 UDP 层。在 IP 层下,支持 Linux 所有网络的是网络设备,例如 PPP 和以太网。网络设备并不总是代表物理设备;有些像环回设备一样纯粹是软件设备。与通过以下方式创建的标准 Linux 设备不同mknod命令,只有当底层软件找到并初始化它们时,网络设备才会出现。你只会看到/dev/eth0当你构建一个内核并在其中包含适当的以太网设备驱动程序时。ARP 协议位于 IP 层和支持地址 ARP 的协议之间。
这是一个通用接口,不仅支持各种形式的网络,而且还是进程间通信机制。一个套接字描述了通信链路的一端,两个通信进程将各自拥有一个套接字,用于描述它们之间通信链路的端点。套接字可以被认为是管道的一种特殊情况,但与管道不同,套接字对其可以包含的数据量没有限制。Linux 支持几种套接字类,这些类被称为地址族。这是因为每个类都有其自己的通信寻址方法。Linux 支持以下套接字地址族或域
UNIX | Unix 域套接字, |
INET | 互联网地址族支持通过以下方式进行通信 |
TCP/IP 协议 | |
AX25 | 业余无线电 X25 |
IPX | Novell IPX |
APPLETALK | Appletalk DDP |
X25 | X25 |
有几种套接字类型,它们代表支持连接的服务类型。并非所有地址族都支持所有类型的服务。Linux BSD 套接字支持多种套接字类型
使用套接字进行通信的进程使用客户端-服务器模型。服务器提供服务,客户端使用该服务。一个例子是 Web 服务器,它提供网页,以及 Web 客户端或浏览器,它读取这些页面。使用套接字的服务器首先创建一个套接字,然后将名称绑定到该套接字。此名称的格式取决于套接字的地址族,实际上,它是服务器的本地地址。套接字的名称或地址使用sockaddr数据结构指定。INET 套接字将绑定到它一个 IP 端口地址。注册的端口号可以在/etc/services; 中看到,例如,Web 服务器的端口号是 80。将地址绑定到套接字后,服务器会侦听传入的连接请求,指定绑定的地址。请求的发起者(客户端)创建一个套接字并在其上发出连接请求,指定服务器的目标地址。对于 INET 套接字,服务器的地址是其 IP 地址和端口号。这些传入的请求必须通过各种协议层,然后在服务器的侦听套接字上等待。一旦服务器收到传入的请求,它会接受或拒绝它。如果要接受传入的请求,服务器必须创建一个新的套接字来接受它。一旦套接字被用于侦听传入的连接请求,它就不能用于支持连接。建立连接后,两端都可以自由发送和接收数据。最后,当不再需要连接时,可以将其关闭。要小心,以确保正确处理传输中的数据包。
BSD 套接字上的操作的确切含义取决于其底层地址族。设置 TCP/IP 连接与设置业余无线电 X.25 连接非常不同。与虚拟文件系统一样,Linux 使用 BSD 套接字层抽象套接字接口,BSD 套接字层负责应用程序的 BSD 套接字接口,该接口又由独立的地址族特定软件支持。在内核初始化时,内置于内核中的地址族会向 BSD 套接字接口注册自身。稍后,当应用程序创建和使用 BSD 套接字时,将在 BSD 套接字及其支持的地址族之间建立关联。这种关联是通过交叉链接数据结构和地址族特定支持例程表建立的。例如,有一个地址族特定的套接字创建例程,当应用程序创建新套接字时,BSD 套接字接口会使用该例程。
配置内核时,许多地址族和协议都构建到协议向量中。每个都由其名称表示,例如“INET”及其初始化例程的地址。在引导时初始化套接字接口时,会调用每个协议的初始化例程。对于套接字地址族,这导致它们注册一组协议操作。这是一组例程,每个例程都执行特定于该地址族的特定操作。注册的协议操作保存在pops向量中,它是指向proto_ops数据结构的指针的向量。
proto_ops数据结构包含地址族类型和指向特定于特定地址族的套接字操作例程的一组指针。pops向量由地址族标识符索引,例如 Internet 地址族标识符 (AF_INET 为 2)。
INET 套接字层支持包含 TCP/IP 协议的互联网地址族。如上所述,这些协议是分层的,一个协议使用另一个协议的服务。Linux 的 TCP/IP 代码和数据结构反映了这种分层。它与 BSD 套接字层的接口是通过它在网络初始化期间向 BSD 套接字层注册的一组 Internet 地址族套接字操作。这些保存在pops向量中,以及其他注册的地址族。BSD 套接字层从注册的 INETproto_ops数据结构调用 INET 层套接字支持例程,以为其执行工作。例如,BSD 套接字创建请求将地址族作为 INET 将使用底层 INET 套接字创建函数。BSD 套接字层将socket数据结构,表示 BSD 套接字,传递给每个操作中的 INET 层。INET 套接字层使用其自己的数据结构,而不是使 BSDsocket混乱 TCP/IP 特定信息袜它链接到 BSDsocket数据结构。 这种链接可以在图 10.3中看到。 它将袜数据结构链接到 BSDsocket数据结构,使用数据指针,位于 BSDsocket中。 这意味着后续的 INET 套接字调用可以轻松地检索袜数据结构。袜数据结构的协议操作指针也在创建时设置,它取决于所请求的协议。 如果请求 TCP,那么袜数据结构的协议操作指针将指向 TCP 连接所需的 TCP 协议操作集。
用于创建新套接字的系统调用传递其地址族、套接字类型和协议的标识符。
首先,请求的地址族用于搜索pops向量以寻找匹配的地址族。 某个特定的地址族可能作为内核模块实现,在这种情况下,kerneld守护程序必须先加载该模块,然后才能继续。 分配一个新的socket数据结构来表示 BSD 套接字。 实际上,socket数据结构是 VFS 的物理组成部分inode数据结构,分配套接字实际上意味着分配 VFSinode。 除非你考虑到套接字的操作方式与普通文件完全相同,否则这似乎很奇怪。 由于所有文件都由 VFS 表示inode数据结构,为了支持文件操作,BSD 套接字也必须由 VFS 表示inode数据结构。
新创建的 BSDsocket数据结构包含指向地址族特定套接字例程的指针,该指针设置为从proto_ops向量中检索到的pops数据结构。 其类型设置为所请求的套接字类型; SOCK_STREAM、SOCK_DGRAM 等之一。 使用保存在proto_ops数据结构。
中地址调用地址族特定创建例程。从当前进程的fd向量中分配一个空闲的文件描述符,并初始化它指向的数据结构。 这包括将文件操作指针设置为指向 BSD 套接字接口支持的 BSD 套接字文件操作集。 任何未来的操作都将被定向到套接字接口,它将依次调用其地址族操作例程,将其传递给支持的地址族。
为了能够侦听传入的互联网连接请求,每个服务器必须创建一个 INET BSD 套接字并将其地址绑定到它。 绑定操作主要在 INET 套接字层中处理,并得到底层 TCP 和 UDP 协议层的支持。 绑定了地址的套接字不能用于任何其他通信。 这意味着socket的状态必须是TCP_CLOSE。sockaddr传递给绑定操作的参数包含要绑定的 IP 地址,以及可选的端口号。 通常,绑定的 IP 地址是被分配给支持 INET 地址族的网络设备,且其接口已启动并可使用的地址。 您可以使用ifconfig命令查看系统中当前处于活动状态的网络接口。 IP 地址也可以是全 1 或全 0 的 IP 广播地址。 这些是特殊的地址,意思是“发送给所有人”3。 如果机器充当透明代理或防火墙,IP 地址也可以指定为任何 IP 地址,但只有具有超级用户权限的进程才能绑定到任何 IP 地址。 绑定的 IP 地址保存在袜数据结构的recv_addr和saddr字段中。 这些字段分别用于哈希查找和作为发送 IP 地址。 端口号是可选的,如果未指定,则会向支持的网络请求一个空闲端口。 按照惯例,端口号小于 1024 的端口不能被没有超级用户权限的进程使用。 如果底层网络确实分配了端口号,它总是分配大于 1024 的端口号。
当底层网络设备接收到数据包时,它们必须被路由到正确的 INET 和 BSD 套接字,以便可以对其进行处理。 因此,UDP 和 TCP 维护哈希表,用于查找传入 IP 消息中的地址,并将它们定向到正确的socket/袜对。 TCP 是一种面向连接的协议,因此处理 TCP 数据包比处理 UDP 数据包涉及更多信息。
UDP 维护一个已分配 UDP 端口的哈希表,即udp_hash表。 它由指向袜数据结构的指针组成,这些指针通过基于端口号的哈希函数进行索引。 由于 UDP 哈希表比允许的端口号数量小得多(udp_hash仅为 128 或UDP_HTABLE_SIZE个条目),因此表中的某些条目指向使用每个袜的袜'snext指针链接在一起的
数据结构的链。袜数据结构添加到其哈希表中,它只是检查所请求的端口号当前是否正在使用。袜数据结构在 *listen* 操作期间添加到 TCP 的哈希表中。
审核说明 输入的路由呢?
只能在处于正确状态的 INET BSD 套接字上发出出站连接; 也就是说,一个尚未建立连接且未被用于侦听入站连接的套接字。 这意味着 BSDsocket数据结构必须处于状态SS_UNCONNECTED。 UDP 协议不在应用程序之间建立虚拟连接,发送的任何消息都是数据报,一次性消息,可能到达或无法到达其目的地。 但是,它确实支持 *connect* BSD 套接字操作。 在 UDP INET BSD 套接字上的连接操作只是设置远程应用程序的地址; 它的 IP 地址和 IP 端口号。 此外,它还设置路由表条目的缓存,以便在此 BSD 套接字上发送的 UDP 数据包不需要再次检查路由数据库(除非此路由无效)。 缓存的路由信息从 INETip_route_cache指针指向袜数据结构。 如果未给出寻址信息,则此缓存的路由和 IP 寻址信息将自动用于使用此 BSD 套接字发送的消息。 UDP 将袜的状态更改为TCP_ESTABLISHED.
。 对于 TCP BSD 套接字上的连接操作,TCP 必须构建一个包含连接信息的 TCP 消息,并将其发送到给定的 IP 目的地。 TCP 消息包含有关连接的信息、唯一的起始消息序列号、启动主机可以管理的最大大小的消息、传输和接收窗口大小等等。 在 TCP 中,所有消息都被编号,初始序列号用作第一个消息号。 Linux 选择一个相当随机的值来避免恶意协议攻击。 TCP 连接的一端发送并被另一端成功接收的每条消息都会被确认为已成功且未损坏地到达。 未确认的消息将被重新传输。 传输和接收窗口大小是可以存在的未发送确认的未完成消息的数量。 最大消息大小基于在请求的启动端使用的网络设备。 如果接收端的网络设备支持较小的最大消息大小,则连接将使用两者中的最小值。 发出出站 TCP 连接请求的应用程序现在必须等待来自目标应用程序的响应以接受或拒绝连接请求。 由于 TCP袜现在期望传入的消息,因此它被添加到tcp_listening_hash中,以便可以将传入的 TCP 消息定向到此袜数据结构。 TCP 还启动计时器,以便如果在目标应用程序没有响应请求的情况下,可以使出站连接请求超时。
对于 UDP 套接字,更改套接字的状态就足够了,但是 TCP 现在将套接字的袜数据结构添加到两个哈希表中,因为它现在处于活动状态。 它们是tcp_bound_hash表和tcp_listening_hash。 两者都通过基于 IP 端口号的哈希函数进行索引。
每当收到活动侦听套接字的传入 TCP 连接请求时,TCP 都会构建一个新的袜数据结构来表示它。 此袜数据结构将成为 TCP 连接的下半部分,当它最终被接受时。 它还会克隆传入的sk_buff,其中包含连接请求,并将其排队到侦听receive_queue的袜数据结构。 克隆sk_buff包含指向新创建的袜数据结构。
拥有多个网络协议层,每一层都使用另一层提供的服务,存在的问题之一是每个协议都需要在数据传输时向数据添加协议头和尾,并在处理接收到的数据时删除它们。这使得协议之间传递数据缓冲区变得困难,因为每一层都需要找到其特定的协议头和尾。一种解决方案是在每一层复制缓冲区,但这样效率很低。相反,Linux使用套接字缓冲区或sk_buffs在协议层和网络设备驱动程序之间传递数据。sk_buffs包含指针和长度字段,允许每个协议层通过标准函数或“方法”来操作应用程序数据。
图 10.4 显示了sk_buff数据结构;每个sk_buff都有一个与之关联的数据块。该sk_buff有四个数据指针,用于操作和管理套接字缓冲区的数据
有两个长度字段len和truesize,它们分别描述了当前协议数据包的长度和数据缓冲区的总大小。该sk_buff处理代码提供了用于向应用程序数据添加和删除协议头和尾的标准机制。这些安全地操作数据, tail和len中的sk_buff:
如果backlog队列增长过大,则接收到的sk_buff将被丢弃。网络底层半部被标记为准备运行,因为有工作要做。
当网络底层半部处理程序由调度程序运行时,它会处理任何等待传输的网络数据包,然后再处理backlog队列,确定将接收到的数据包传递到哪个协议层。sk_buff由于Linux网络层已初始化,因此每个协议通过将
packet_type数据结构添加到ptype_all列表或添加到ptype_base哈希表中来注册自身。该数据结构包含协议类型、指向网络设备的指针、指向协议的接收数据处理例程的指针以及指向列表或哈希链中下一个数据结构添加到数据结构的指针。该数据结构添加到链用于侦听从任何网络设备接收的所有数据包,通常不使用。该列表或添加到哈希表按协议标识符进行哈希处理,用于决定哪个协议应接收传入的网络数据包。网络底层半部将传入的哈希表中来注册自身。该的协议类型与任一表中一个或多个sk_buff条目进行匹配。该协议可能匹配多个条目,例如,当侦听所有网络流量时,在这种情况下,数据结构添加到将被克隆。该sk_buff传递给匹配协议的处理例程。sk_buff传递给匹配协议的处理例程。
sk_buff需要将数据包传递到网络设备才能进行传输。但首先,协议(例如IP)需要决定使用哪个网络设备。这取决于数据包的最佳路由。对于通过调制解调器连接到单个网络(例如通过PPP协议)的计算机,路由选择很容易。数据包应通过环回设备发送到本地主机,或发送到PPP调制解调器连接末端的网关。对于连接到以太网的计算机,选择更加困难,因为有许多计算机连接到网络。
对于每个传输的IP数据包,IP都使用路由表来解析目标IP地址的路由。在路由表中成功查找的每个IP目标都返回一个rtable
数据结构,描述要使用的路由。这包括要使用的源IP地址、网络device数据结构的地址,有时还包括预构建的硬件标头。此硬件标头是网络设备特定的,包含源和目标物理地址以及其他媒体特定信息。如果网络设备是以太网设备,则硬件标头如图 10.1 所示,并且源和目标地址将是物理以太网地址。硬件标头与路由一起缓存,因为它必须附加到在此路由上传输的每个IP数据包,并且构建它需要时间。硬件标头可能包含必须使用ARP协议解析的物理地址。在这种情况下,传出数据包会停滞,直到地址被解析。一旦地址被解析并构建了硬件标头,硬件标头就会被缓存,以便将来使用此接口发送的IP数据包不必进行ARP。
每个网络设备都有一个最大数据包大小,并且它无法传输或接收大于此大小的数据包。IP协议允许这样做,并将数据分成更小的单元,以适应网络设备可以处理的数据包大小。IP协议标头包括一个片段字段,该字段包含一个标志和片段偏移量。
当IP数据包准备好传输时,
IP会找到用于发送IP数据包的网络设备。该设备是从IP路由表中找到的。每个device都有一个字段描述其最大传输单元(以字节为单位),这就是mtu字段。如果设备的mtu小于等待传输的IP数据包的数据包大小,则必须将IP数据包分解为更小的(mtu大小)片段。每个片段由一个sk_buff表示;其IP标头被标记为显示它是一个片段以及此IP数据包包含的数据偏移量。最后一个数据包被标记为最后一个IP片段。如果在分片期间,IP无法分配一个sk_buff,则传输将失败。
接收IP片段比发送它们要困难一些,因为IP片段可以按任何顺序接收,并且必须在重新组装之前全部接收。每次收到IP数据包时,都会检查它是否为IP片段。第一次收到消息的片段时,IP会创建一个新的ipq数据结构,并将其链接到等待重组的IP片段的ipqueue列表中。当收到更多IP片段时,会找到正确的ipq数据结构,并创建一个新的ipfrag数据结构来描述此片段。每个ipq数据结构唯一地描述了一个分片的IP接收帧,包括其源和目标IP地址、上层协议标识符以及此IP帧的标识符。当收到所有片段后,它们将被组合成一个sk_buff,并传递到下一个协议层进行处理。每个ipq都包含一个计时器,每次收到有效片段时都会重新启动。如果此计时器到期,则ipq数据结构及其ipfrag将被拆除,并且该消息被假定为在传输过程中丢失。然后,由更高级别的协议重新传输消息。
它执行各种检查,以查看此设备是否需要硬件标头,如果需要,数据包的硬件标头是否需要重建。 Linux会缓存硬件标头,以避免频繁重建它们。 如果需要重建硬件标头,它会调用设备特定的硬件标头重建例程。 所有以太网设备都使用相同的通用标头重建例程,
该例程又使用ARP服务将目标IP地址转换为物理地址。
ARP协议本身非常简单,由两种消息类型组成:ARP请求和ARP响应。 ARP请求包含需要转换的IP地址,而响应(希望如此)包含已转换的IP地址,即硬件地址。 ARP请求会广播到连接到网络的所有主机,因此,对于以太网网络,连接到以太网的所有机器都会看到ARP请求。 请求中拥有IP地址的机器将使用包含其自身物理地址的ARP响应来响应ARP请求。
Linux中的ARP协议层围绕着一个arp_table数据结构构建,每个数据结构描述了一个IP到物理地址的转换。 这些条目是在需要转换IP地址时创建的,并在它们随着时间的推移而变得陈旧时删除。 每个arp_table数据结构具有以下字段:
上次使用时间 | 上次使用此ARP条目的时间, |
上次更新时间 | 上次更新此ARP条目的时间, |
标志 | 这些描述了此条目的状态,例如它是否完整等等, |
IP地址 | 此条目描述的IP地址 |
硬件地址 | 已转换的硬件地址 |
硬件标头 | 这是指向缓存的硬件标头的指针, |
计时器 | 这是一个timer_list用于对未收到响应的ARP请求进行超时的条目, |
即未收到响应的 ARP 请求的时间。 | |
重试次数 | 此ARP请求已 |
重试的次数, | |
sk_buff队列 | 等待sk_buff此IP地址的解析的条目的列表 |
要被解析 |
ARP表由一个指针表(arp_tables向量)组成,这些指针指向arp_table条目的链。 条目被缓存以加快对其的访问速度。通过获取其IP地址的最后两个字节来生成到表中的索引,然后跟随条目链直到找到正确的条目来找到每个条目。 Linux还在arp_table条目中缓存预构建的硬件标头,形式为hh_cache数据结构的指针的向量。
当请求IP地址转换并且没有相应的arp_table条目时,ARP必须发送ARP请求消息。 它在表中创建一个新的arp_table条目,并将sk_buff包含需要地址转换的网络数据包的数据包排队到新条目的sk_buff队列中。 它发送一个ARP请求并启动ARP过期计时器。 如果没有响应,则ARP将重试该请求多次,如果仍然没有响应,则ARP将删除该arp_table条目。 任何sk_buff等待IP地址被转换的数据结构将被通知,并且由传输它们的协议层来处理此失败。 UDP不在乎丢失的数据包,但是TCP将尝试在已建立的TCP链接上重新传输。 如果IP地址的所有者使用其硬件地址进行响应,则该arp_table条目将被标记为完成,并且任何排队的sk_buff将被从队列中删除,并将继续传输。 硬件地址被写入每个sk_buff.
的硬件标头中。ARP协议层还必须响应指定其IP地址的ARP请求。 它注册其协议类型(ETH_P_ARP数据结构添加到),生成一个device数据结构。
数据结构。 这意味着它将传递由网络设备接收到的所有ARP数据包。 除了ARP响应之外,这还包括ARP请求。 它使用保留在接收设备的arp_table中的硬件地址生成ARP响应。网络拓扑会随着时间的推移而发生变化,并且IP地址可以重新分配给不同的硬件地址。 例如,某些拨号服务在建立每个连接时分配一个IP地址。 为了使ARP表包含最新的条目,ARP会运行一个定期计时器,该计时器会检查所有arp_table条目,以查看哪些条目已超时。 它非常小心,不要删除包含一个或多个缓存的硬件标头的条目。 删除这些条目很危险,因为其他数据结构依赖于它们。 一些arp_table条目是永久性的,这些条目被标记为不会被解除分配。 不允许ARP表增长过大;每个
IP路由功能确定将目标IP地址为特定IP地址的IP数据包发送到何处。 在传输IP数据包时,有很多选择要做。 目标是否可以到达? 如果可以到达,应该使用哪个网络设备来传输它? 如果有多个可以用于到达目标地的网络设备,哪个更好? IP路由数据库维护提供这些问题答案的信息。 有两个数据库,最重要的是转发信息数据库。 这是已知IP目标及其最佳路由的详尽列表。 一个更小,更快的数据库,路由缓存用于快速查找IP目标的路由。 像所有缓存一样,它必须仅包含频繁访问的路由;它的内容来自转发信息数据库。
路由通过对BSD套接字接口的IOCTL请求添加和删除。 这些被传递到协议进行处理。 INET协议层只允许具有超级用户权限的进程添加和删除IP路由。 这些路由可以是固定的,也可以是动态的,并且会随着时间的推移而发生变化。 大多数系统使用固定路由,除非它们本身是路由器。 路由器运行路由协议,这些协议不断检查到所有已知IP目标的路由的可用性。 不是路由器的系统被称为终端系统。 路由协议被实现为守护进程,例如GATED,它们也通过IOCTL BSD套接字接口添加和删除路由。
。 如果最近未使用该路由,则会从路由缓存中丢弃该路由。 如果路由保留在路由缓存中,则对其进行排序,以使最常用的条目位于哈希链的前面。 这意味着在查找路由时可以更快地找到它们。
转发信息数据库(如图 10.5所示)包含IP目前对该系统可用的路由的视图。它是一个相当复杂的数据结构,尽管它的排列相当有效,但并不是一个快速的数据库来进行查询。特别是,为每个传输的IP数据包在此数据库中查找目标会非常慢。这就是存在路由缓存的原因:使用已知的良好路由来加速IP数据包的传输。路由缓存来自转发数据库,并表示其常用条目。
每个IP子网都由一个fib_zone数据结构表示。所有这些都从fib_zones哈希表中指向。哈希索引来自IP子网掩码。到同一子网的所有路由都由fib_node和fib_info数据结构对描述,这些数据结构对被排队到每个fz_list数据结构的fib_zone中。如果此子网中的路由数量变得很大,则会生成一个哈希表,以使查找fib_node数据结构更容易。
可能存在到同一IP子网的多个路由,并且这些路由可以通过多个网关之一。IP路由层不允许使用同一网关到子网的多个路由。换句话说,如果存在到子网的多个路由,则保证每个路由都使用不同的网关。与每个路由关联的是其度量。这是衡量此路由优劣的指标。路由的度量本质上是必须跨越才能到达目标子网的IP子网的数量。度量越高,路由越差。
1 国家科学基金会
2 同步只读存储器
3 duh? 用途是什么?