【翻译】邻居子系统:概念
本章描述了邻居协议使用的原因和时间,以及其主要功能。这主要是邻居协议(如arp)的一个简单的概观以及快速参考。包含了如下的常见问题:
l 通用邻居框架具有什么功能
l 高速缓存有何重要意义
l 缓存中的邻居条目能够获得的状态
l 可达探测和网络不可达探测
l 为什么使用代理
Linux内核源码中使用的邻居术语来源于RFC2461和“邻居协议”小节中描述的IPv6邻居探
测模型,但是我们的讨论尽可能协议独立。
通常,L2地址、二层地址、硬件地址、MAC地址和链路层地址都指相同的概念。本章,我们将主要使用第一种说法。
26.1邻居是什么?
如果主机连接到和你同样的局域网,则它是你的邻居(也就是说,你和它直接通过共享介质或点对点链路连接),你们都被配置为同样的L3层网络。例如,在IP网络中,如果两台主机连接到同样的局域网,且每个主机至少有一个接口在同样的IP子网(译者注:对多网卡主机而言),则它们是邻居。这样的两台主机可以通过连接到它们的介质(如以太网)关联的协议直接通信。另外一种定义邻居的做法是离主机只有一个L3跳(hop)的主机都是邻居。它的L3层(网络层)路由表必须提供一个和其邻居直接通信的方法。不是邻居的主机必须通过网关或路由器通信。
如果两个主机由工作在L2层的系统(如网桥)分隔,则它们仍然是邻居。关于这点第四部分已经详细介绍了,这里我们看些基于IP网络的简单例子,如图26-1所示:
图26-1 邻居及非邻居主机
图26-1中每个拓扑图都表明了L2和L3层地址之间的不同的关系,这两种地址对可达的邻居而言有所联系。
图26-1(a)
主机a和主机b属于同样的 10.0.1 .0/24IP子网,所以它们能够直接通信,它们之间在L3层仅有一跳距离,所以它们是邻居。
图26-1(b)
此图展示的稍微复杂一点的情况,主机a和主机b仍然属于同一子网,所以相互间能够之间通信;另一方面,主机a和主机c属于不同的IP子网,因此它们需要通过路由器(假设做了适当的配置)来互相通信。这种情况,我们认为主机a和主机c之间在L3层有两跳的距离。
图26-1(c)
此图展示了两个主机连接在同一个集线器上但不能相互通信的情况。即使每台主机都可以接收到其它任何主机传来的数据,它们也不能够在L3层通信,因为它们配置了不同的ip子网。这样,主机a认为它只能够到达子网 10.0.1 .0/24内的主机,如果目标地址在子网之外,它不会朝目标主机发送任何数据。这个问题有很多方法可以解决,我们在接下来的章节中就会看到。
图26-1(d)
此图表明 10.0.1 .0/24子网实际由两个局域网通过集线器或网桥组成的一个子网的情况,我们在第14章就看到它们的不同之处,在本章中,我们认为这是同一种情况。注意,连接两个局域网的的两个接口(译者注:网桥)没有ip地址:这是因为所有的这三种设备类型必然是工作在IP层之下。
当两台主机在L3层相距一跳距离,实际上它们在L2层也是一跳的距离,如图26-1(a)、(b)、(c)所示。但并非总是这样的,如图26-1(d)所示,主机a和路由器在L3层相距一跳(所以是邻居)但在L2层却相距两跳。
此外,物理子网(局域网)和逻辑子网(IP子网)并非总是一一对应的,如图26-2(a)所示:在一个局域网中可以有多个IP子网或者在一个IP子网中有多个局域网。比如,图26-1(a)展示了同一个局域网中的两个IP子网,图26-1(d)表明同一IP子网(左边)中的两个局域网通过集线器相连,前者并不常见,后者常用于配置ARP代理或网桥时。我们可以在第28章的“最终公共处理”小节看到ARP代理配置的例子,在第五部分看到网桥的例子。
图26-2(b)表明:两组主机通过配置捆绑在不同的ip子网中,即使这两个组中的主机共享同一个局域网,并因此能够相互通信,但是它们必须通过监听两边请求的路由器。路由器必须具有两个不同的网络接口或双网卡,或者具有单网卡多IP地址。后者比较罕见,常用于设备地址临时短缺或配置失败时,例如,在图26-2(a)的场景中LAN1配置失败的情况下,我们可以把LAN1的主机移到LAN2(包括路由器的eth0接口[*]),则LAN1的主机的IP子网配置不需要任何改变即可再次工作。已经在LAN2的主机仍然可以通过路由器访问其它主机。即使这种情况不常见,但内核必须能够正确处理它,这种情况,特别是当路由器使用单个接口访问其子网时(也就是eth0被移除,则它的地址被加进eth1)的情景将在第28章的“可调节的ARP选项”小节中描述。
[*]一个可选的方法就是简单的把一个ip地址加到路由器下面的接口而不是移走路由器上面的接口
图26-2 (a) IP子网_LAN 1:1 (b) IP子网_LAN n:1
在接下来的章节中,我们不会明确提到图26-2(b)的情况,但你应该记得象这样的配置是可能的,不是非法的。
26.2 需要邻居协议的原因
本节中,我们会明白邻居子系统存在的基本原因,它将基本网络划分成层以及共享介质实体,如以太网。
26.2.1 当L3层地址需要转换为L2层地址时
L3:layer three 网络层
L2:layer second 数据链路层
在网络二层(以太网、802.11无限网、令牌网、点对点连接等等)和三层(IP层、私有的)协议之间存在差异的原因就是许多不同的L2层协议存在且在邻居之间传送数据,而L3层路又不需担心所用的传输介质,更高的层能够使用相同的软件在两个系统间传送报文而不管它在以太网上还是在点对点连接中。
图26-3表明了通过邻居子系统获得不同响应的不同情况
图26-3 点对点连接与共享介质
图26-3(a) 表明了点对点连接,如电话线。其L2层协议相当简单,如果运行在半双工介质上它仅处理错误校验和轮询等问题。其邻居协议比较简单,因为它仅仅只是使用L2层协议,邻居没有发送包给谁的决定权。
图26-3(b) 展示了更复杂的情况:以太网或其它共享介质通过广播来通信。如果主机a要发送数据给主机b,它必须把数据放到电缆上(或无线环境的电波中),并让共享介质上的所有系统都收到数据。它也必须指定一个L2层地址以标识数据使接收主机知道数据是发送给自己的,其它主机检测地址并丢弃数据。由邻居协议来选择包中L3层地址对应的L2层地址。
如果主机a和主机b通过网桥分开,则桥接收到L2层地址并直接传递给正确主机[*];而邻居子系统并不考虑这些情况,事实上,桥对于邻居子系统是透明的。
[*]我们在第四部分看到了桥和交换机管理并直接把数据包尽可能传递给正确的主机,用这种方式减少局域网中主机的无用的帧传输。
通常在L3层地址及其对应的L2层帧之间有一一对应的关系,具有多个L3层地址的系统(通常是路由器)提供多个接口以保持L3层地址及其对应的L2层帧之间有一一对应的关系。但是后面“特殊情况”小节也解释了,在L3层的多个多点传输地址能够映射到同样的L2层地址上,也有可能一个接口配置多个IP地址。
26.2.2 共享介质
在共享介质中,主机传输的任何帧都被直连在介质上的主机收到,无线连接就是一个简单的例子,另外一个例子就是用于以太网10-base2的共享的同轴电缆。
因此,用于共享介质的链路层协议需要定义一个地址策略以便发送方能够指定每帧的接受方,并且接受方也能够识别发送方。这个地址策略通常也定义一些特殊的地址以便对一个帧编址传给多个主机或所有的主机:即多播地址和广播地址。
由于多播主机需要传输数据因此也同时需要使用共享介质,链路层协议也必须包含一种方法以确保所有连接到共享介质的主机能够检测到这种冲突情况。以太网使用的是叫做带冲突检测的载波侦听多路访问协议(CSMA/CD),我们不关心这种冲突如何处理,因为这偏离了本章的主题。所有这些以太网相关的信息都可以在“以太网:权威指南”一书(O'Reilly出版社)中获取。
另一方面,点对点介质,如串行线,被设计仅用于两个节点间通信,在这种情况下,不需使用链路层地址标识源节点和目的节点,两个节点能够依靠同一条线或各自拥有一根线半双工或全双工通信,这两种情况都不需要冲突检测机制。两端要麽各分配一根线(全双工),要麽每端使用一种机制获取共享介质的使用权。因此,两台主机通过点对点介质相连时不需要邻居协议。
以太网最先是被设计工作在共享介质上,允许主机共享同样的介质并通过CSMA/CD处理冲突,这是共享同轴电缆时期(也就是10Base-2)。但是,由于各种原因,随着时间的过去,使用共享同轴电缆被使用双绞线(UTP)或RJ-45线代替。后台允许以太网接口配置成半双工或全双工模式,因为双绞线有足够的电缆允许双方同时通信,全双工模式的以太网仅用于两个以太网接口间的点对点连接。在这种情况下,每端都指派一根线进行传输和接收,因此不需要CSMA/CD。
现在,以太局域网主要通过交换机[*]实现,每台主机都通过双绞线连接到交换机。在此情况下,我们可以配置接口为半双工模式,这样CSMA/CD主要用于处理交换机端口与主机以太网卡适配器之间的冲突;或者配置两个接口为全双工模式,并允许交换机和主机同时传输数据,此时两端都必须用同样的双工模式。在绝大多数情况下,都不必明确配置连接双方的双工模式,因为有双工模式检测机制做了这件事。
[*]本书中,桥和交换机用于指代同样类型的设备,更多细节请参考第四部分
注意,主机生成的帧决不会被编址到交换设备(虽然通用规则也有例外);交换设备只是用于主机到达其它连在相同交换机上的其它主机。所以,即使在全双工模式的接口不需要CSMA/CD,我们仍然需要源地址和目的地址及邻居协议。这也意味着共享介质,如同轴电缆提供多播和广播功能由交换机以其它方式提供了:当交换机收到一个帧编址是多播或广播的链路层地址,它会把数据拷贝给除接收到此帧端口以外的所有其它端口,我们在第四部分看到,交换机这样做是很迅速的。
我们所知的当前的局域网大体上都是由以太网交换设备实现,主机都是通过点对点形式连接(UTP双绞线)到交换设备上。在新的以太网标准的设计中,CSMA/CD的用途变得不那么重要了。也由于这个原因,新的以太网标准设计用于高速数据传输,它要麽将CSMA/CD作为可选项,要麽完全摈弃它。
表26-1 展示了以太网支持CSMA/CD的情况,注意,G比特以太网依然支持CSMA/CD,虽然它主要用于全双工的点对点连接。 10G 比特标准化主要用于广域网(与局域网相对),它根本就不支持CSMA/CD,仅用在光纤介质上的点对点连接。表26-1的每项实际上都有许多变量属性,但由于它们不在我们的讨论范围,我也就没有列出它们。
表26-1 以太网类别和点对点/共享介质功能
以太网类别 | 点对点功能 | 共享介质功能(即支持CSMA/CD) |
以太网(10 Mbit/s) |
| X |
快速以太网(100 Mbits/s) |
| X |
G比特以太网 |
| X |
10G 比特以太网 | X |
|
26.2.3 为什么静态指定地址不够用
我们在第13章已经看到L3层和L2层地址及协议的功能,L3层地址如IP地址是逻辑的,所以只要是有效的地址都能够指派给任何接口;另一反面,L2层地址绑定到网卡,并且不支持可配置:它们由厂家被指派给网卡接口并且是全球唯一。但是,有些网卡接口可以通过ifconfig等常见工具强制配置L2层地址,这在处理本地IEEE地址时非常有用,第13章中有此描述。不过,当你把网卡接口地址改成你不拥有的L2层地址时,这样你会有一定的风险:你不再能够确定这个地址是否唯一以及以此L2层地址标识的网卡在共享介质上是否能够正常工作。通常只有比较资深的管理员才这样做特殊的配置,比如虚拟服务器或高可用设备。
因为L3层地址是逻辑的,所有有很多理由去改变它,下面是一些改变L3层地址的情形,这还需要在L3层地址和关联的L2层地址间建立映射。
l 动态配置:
在IP网络中,主机可以通过某种协议如DHCP指定动态IP地址,同样的主机可以在它每次请
求时获得不同的ip地址。但是硬件地址被硬编码进以太网卡或无线网卡,所以L3层到L2层地址的映射也要做响应的更新。
l 替换有问题的接口:
一旦网卡被替换,则L2层地址也改变了,但是管理员可能更希望保持网络逻辑地址即L3层地
址不变。
l 移动L3层地址:
一台服务器宕机时可能需要另外一台不同的服务器来处理相同的通信量请求,这就意味着旧的
L3层地址需要关联到新的服务器和网络接口。如果管理员需要保留同样的L3层地址在同一主机但是不同的接口,也需要做这样的改变。
需要屏蔽L2层和L3层之间的这种变化,因为有许多没有处理的不可预料的事以及棘手的事情要做,所以协议需要关联L2层和L3层之间的这种关联。这就是本书邻居协议部分需要讨论的事情。
26.2.4 特殊情况
有时,不需要任何协议把L3层地址解析成L2层地址,下面就是这种情况:
l 在点对点介质上数据仅仅只可以发送给一个主机,如拨号连接或通过电缆临时连接系统到另外一各管理员想监控的系统。此时,所有的L2层都不需要地址策略。(即使点对点介质在某种情况下使用了L2层地址也不需要)
l 可能用一种简单的公司也可以获得与L2层地址关联的特殊的L3层地址。因为没有不明确的或动态的分配,所有不需要任何协议。
l 多播地址不需要任何协议就可以静态传递。在IPv4/ARP网络中,当设备是以太网卡时,多播地址通过函数arp_mc_map来解析,这个函数轮流调用更为简单的ip_eth_mc_map函数。在ip_eth_mc_map中不使用任何协议而通过一个公式实现映射。图26-4就是插图和解释:
² 最高的24位被指派为IANA分配的静态值01:00:5E
² 23位(低24位的最高位)被设置为0
² 低23位是从IP地址的低23位拷贝过来
注意,同样的以太网多播地址能够被指派给IP地址(因为IP地址的高9位没用)
图26-4 根据IPv4多播地址生成以太网多播地址
l 广播地址(IP子网广播)静态解析成链路层广播地址(以太网是FF:FF:FF:FF:FF:FF)。如果有必要,每个设备的L2层广播地址也可以被明确配置。
26.2.5 诱发请求和响应
当L3层到L2层地址不能够通过前面章节描述的来静态解析,则邻居协议需要做此映射。不同的协议可能使用不同的机制,但是对所有的协议而言,熟悉下面的术语是很有用的,这些术语我们在本章将频繁的使用:
l 诱发请求(也叫邻居请求):
这导致网络中的包传输,包询问网络中的所有主机是否知道和被给L3层地址相关联的L2层地
址。这个请求根据协议或环境能够作为单播、多播或广播。
l 诱发响应(也叫邻居广告):
即包通常作为诱发请求的响应而发送,但是此包也可以单独生成(例子可参考第28章的“免费ARP”)。在通常情况下,和目标L3层地址相关联的主机生成响应包单也有可能是在此位置的另外一台主机去响应(参考“代理邻居协议”小节)。这种包通常作为单播发送,单在某些特殊情况下也可作为广播发送。
26.3 linux实现
早期的linux内核让L3层协议直接调用邻居协议提供的函数,因此,IPv4子系统直接和ARP代码交互。在最近的内核版本中,开发者总结了各种不同协议的各种请求,并把它们抽象成新的一个层,叫做邻居框架。
由于内核仍然包含了许多未更新至最新协议独立层的旧代码,所以我们仍然可以看到直接调用不推荐的ARP代码的现象(如arp_find),但是这只是例外。第27章的“L3层协议与邻居协议之间的通用接口”小节将详细介绍邻居体系结构的接口。
图26-5展示了linux邻居子系统的关键部分,以及与其交互的内核其它系统。L3层协议通过一个普通的接口与邻居层交互,此接口根据L3层协议来选择正确的邻居协议(如ARP、ND等),因此也被称作服务[*]。
[*]这张图并不包含DECnet 和ATM,因为它们不在本书讨论范围
包在传输时,会经历下面的几个环节:
1、本地主机的路由子系统选择目标L3层地址(下一跳)
图26-5 重要的图片
2、根据路由表,如果下一跳在同一网络(如果是这样,则下一跳是邻居),则邻居系统将目标L3层地址解析成L2层地址,这种关联关系被缓存起来龚以后使用。这样,如果一个应用在很短的时间内发送若干个包给另外的应用,则邻居协议仅在发送第一个包时被使用一次。
3、最后,某个函数如dev_queue_xmit(11章已描述)负责具体的传输,它将包传给流量控制与服务质量(QoS)层。虽然图26-5只是展示了dev_queue_xmit函数,但是邻居层实际上还可以调用其它函数(绝大部分是dev_queue_xmit函数的封装),这点我们在本章的后面会看到。
注意,dev_queue_xmit函数仅在要传输的包准备发送时才被调用。因此,如果需要L2层协议头,邻居协议必须在调用此函数之前加入L2层协议头。有些类型如点到点传输连接、广播或多播都不需要L2层协议头,因此它们不需要L3层到L2层的映射。这些传输在“特殊情况”小节中有描述;其它类型的传输由于采用共享介质所以需要L2层协议头,这个协议头要麽来自于邻居子系统缓存要麽通过邻居子系统向网络发送一个请求(获得)。
26.3.1 邻居协议
现在,IP网络中正在使用两个协议:大量系统使用的IPv4中的ARP和主要为IPv6开发的用于通用目的的ND(Neighbor Discovery邻居探测)协议。还有其它一些协议在linux内核中实现以用于私有网络,如:DECnet实现的协议,由于其使用有限所有本书不去关心它。
虽然ARP被认为是L3层协议,但是它实现的功能被IPv6的设计者移至L4层。如图26-6所示,ND协议被划为ICMPv6(IPv6 implementation of the Internet Control Message Protocol (ICMP):ICMP的IPv6的实现)的一部分,这样做是基于IPv4多年的运营经验,这样提供给ND许多便利,其中之一就是有机会充分利用L3层的特点,如IPSec加密。28章的“建立在ARP(IPv4)上的ND(IPv6)改进”一节中给出了在ND和ARP之间主要不同点的概观。
图26-6 网络堆栈中ARP/ND协议所处的位置
如前所述,linux也提供了一个通用的体系框架以简化上层服务的代码复制,这些服务都极其类似的使用了邻居协议。通常邻居协议提供的服务能够被不同的协议裁剪以适应其各自的需要,下面列出了连接体系结构提供给各协议的一些服务:
l 用于存储L 3 ß à L 2层转换结果的各协议缓存区
l 用于在缓存区中增加、删除、修改、查找特定的转换条目的函数。因为查找函数会极大的影响系统的性能,所以它必须快。
l 协议缓冲区条目的失效(或老化)机制
l 当缓存区满,但又有新的请求需要在缓存中创建一个新的条目时所采用的决策策略
l 各协议对应的邻居请求队列。当包准备好了将被发送,而缓存中又没有其L2层地址时,则包必须被放到包缓冲区,直到诱发请求包发送并收到响应时。可参考27章的“队列”一节
为了使每个协议都能够定制邻居子系统的行为,它定义了一组占位符和和虚函数以使每个协议
替换为它应该使用的函数。这于绝大多数linux内核允许用户定制的原理类似。邻居层也提供了一组可调整参数用于用户命令、/proc文件系统或协议自身的配置。最后,访问缓存区的函数通常是面向所有的协议,但是不同的协议可以使用不同大小的关键字(地址)。所有,邻居协议体系结构提供了一个通用的方法定义使用什么类型的关键字,后面的章节会降到这些细节要点。
每个协议都能够被使用与独立配置,27章的“协议初始化和清除一节”展示了邻居协议如何在内核中注册或注销自己。
26.4 代理邻居协议
当一个主机截获发到另外的主机的数据包并代替后者处理数据包时,它实际上扮演着代理的角色。当然,这个术语也包含了发动中间人(man-in-the-middle)攻击的恶意主机.常见的一个代理的例子就是http服务缓冲系统,它通过中途拦截请求截获数据包并重定向到其它web服务器 ,同时它会缓存来自于这些web服务器的网页并提供给请求者。
如果主机和应用不需要做明确的配置去标识代理提供的服务功能,则这种代理被称作透明的,前面提到的http服务缓冲系统就是透明代理的一个例子。但是,如图26-7所示,服务既可以通过透明代理提供也可通过非透明代理提供。图片展示了两个使用http代理的例子:
l (a)代理被安装在本地网路的路由器上以访问internet,所有网络上的主机的浏览请求都通过路由器,因此管理员可以把路由器配置成拦截模式以代理所有的http请求。这种情况被视作透明代理,因为在主机B上不需要任何的配置或特殊编程的浏览器。
l (b)主机B上的浏览器被设置成使用名叫proxy的主机作为代理去访问internet。主机proxy在必要时使用路由器(即当其缓存内容失效时)。
图26-7 (a)透明代理 (b)非透明代理
前面的例子展示了流行的代理类型:http或web代理,现在我们来看看本书这部分的代理。邻居协议的代理服务器是被配置成响应诱发请求的主机,这台主机没有相应的下层地址,且它用来代替实际拥有这些地址的其它主机。由于有代理,不同局域网的主机可以相互自由的交互,就像在同一个局域网中。
例如:ARP代理通常用在IPv4网络中协助底层到子网的传输。由于代理是透明的,所以主机并不需要特殊的协议或配置。但是如果代理服务器宕机了,则被代理的主机之间的联通也中断了,这可以通过提供多个代理服务器来避免。在这种情况下,主机会收到它(所广播)的请求的多个诱发响应。通过选择最先到达的响应,主机可以获得最快且负载最小的代理服务器(的服务)。
代理的使用也可以使依赖代理的主机简化配置。28章的“代理与路由”提供了这样的一个例子。
Linux内核实现的邻居协议中,仅IPv4和IPv6能够使用其代理功能。这个通用的体系架构被这两个协议共享,并根据需要裁剪代理功能。两者之间的不同之处在28章的“建立在ARP(IPv4)上的ND(IPv6)改进”小节中描述。
在27章的“充当代理角色”一节,我们将看到协议独立组件特性的实现细节(如定时器、队列等待)。在28章的“ARP代理”一节中,我们将会看到IPv4和ARP的特殊情况的一些细节。
26.4.1 代理请求条件
代理收到的诱发请求并非都被处理,如果碰到下述情况,则代理服务器会响应诱发请求一个地址:
l 地址和接收请求的代理所在网卡配置地址不属于同一子网。因为代理服务器代替其它主机响应诱发请求,这些主机和发送诱发请求的主机一般都不在同一个子网。否则,(如果在同一子网)目标主机也会和代理一样响应,这样,它也不清楚发送请求的主机会选择哪一个响应了。
l 代理特性开启时。这个约束听起来很直观,但事实并非如此。有几个标准制约着代理是否需要响应请求,这些在不同的邻居协议里面又有不同。在linux中,内核提供了通用和专用两种代理形式:
a> 基于设备
设备收到的所有请求都被处理。这是IPv4网络中使用最常见的情况,IPv6不用这种策略
b> 基于目标(地址)
在决定是否代理时,目标地址和设备都要被考虑。意即代理能够响应对特定IP地址的请求,基于目标的代理在IPv6网络中被标准化了,但是在IPv4网络中也可以使用。
图26-8 展示了两种代理之间的优先权,当主机收到本地子网之外地址的诱发请求时,如果主机的代理特性开启了,则它可以直接处理此请求。首先子系统会检测设备上的代理特性是否全局性开启;如果没有,则检测设备是否被配置成代理特殊地址。
图26-8 设备代理和地址代理间的优先权
l 代理服务器收到请求时,其转发功能是开启的。
因为代理服务器处在各个主机之间,它不得不在两个终端之间转发数据[*]。
[*]这并不是说通过在代理主机上开启代理功能,就可以自动开启转发功能,这两个功能是分开配置的,但是代理需要转发给核实的函数。
ARP诱发请求总是发送给L2层广播地址,这确保所有共享同一介质的所有主机都可以接收到。这样,代理能够截获对这些主机的地址请求,即使代理没有设置为混杂模式。当ARP进行可达确认(参考“可达确认”一节)时,它采用单播而不是多播。
ND用L3层多播地址来处理诱发请求和响应。当路由器要为一个被给的IP地址作代理时,它需要使用相关联的L3层多播地址。
26.5 诱发请求传递和处理过程
本节中,我们将看到基于接收主机以及网络物理拓扑配置的诱发请求是何时处理的,图26-9列出了促使主机发送诱发请求的因素,图26-10显示了决定收到诱发请求的linux主机是否处理它的常见的因素。为了展示接收者判断的可能的复杂性,图26-10假定接收者实现代理和网桥的功能[*];移走任何一个功能都会简化这张图。图26-10假定是基于设备的代理;基于目标的代理与此一致,故略去。注意图26-10展示了代理服务器和未实现代理的普通服务器两种情况:“代理开启”表示是代理服务器,“代理禁止”表示普通的服务器。
[*]我在图26-10中加了桥表示桥在邻居协议前处理,所以后面可能不会看到输入队列的诱发请求。桥的细节在第四部分已经描述。
图26-9 转发诱发请求
这是协议独立的分析,关于ARP更详细的描述放在第28章。
图26-10 处理准入的诱发请求
当网桥被使能启用时,诱发请求并不是被此主机处理,而是根据网桥配置被转发给正确的网络接口;即网桥的转发发生在正确的网络接口的邻居协议系统有机会处理进入的包之前。换句话说,如图所示,在linux内核中实现的代理处理诱发请求之前,网桥就已经处理过了,详情请参考第四部分。
假设网桥被禁止了,我们要记住,建立在共享介质基础上的主机依然能够收到属于其它主机的诱发地址请求。下面是几个影响linux主机是否响应进入的诱发请求的变量:
l 逻辑子网(如IP子网)
当请求地址和接收诱发请求的的网卡所配置的L3层地址属于同样的逻辑子网时(根据接收主机
的配置),即图26-10中的"Same logical subnet"就是这种情况。我们拿IPv4作为例子, 10.0.0 .1(请求的地址)和10.0.0.2/24(接收网卡配置的地址)属于同样的IP子网10.0.0.0/24
当两个主机属于同样的逻辑子网时,它们能够直接交互通信。否则需要路由器的协助。
注意,在同样的逻辑子网中,一个接口可能配置为拥有多个地址(其中之一时主地址,其它都是次地址); 在不同的逻辑子网中,接口也可能配置为拥有多个地址;或者上述两种情况的组合。如果接收网卡在不同的逻辑子网并被配置拥有都个地址,则请求地址必须属于某个子网。
l 物理子网(LAN)
当两台主机属于同一局域网时,原则上它们时可以直接通信的,但是实际上它们能否通信是由
L3层逻辑配置决定的。例如,图26-1(c)中,主机在同一局域网,但分属不同的ip子网。
每台主机都不会去解析属于不同子网的其它主机的地址,但是它要解析路由器的地址,因为路
由器是它需要通信以到达远端的主机。可以参考图26-9。
除非使用了代理,否则主机决不会在网卡接收请求其它网卡配置的L3层地址的诱发请求。因为图26-10所示的接收者情形中,在不同的逻辑子网下,我们并不能区别同样的物理子网和不同的物理子网,因为它们没有任何的不同,仅仅代理状态是比较重要的。
l 代理请求:
代理收到的诱发请求也并非都处理,细节请参考“代理请求条件”一节
第28章的“处理入队列的ARP包”一节展示了ARP协议如何处理图26-10中的各种情形。
26.6 邻居状态和网络不可到达检测(NUD)
图26-11简单概括了将包传递给L3层地址的时,内核所经历的步骤流程图。
图26-12是一个简单模型,展示了邻居协议所经历的各种状态。
图26-11和图26-12中的两个模型工作在各种情况,但linux内核采用更经典的模型处理各种可能的状态。下一节将扩展图26-12模型,后面的章节将集中在图26-11所示的细节。
正如我们看到的,管理邻居系统的一个很重要的部分就是查看是否可达。
图26 -11 L 3到L2层地址解析步骤
图26 -12 L 3层到L2层状态映射
26.6.1 可达性
根据邻居子系统的观点,可达性可以做如下相关类推。假设你和很多人包括我都在一个暗室里面,你说,每个人都到房间外面去,则每个人都会走出去,因为大家都能够听到你说话;但是如果你需要叫我出去,你需要提供某种信息,如:我的名字。
所以,发送给广播目的地址的诱发响应并不能够提供一些发送给单播目标地址的信息:任何人都可以接收广播包,但是如果你想和某个接收者交互,必须提供精确的地址。
邻居系统认为,如果内核能够验证接收者能够正确接收到以其单播地址编址的包,主机认为是可达的,反之亦然。换句话说,就是必须双向可达时内核才会认为邻居是可达的。本章接下来的部分我们用术语可达表示双向可达。我们会在“可达确认”小节中看到可达确认的两种方法:L4层确认和诱发响应。
26.6.2 在NUD状态间的传输
IPv6定义了NUM机制有助于快速判断邻居是否断开或宕机,linux内核在IPv4和IPv6的实现中采用同样的机制。类似的模块也会被本书没有讲述的其它协议使用,如:DECnet。
图26-13总结了邻居协议支持的状态以及触发状态改变的条件。有几种事件会导致创建邻居条目,包括转发数据报给邻居的请求、或者从邻居接收到诱发请求。
邻居条目的状态可以在其生命期改变多次,同一个状态可能多次出现在条目生命周期的不同阶段。不同的协议可能导致不同的状态迁移,有些特定条件下才出现的状态变迁未在图中显示,如:IPv4直接置最近创建的邻居条目的状态为:NUD_STALE,而IPv6却不这样做。
图26-13后面是这些状态的描述,可能的状态值都是基于某类常见的属性。每个状态下都有图中状态迁移的讨论,特别是NUD机制。
26.6.2.1 基本状态
图26-13的状态定义如下,我们从新建邻居条目时的默认状态开始:
NUD_NONE
表示邻居条目被创建,但目前尚不可用。
图26-13 NUD状态迁移
下面的状态来自于IPV6邻居定义,并在还在linux ARP/IPv4的实现中采用:
NUD_INCOMPLETE
诱发请求已经发送,但是尚未收到响应时所处的状态。在这个状态下,不可以使用任何硬件地址(not even an old one, as there is with NUD_STALE)
NUD_REACHABLE
邻居地址被缓存,并且最近已知是可达的(即以及验证是可达的)
NUD_FAILED
由于诱发请求失败而标记邻居是不可达状态。包括创建条目时生成的和由NUD_PROBE触发的诱发请求失败。
NUD_STALE
NUD_DELAY
NUD_PROBE
这几个是迁移的中间状态;当本地主机判断邻居是否可达的过程中设置这些状态。可以参考“可达确认”小结。
下面几个状态代表一组特殊的状态,通常一旦设置就不会改变了。
NUD_NOARP
这个状态用于标记邻居不需要任何协议来解析L3层到L2层的地址映射转换(参考“特殊情况”小节)。28章的“ARP构造函数”小节中描述了在IPv4/ARP中为什么设置这个状态的原因以及如何设置它。虽然这个状态的名称暗示它仅用于ARP,但实质上它使用于所有邻居协议。
NUD_PERMANENT
L2层邻居地址被静态配置时的状态置(如用户空间命令配置),所以不需要任何邻居协议考虑它。参考29章的“邻居的系统管理”一节。
26.6.2.2 衍生状态
除了前面小节所列出的基本状态,为了代码的清晰可读,当在某些情况下需要引用多个状态时,系统定义了下面的一些衍生状态:
l NUD_VALID
如果一个邻居条目的状态是下述几个状态中的一个,则可以认定它是NUD_VALID状态。这表明邻居确信现在有一个可用地址。
即:NUD_PERMANENT、NUD_NOARP、NUD_REACHABLE、NUD_PROBE、NUD_STALE、NUD_DELAY
l NUD_CONNECTED
这个状态是NUD_VALID的子集,表明没有未决的确认处理,即如下几个状态都表示处在NUD_CONNECTED状态:NUD_PERMANENT、NUD_NOARP、NUD_REACHABLE
l NUD_IN_TIMER
表示邻居子系统为邻居条目提供了一个正在运行的定时器,这发生在在邻居条目状态不确定时。符合这个状态的基本状态是:NUD_INCOMPLETE、NUD_DELAY、NUD_PROBE
我们来看一个为何衍生状态在内核代码中的作用的例子,当邻居实例被移出时,系统需要停止和这个数据结构相关联的所有未决的定时器,此时它不是将邻居状态与这三个状态相比较以判断与它们是否有相关联的未决定时器。,而是扮演清洁器的角色,定义一个NUD_IN_TIMER,并通过为操作符&来与邻居状态进行比较。
26.6.2.3初始状态
当邻居实例被创建时,其缺省的状态是NUD_NONE。但是当一个外部用户命令创建邻居实例时,它将直接设置其状态为NUD_NONE(参考29章)。
27章的“邻居初始化”一节解释说:协议的constructor方法也可以根据相关设备驱动(如点对点)的属性和L3层地址(如广播)来改变这个状态
26.6.3 可达确认
在“为什么静态指定地址不够用”一节中我们看到,L3层到L2层的地址映射可能会变化。因此,如果信息有一段时间未使用了,确认信息已经正常的存储在缓存中是很有意义的。这就称为可达确认。
根据“需要邻居协议的原因”一节中列出的原因,可达状态的变化就不是必要的了,路由器、网桥、或其它网络设备可能会面临某些问题。在进行可达确认的过程中,缓存中的信息被假定为有可能仍然有效而被临时使用。
支持可达确认工作的三个NUD状态是:NUD_STALE, NUD_DELAY, 和NUD_PROBE。使用这三个状态的主要原因是直到包需要被发送到相关邻居时才有必要启动可达确认处理。
我们下面再次详细说明下这三个NUD状态的精确的意思,并看一下确认映射的两种方法:
l NUD_STALE
缓存中有邻居的地址,但之后有一段时间没有被确认(参考29章的“neigh_parms结构”一节中关于reachable_time的讨论)。下次,一个包被发送给邻居,则会启动可达确认处理过程
l NUD_DELAY
这个状态与NUD_STALE类似,可以很好的减少诱发请求的发送次数。当邻居的关联条目处在NUD_STALE状态且又一个包到达时,就进入此状态。NUD_DELAY状态表示外部能够确认邻居是否可达的时间窗口。最简单的一种外部的确认是当邻居有疑问(译者注:指时间过期时)而发送一个包时,这样就表明它是正常的且可以访问。
这个状态有时是给网络上层一个可达确认,这样可以使内核减少发送诱发请求(的次数),从而节约带宽和减少CUP使用率。这个状态是一个小小的改进,但你考虑下大的网络环境,你可以想象它能够提供多大的便利啊。
如果没有受到确认,邻居条目将进入下一个状态:NUD_PROBE,它通过直接的诱发请求或协议提供的其它机制来解析邻居状态。
l NUD_PROBE
当邻居根据分配的时间余额而处在NUD_DELAY状态且没有收到可达验证包时,其状态被改变成NUD_PROBE且开始诱发请求处理
邻居的可达状态有两种主要的确认方式。下面我们会看到:这两种方式没有相同级别的认证,如下两种方式:
l 来自于单播诱发响应的确认:
当你的主机收到先前发送的诱发请求的诱发响应时,这意味着邻居收到了请求,并且能够发回响应;这也意味着:要么它有了你的L2层地址,要么它从你的请求消息包中知道了你的地址(参考27章的“创建邻居条目”小节);这同时也意味着在两个方向上都有工作路径。但是请注意,这仅在诱发响应是以单波包的形式发送是才是正确的。如果是收到广播响应,则邻居状态迁移到NUD_STALE而不是NUD_REACHABLE(你可以在28章“处理进入的ARP包”一节中从ARP的角度找到更多的细节)。
l 外部确认:
如果你的主机确信收到一个来自邻居的对先前发送的某个包的响应,它也可以认为邻居是可达
的。图26-14展示了一个例子:其中,当主机A的TCP层收到B响应它的SYN包的SYN/ACK包时确认主机B可达。注意B不是A的邻居,收到来自于主机B的SYN/ACK包将确认被A使用的下一跳网关到主机B的可达性。
图26-14 外部邻居可达确认的例子
确认工作由dst_confirm函数处理,它确认用于路由SYN包到主机B的路由表缓存条目的有效性。dst_confir是neigh_confirm函数的简单封装,它用于完成我们先前描述的工作:它确认邻居的可达和L3层到L2层的映射。注意,neigh_confirm只是更新neigh->confirmed的时间戳;实际上是neigh_periodic_timer函数更新邻居条目的状态到NUD_REACHABLE。[*]
[*]从L4层收到确认与设置到NUD_REACHABLE状态之间的延迟不会影响到流量。
注意图26-14中两个包的关联并不是在IP层完成的,因为后面并不知道数据流。这就是L4层需要考虑确认的原因。TCP的SYN/ACK包交互只是L4层协议提供外部确认的一个例子。如果给一个套结字、及其关联的路由缓存条目和下一跳网关,用户空间的应用能够根据传输中的MSG_CONFIRM选项调用send和sendmsg确认网关的可达性。
而收到诱发请求时可以将状态迁移到NUD_REACHABLE而不管当前是何状态,外部确认只能够用于当前状态是NUD_STALE的情况。这意味着如果邻居条目已被创建且状态为NUD_INCOMPLETE,外部确认不允许确认邻居的可达性(参考图26-13)。
注意:NUD_DELAY/NUD_PROBE 和NUD_NONE能够迁移到NUD_REACHABLE,如图26-13所示。但是,从NUD_NONE到NUD_REACHABLE,你需要完整的可达验证;而从NUD_DELAY/NUD_PROBE到NUD_REACHABLE,任何一种确认都足够了。