《TCP/IP详解 卷2:实现》 —3.11 if_attach函数

举报
华章计算机 发表于 2019/11/21 11:52:56 2019/11/21
【摘要】 本节书摘来自华章计算机《TCP/IP详解 卷2:实现》一书中第3章,第3.11节,作者是[美]加里R.赖特(Gary R.Wright),W. 理查德史蒂文斯(W.Richard Stevens),陆雪莹 蒋慧 等译 谢希仁 校。

3.11   if_attach函数

前面显示的三个接口初始化函数都调用if_attach来完成接口的ifnet结构的初始化,并把这个结构插到先前配置的接口的列表中。在if_attach中,内核也为每个接口初始化并分配一个链路层地址。图3-32说明了由if_attach构造的数据结构。

image.png

图3-32   ifnet列表

在图3-32中,if_attach被调用了三次:以一个le_softc结构为参数从leattach调用,以一个sl_softc结构为参数从slattach调用,以一个通用ifnet结构为参数从loopattach调用。每次调用时,它向ifnet列表中添加一个新的ifnet结构,为这个接口创建一个链路层ifaddr结构(包含两个sockaddr_dl结构,见图3-33),并且初始化ifnet_addrs数组中的一项。

image.png

图3-33   结构sockaddr_dl

图3-20显示了包含在le_softc[0]和sl_softc[0]中的嵌套结构。

初始化以后,接口仅配置链路层地址。例如,IP地址直到后面讨论的ifconfig程序才配置(6.6节)。

链路层地址包含接口的逻辑地址和硬件地址(如果网络支持,例如le0的48位以太网地址)。在ARP和OSI协议中要用到这个硬件地址,而一个sockaddr_dl中的逻辑地址包含一个名称和这个接口在内核中的索引数值,它支持用于在接口索引和关联ifaddr结构(ifa_ifwithnet,图6-32)间相互转换的表查找。

结构sockaddr_dl显示在图3-33中。

55-57   回忆图3-18,sdl_len指明了整个地址的长度,而sdl_family指明了地址族类,在此例中为AF_LINK。

58    sdl_index在内核中标识接口。图3-32中的以太网接口的索引为1,SLIP接口的索引为2,而环回接口的索引为3。全局整数变量if_index包含的是内核最近分配的一个索引值。

60   sdl_type根据这个数据链路地址的ifnet结构的成员if_type进行初始化。

61-68   除了一个数字索引,每个接口有一个由结构ifnet的成员if_name和if_unit组成的文本名称。例如,第一个SLIP接口叫“sl0”,而第二个叫“sl1”。文本名称存储在数组sdl_data的前面,并且sdl_nlen为这个名称的字节长度(在我们的SLIP例子中为3)。

数据链路地址也存储在这个结构中。宏LLADDR将一个指向sockaddr_dl结构的指针转换成一个指向这个文本名称的第一个字节的指针。sdl_alen是硬件地址的长度。对于一个以太网设备,48位硬件地址位于结构sockaddr_dl的这个文本名称的前面。图3-38所示的是一个初始化了的sockaddr_dl结构。

Net/3不使用sdl_slen。

if_attach更新两个全局变量。第一个是if_index,它存放系统中的最后一个接口的索引;第二个是ifnet_addrs,它指向一个ifaddr指针的数组。这个数组的每项都指向一个接口的链路层地址。这个数组提供对系统中每个接口的链路层地址的快速访问。

函数if_attach较长,并且有几个奇怪的赋值语句。从图3-34开始,我们分5个部分讨论这个函数。

59-74   if_attach有一个参数ifp,这是一个指向ifnet结构的指针,由网络设备驱动程序初始化。Net/3在一个链表中维护所有这些ifnet结构,全局指针ifnet指向这个链表的首部。while循环查找链表的尾部,并将链表尾部的空指针的地址存储到p中。在循环后,新ifnet结构被接到这个ifnet链表的尾部,if_index加1,并且将新索引值赋给ifp-> if_index。

image.png

图3-34   函数if_attach:分配接口索引

1. 必要时调整ifnet_addrs数组的大小

75-85   第一次调用if_attach时,数组ifnet_addrs不存在,因此要分配16(16=8<<1)项的空间。当数组满时,一个两倍大的新数组被分配,并且老数组中的项被复制到新的数组中。

if_indexlim是if_attach私有的一个静态变量。if_indexlim通过<<=操作符来更新。

图3-34中的函数malloc和free不是同名的标准C库函数。内核版的第二个参数指明一个类型,内核中可选的诊断代码用它来检测程序错误。如果malloc的第三个参数为M_WAITOK,且函数需要等待释放的可用内存,则阻塞调用进程。如果第三个参数为M_DONTWAIT,则当内存不可用时,函数不阻塞并返回一个空指针。

函数if_attach的下一部分显示在图3-35中,它为接口准备一个文本名称并计算链路层地址的长度。

image.png

图3-35   if_attach函数:计算链路层地址大小

2. 创建链路层名称并计算链路层地址的长度

86-99   if_attach用if_unit和if_name组装接口的名称。函数sprint_d将if_unit的数值转换成一个串并存储到workbuf中。masklen是sockaddr_dl数组中sdl_data前面的信息所占用的字节数加上这个接口的文本名称的大小(namelen + unitlen)。函数对socksize进行“上舍入”,socksize是masklen加上硬件地址长度(if_addrlen),上舍入为一个长整型(ROUNDUP)。如果它小于一个sockaddr_dl结构的长度,就使用标准的sockaddr_dl结构。ifasize是一个ifaddr结构的大小加上两倍的socksize,因此,它能容纳结构sockaddr_dl。

在函数的下一部分中,if_attach分配结构并将结构连接起来,如图3-36所示。

image.png

图3-36中,在ifaddr结构与两个sockaddr_dl结构间有一个空隙,用来说明它们分配在一个连续的内存中但没有定义在一个C结构中。

像图3-36所示的组织还出现在结构in_ifaddr中;在这个结构的通用ifaddr部分中的指针指向在这个结构的设备专用部分中的专用化sockaddr结构,在本例中是结构sockaddr_dl。图3-37所示的是这些结构的初始化。

3. 地址

100-116   如果有足够的内存可用,bzero把新结构清零,并且sdl指向紧接着ifnet结构的第一个sockaddr_dl。若没有可用内存,代码被忽略。

sdl_len被设置为结构sockaddr_dl的长度,并且sdl_family被设置为AF_LINK。用if_name和unitname组成的文本名称存放在sdl_data中,而它的长度存放在sdl_nlen中。接口的索引被复制到sdl_index中,而接口的类型被复制到sdl_type中。分配的结构***入数组ifnet_addrs中,并通过ifa_ifp和ifa_addrlist链接到结构ifnet。最后,结构sockaddr_dl用ifa_addr链接到ifnet结构。以太网接口用arp_rtrequest取代默认函数link_rtrequest。环回接口装入函数loop_rtrequest。我们在第19章和第21章讨论ifa_rtrequest和arp_rtrequest。而linkrtrequest和loop_rtrequest留给读者自己去研究。以上完成了第一个sockaddr_dl结构的初始化。

image.png

图3-37   函数if_attach:分配并初始化链路层地址

4. 掩码

117-123   第二个sockaddr_dl结构是一个比特掩码,用来选择出现在第一个结构中的文本名称。ifa_netmask从结构ifaddr指向掩码结构(在这里是选择接口文本名称而不是网络掩码)。while循环把与名称对应的那些字节的每个比特都置为1。

图3-38所示的是我们以太网接口例子的两个初始化了的sockaddr_dl结构。它的if_name为“le”,if_unit为0,if_index为1。

图3-38中所示的是ether_ifattach对这个结构初始化后的地址(图3-41)。

图3-39所示的是第一个接口链接if_attach后的结构。

在if_attach的最后,以太网设备的函数ether_ifattach被调用,如图3-40所示。

124-127   开始不调用ether_ifattach (例如:从leattach),是因为它要把以太网硬件地址复制到if_attach分配的sockaddr_dl中。

XXX注释表示作者发现在此处插入代码比修改所有的以太网驱动程序要容易。

image.png

图3-38   初始化了的以太网sockaddr_dl结构(省略了前缀sdl_)

image.png

图3-39   第一次调用if_attach后的ifnet和sockaddr_dl结构

image.png

图3-40   函数if_attach:以太网初始化

image.png

图3-41   函数ether_ifattach

image.png

图3-41   (续)

5. ether_ifattach函数

函数ether_ifattach执行对所有以太网设备通用的ifnet结构的初始化。

338-357   对于一个以太网设备,if_type为IFT_ETHER,硬件地址有6字节,整个以太网首部有14字节,而以太网MTU为1500字节 (ETHERMTU)。

leattach已经指派了MTU,但其他以太网设备驱动程序可能没有执行这个初始化。

4.3节讨论以太网帧组织的更多细节。for循环定位接口的链路层地址,然后初始化结构sockaddr_dl中的以太网硬件地址信息。在系统初始化时,以太网地址被复制到结构arpcom中,现在被复制到链路层地址中。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。