第4章 接口以太网(2)

2019-09-01 18:53

ether_input函数

函数ether_input显示在图4.13中,它检查结构ether_header来判断接收到的数据的类型并将接收到的分组加入到队列中等待处理。

图4.13 函数ether_input

广播和多播的识别

196-209 传给ether_input的参数有:ifp,一个指向接收此分组的接口的ifnet结构的指针;eh,一个指向接收分组的以太网首部的指针;m,一个指向接收分组的指针(不包括以太网首部)。

任何到达不工作接口的分组将被丢弃。可能没有为接口配置一个协议地址,或者可能被程序ifconfig(8)(6.6节)显式地将接口禁用了。

210-218 变量time是一个全局的timeval结构,内核用它维护当前时间和日期,它是从Unix新纪元(1970年1月1日00:00:00,协调通用时间[UTC])开始的秒和微秒数。在[Itano and Ramsey 1993]中可以找到对UTC的简要讨论。我们在Net/3源代码中会经常遇到结构timeval:

struct timeval {

long tv_sec; /* seconds */

long tv_usec; /* and microseconds */ };

ether_input用当前时间更新if_lastchange并且把if_ibytes加上输入分组的长度(分组长度加上14字节的以太网首部)。

然后,ether_input再次用leread去判断分组是否为一个广播或多播分组。

有些内核编译时可能没有包括BPF代码,因此测试必须在ether_input中进行。 链路层分用

219-227 ether_input根据以太网类型字段来跳转。对于一个IP分组,schednetisr调度一个IP软件中断并且选择IP输入队列,ipintrq。对于一个ARP分组,调度ARP软件中断并选择arpintrq。

一个isr是一个服务例程中断。

在原先的BSD版本,当处于网络中断级别时,ARP分组通过调用arpinput立即被处理。通过分组排队,它们可以在软件中断级别被处理。

如果要处理其他以太网类型,一个内核程序员应在此增加其他情况的处理。或者,一个进程能用BPF接收其他以太网类型。例如,在Net/3中,RARP服务通常用BPF实现。

228-307 默认情况处理不识别以太网类型或按802.3标准(例如OSI无连接传输)封装

的分组。以太网type字段和802.3 length字段在一个以太网帧中占用同一位置。两种封装能够分辨出来,因为一个以太网封装的类型范围和802.3封装的长度范围是不同的(图4.14)。我们跳过OSI代码,在[Stallings 1993]中有对OSI链路层协议的说明。

范围 0 – 1500 1501 – 65535 2048 2045

4—6

说明 IEEE 802.3 length字段 以太网type字段: IP分组 ARP分组 图4.14 以太网type和802.3 length字段

有很多其他以太网类型值分配给各种协议;我们没有在图4.14中显示。在RFC 1700 [Reynolds and Postel 1994]中有一个有更多通用类型的列表。

分组排队

308-315 最后,ether_input把分组放置到选择的队列中,若队列为空则丢弃此分组。我们在图7.23和21.16中会看到IP和ARP队列的默认限制为每个50个(ipqmaxlen)分组。

当ether_input返回时,设备驱动程序通知硬件它已准备接收下一分组,这时下一分组可能已存在于设备中。当schednetisr调度的软件中断发生时处理分组输入队列(1.12节)。准确地说,调用ipintr来处理IP输入队列中的分组,调用arpintr来处理ARP输入队列中的分组。

ether_output函数

我们现在查看以太网帧的输出,当一个网络层协议,如IP,调用此接口ifnet结构中指定的函数if_output开始处理输出。所有以太网设备的if_output是ether_output(图4.2)。ether_output用14字节以太网首部封装一个以太网帧的数据部分,并将它放置到接口的发送队列中。这个函数比较大,我们分4个部分来说明:

? 验证,

? 特定协议处理, ? 构造帧, ? 接口排队。

图4.15包括这个函数的第一个部分。

49-64 ether_output的参数有:ifp,它指向输出接口的ifnet结构;m0,要发送的分组;dst,分组的目的地址;rt0,路由信息。

65-67 在ether_output中多次调用宏senderr。

#define senderr(e) { error = (e); goto bad;}

senderr保存差错码并跳到函数的尾部bad,在那里分组被丢弃并且ether_output返回error。

如果接口启动并在运行,ether_output更新接口的上次更改时间。否则,返回ENETDOWN。 主机路由

68-74 rt0指向ip_output找到的路由项并传递给ether_output。如果从BPF调用ether_output,rt0可以为空,在这种情况控制转给图4.16中的代码。否则,验证路由。如果路由无效,参考路由表并且当路由不能被找到时返回EHOSTUNREACH。这时,rt0和rt指向一个到下一跳目的地的有效路由。

图4.15 函数ether_output:验证

网关路由

75-85 如果分组的下一跳是一个网关(而不是最终目的),找到一个到此网关的路由,并且rt指向它。如果不能发现一个网关路由,则返回EHOSTUNREACH。这时,rt指向下一跳目的地的路由。下一跳可能是一个网关或最终目的地址。 避免ARP泛洪

4—7

86-90 当目的方不准备响应ARP请求时,ARP代码设置标志RTF_REJECT来丢弃到达

目的方的分组。这描述在图21.24中。

ether_output根据此分组的目的地址继续处理。因为以太网设备仅响应以太网地址,要发送一个分组,ether_output必须发现下一跳目的地的IP地址所对应的以太网地址。ARP协议(21章)用来实现这个转换。图4.16显示了驱动程序是如何访问ARP协议的。

图4.16 函数ether_output:网络协议处理

IP输出

91-101 ether_output根据目的地址中的sa_family进行跳转。我们在图4.16中仅显示了case AF_INET、AF_ISO和AF_UNSPEC而略过了AF_ISO的代码。

case AF_INET调用arpresolve来决定与目的IP地址相对应的以太网地址。如果以太网地址已存在于ARP高速缓存中,则arpresolve返回1并且ether_output继续执行。否则,这个IP分组由ARP控制,并且ARP判断地址,从函数in_arpinput调用ether_output。

假设ARP高速缓存包含硬件地址,ether_output检查是否分组要广播并且接口是否是单向的(例如,它不能接收自己发送的分组)。如果都成立,则m_copy复制这个分组。在switch执行后,这个复制的分组同到达以太网接口的分组一样进行排队。这是广播定义的要求,发送主机必须接收这个分组的一个备份。

我们在第12章会看到多播分组可能会环回到输出接口而被接收。

显式以太网输出

142-146 有些协议,如ARP,需要显式地指定以太网目的地和类型。地址族类常量AF_UNSPEC知识:dst指向一个以太网首部。bcopy复制edst中的目的地址并把以太网类型设为type。它不必调用arpresolve(如AF_INET),因为以太网目的地址已由调用者显式地提供了。 未识别的地址族类

147-151 未识别的地址族类引起一个控制台消息并且ether_output返回EAFNOSUPPORT。

图4.17所示的是ether_output的下一部分:构造以太网帧。

图4.17 函数ether_output:构造以太网帧

以太网首部

152-167 如果在switch中的代码复制了这个分组,这个分组副本同在输出接口上接收到的分组一样通过调用looutput来处理。环回接口和looutput在5.4节讨论。 M_PREPEND确保在分组的前面有14字节的空间。

大多数协议要在mbuf链表的前面留一些空间,因此M_PREPEND仅需要调整一些指针(例如,16.7节中UDP输出的sosend和13.6节的igmp_sendreport)。

ether_output用type,edst和ac_enaddr(图3.26)构成以太网首部。ac_enaddr是与此输出接口关联的以太网单播地址并且是所有从此接口传输的帧的源地址。ether_header用ac_enaddr重写调用者可能在ether_header结构中指定的源地址。这使得伪造一个以太网帧的源地址变得更难。

这时,mbuf包含一个除32 bit CRC以外的完整以太网帧,CRC由以太网硬件在传输时计算。在图4.18所示的代码对设备要传送的帧进行排队。

4—8

图4.18 函数ether_output:输出排队

168-185 如果输出队列为空,ether_output丢弃此帧并返回ENOBUFS。如果输出队

列不为空,这个帧放置到接口的发送队列中,并且若接口未激活,接口的if_start函数传输下一帧。

186-190 宏senderr跳到bad,在这里帧被丢弃,并返回一个差错码。

lestart函数

函数lestart从接口输出队列中取出排队的帧并且交给LANCE以太网卡发送。如果设备空闲,调用此函数开始发送帧。ether_output(图4.18)的最后是一个例子,直接通过接口的if_start函数调用lestart。

如果设备忙,当它完成了当前帧的传输时产生一个中断。设备调用lestart来退队并传输下一帧。一旦开始,协议层不再用调用lestart来排队帧,因为驱动程序不断退队并传输帧直到队列为空为止。

图4.19所示的是函数lestart。lestart假设已调用splimp来阻塞所有设备中断。 接口必须已初始化

325-333 如果接口没有初始化,lestart立即返回。 将帧从输出队列中退队

335-342 如果接口已初始化,下一帧从队列中移去。如果接口输出队列为空,则lestart返回。

传输帧并传递给BPF

343-350 leput将m中的帧复制到leput第一个参数所指向的硬件缓存中。如果接口带有BPF,将帧传给bpf_tap。我们跳过硬件缓存中帧传输的设备专用初始化代码。 如果设备准备好,重复发送多帧

359 当le->sc_txcnt等于LETBUF时lestart停止给设备传送帧。有些以太网接口能排队多个以太网输出帧。对于LANCE驱动器,LETBUF是此驱动器硬件传输缓存的可用个数,并且le->sc_txcnt保持跟踪有多少个缓存被使用。 将设备标记为忙

360-362 最后,lestart在ifnet结构中设置IFF_OACTIVE来标识这个设备忙于传输帧。

在设备中将多个要传输的帧进行排队有一个不利的负面影响。根据[Jacobson 1998a],LANCE芯片能够在两个帧间以很小的时延传输排队的帧。不幸的是,有些[差]以太网设备会丢失帧,因为它们不能足够快地处理输入的数据。

在一个应用如NFS中,这会很糟糕地互相影响。NFS发送大的UDP数据报(经常是超过8192字节),数据报被IP分片并在LANCE设备中作为多个以太网帧排队。分片在接收方丢失,当NFS重传整个UDP数据报时,会导致很多未完成的数据报及大的延时。

Jacobson提出Sun的LANCE驱动器一次只排队一个帧则可能避免这一问题。

图4.19 函数lestart

4.4 ioctl系统调用

icotl系统调用提供一个通用命令接口,一个进程用它来访问一个设备的标准系统调用所不支持的特性。ioctl的原型为:

4—9

int ioctl(int fd, unsigned long com, ...);

fd是一个描述符,通常是一个设备或网络连接。每种类型的描述符都支持它自己的一套ioctl命令,这套命令由第二个参数com来指定。第三个参数在原型中显示为“...”,因为它是依赖于被调用的ioctl命令的类型的指针。如果命令要取回信息,第三个参数必须是指向一个足够保存数据的缓存的指针。在本文中,我们仅讨论用于插口描述符的ioctl命令。

我们显示的系统调用的原型是一个进程进行系统调用的原型。我们在第15章会看见在内核中的这个函数还有一个不同的原型。

我们在第17章讨论系统调用ioctl的实现,但在本文的各个部分讨论ioctl单个命令的实现。

我们讨论的第一个ioctl命令提供对我们讨论过的网络接口结构的访问。我们总结的本文所有ioctl命令如图4.20所示。 命令 SIOCGIFCONF SIOCGIFFLAGS SIOCGIFMETRIC SIOCSIFFLAGS SIOCSIFMETRIC 第三个参数 struct ifconf * struct ifreq * struct ifreq * struct ifreq * struct ifreq * 函数 ifconf ifioctl ifioctl ifioctl ifioctl 说明 获取接口配置清单 获得接口标志 获得接口度量 设置接口标志 设置接口度量 图4.20 接口ioctl的命令

第一列显示的符号常量标识ioctl命令(第二个参数,com)。第二列显示传递给第一列所显示的命令的系统调用的第三个参数的类型。第三列是实现这个命令的函数的名称。 图4.21显示处理ioctl命令的各种函数的组织。带阴影的函数我们在本章说明。其余的函数在其他章说明。

图4.21 在本章说明的ioctl函数

ifioctl函数

系统调用ioctl将图4.20所列的5种命令传递给图4.22所示的ifioctl函数。

图4.22 函数ifioctl:概述与SIOCGIFCONF

394-405 对于命令SIOCGIFCONF,ifioctl调用ifconf来构造一个可变长ifreq

结构的表。

406-410 对于其他ioctl命令,数据参数是一个指向一个ifreq结构的指针。ifunit在ifnet列表中查找名称为进程在ifr->ifr_name中提供的文本名称(例如:“sl0”,“le1”或“lo0”)的接口。如果没有匹配的接口,ifioctl返回ENXIO。剩下的代码依赖于cmd,它们在图4.29中讨论。

447-454 如果接口ioctl命令不能被识别,ifioctl把命令发送给所请求插口关联协议的用户要求函数。对于IP,这些命令以一个UDP插口发送并且调用udp_usrreq。这一类命令描述在图6.10中。23.10节将详细地讨论函数udp_usrreq。 如果控制到达switch语句外,返回0。

ifconf函数

ifconf为一个进程提供一个标准的方法来发现一个系统中的接口和配置的地址。由结构ifreq和ifconf表示的接口信息如图4.23和4.24所示。

4—10


第4章 接口以太网(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:材料分析测试方法作业题

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: