接上文:TCP/IP协议栈Lwip的设计与实现:之一_龙赤子的博客-CSDN博客
目录
7.网络接口
在LWIP中,对于物理网络硬件的设备驱动通过类似于在BSD中所使用的一个网络接口数据结构来表示。该网络接口结构如图5所示。这个网络接口被放置在一个全局的链表上,这个链通过结构体中的next指针连接起来。
每一个网络接口都有它自己的名称,这被保存在结构体的name域中。这个两个字母的名称指示了用于网络接口的设备驱动的种类,并且只有在人工操作员在运行时配置后才会被使用。该名称由设备驱动设置,并反映网络接口所代表的硬件的种类。举个例子,用于蓝牙驱动的网路接口可能会使用bt这个名称,而用于IEEE802.11B WLAN硬件的网络接口可以使用wl这个名称。由于名称并不需要是唯一的,所以,num域用来区分同一种类的网路接口。
三个IP地址ip_addr、netmask和gw在IP层发送和接收数据包时将被使用,有关它们的使用将在下一节介绍。使用多个IP地址来配置网络接口不太可能,相反,对于每一个IP地址,一个网络接口必须被生成。
输入指针指向一个功能函数,当一个数据包收到时设备驱动将调用该函数。
一个网络接口通过输出指针被连接到设备驱动,该指针指向设备驱动中的一个函数,这个函数用来在物理网络上传输一个数据包,并且当一个数据包将被发送时,IP层就会调用这个函数。这个域会被驱动程序的初始化函数来填充。输出函数的第三个参数,ipaddr,是接收实际链路层帧的主机的IP地址。它可以不与IP包的目的地址相同。特殊情况下,当发送一个数据包给一个不是在本地网路内的主机时,链路层上的帧将被发送给网络上的路由器,在这种情况下,赋予输出函数的IP地址参数将是路由器的IP地址。
最后,状态指针指向该网络接口的设备驱动的具体状态,并由设备驱动设置。
8.IP处理
LWIP仅仅实现了IP的最基本的功能。它能够发送、接收以及转发数据包,但是既不能发送或接收分段(或分片)的IP包也不能捕获带有IP选项的包。在大部分程序中这不会造成任何问题。
8.1接收包
对于传进来的IP包,其处理在网络设备驱动调用ip_input()函数时开始。这儿,IP的版本域以及头长度的初始正常检查会被完成,也包括计算和校验头部的校验和。协议栈不接收任何IP分段是被期望的,因为代理被假设重新组合汇编了任何分段的IP包,所以任何IP分段包将被简单的丢弃而不会有任何通知。对于带有IP选项的包也被假设已经被代理处理,同样会简单得丢掉。
下一步,函数会根据网络接口的IP地址检查目的地址来决定包的目的是否就是当前的主机。网络接口在链表中是有序的,可以进行线性查找。因为所期望的网络接口号是比较小的,所以一个更加富有经验的查找策略而不是线性查找被实现。
如果传进来的包的目的被确定为当前主机,protocol域常常用来决定这个包将被传给更高层的哪一个协议。
8.2发送包
发送的数据包将被函数ip_output()获得,该函数通过函数ip_route()找到相应的网络接口将包传送出去。当发送网络接口被决定后,包就被传给ip_output_if(),输出网络接口会被作为它的一个参数。这儿,所有的IP头数据域将被填充,IP头的校验和也将被计算。IP包的源和目的地址将被作为一个参数传给ip_output_if()。源地质可能会被丢掉,不管怎样,在这种情况下输出网络接口的IP地址将被作为包的源IP地址。
ip_route()函数通过线性查找网络接口链表找到相关的网络接口。在IP包的目的IP地址被查找期间,它将被网络接口的网络掩码所掩盖。如果掩盖后的目的地址等于接口掩盖后的IP地址,该接口将被选中。如果没有发现可匹配的,一个默认的网络接口将被使用。默认网络接口可以在启动或运行时由人工操作员配置(LWIP运行时的人工配置要求有一个能够配置栈的应用程序,这样的一个程序并不包括在LWIP中)。如果默认接口的网络地址与目的IP地址不匹配,网络接口数据结构中的gw(网关)域(如图5)将被选中作为链路层帧的目的IP地址(注意,在这种情况下,IP包的目的地址与链路层帧的目的地址是不同的)。这种平滑路由的基本形式基于一个网络可能有多个路由器与它相匹配这样一种事实。对大多数基本情况而言,也就是本地网路仅有一个路由器,它也照常工作。
因为传输层协议UDP和TCP在计算传输层校验和时需要知道目的IP地址,所以,许多情况下在包被传送到IP层之前,输出网络接口必须被决定。这可以通过让传输层函数直接调用ip_route()函数来完成,又因为包在到达IP层之前输出网络接口已经被确定,所以不再需要重新查找网络接口链。而实际上,那些协议将直接调用函数ip_output_if(),因为这个函数将网络接口作为一个参数,所以可以避免查找输出接口。
8.3转发包
如果网络接口没有IP地址与传送进来的包的目的地址相同,这个包将被转发。这项工作由函数ip_forward()来完成。这时,TTL域将被减一,如果直到0时还没有找到,一个ICMP错误信息将被发送给原始的发送者,同时丢弃该包。当IP头被改变后,IP头的校验和也需要被调整。即使这样,也不需要重新计算整个校验和,因为简单的计算能够被用来调整原始的IP校验和[MK90,Rij94]。最后,这个包被转发到相关的网络接口。用来发现相关的网络接口的算法与发送IP包时相同。
8.4 ICMP处理
ICMP处理非常简单。ICMP包被ip_input()收到后就交给icmp_input(),由它来解码ICMP头并作出相关的反应。一些ICMP消息被传给上层协议,它们会被传输层的特殊函数处理。ICMP的目的不可达的消息能够被传输层协议发送,特别是UDP,icmp_dest_unreach()函数就是用来做这项工作。
使用ICMP ECHO消息来探测一个广泛使用的网络,因此ICMP响应处理可以用来优化性能。实际的处理发生于icmp_input(),包括输入包的IP源和目的地址的交换,改变ICMP类型来回送响应以及调整ICMP校验和。这个包之后就被传回到IP层进行传输。
9.UDP处理
UDP是一个简单的协议,它用来在不同的进程之间多路分解包。每一个UDP会话的状态被保存在一个如图7所示的PCB结构体中。UDP PCBs控制块通过一个链表来保存,当一个UDP数据报到达时可以被用来进行查找匹配。
UDP PCB结构体包含有一个指针用来指向全局UDP PCBs链表中的下一个PCB。一个UDP会话通过终端的IP地址和端口号来定义,它们被存储在local_ip、dest_ip、local_port以及dest_port域中。标识域用来指示当前会话中应当采用什么样的UDP校验和策略。这可以选择UDP彻底校验或者使用UDP Lite[LD999],它仅校验数据报的一部分。如果UDP Lite被使用,校验和长度域用来确定数据报的多大部分应当被校验。
在被PCB确定的会话中当一个数据报被接收到时,最后两个参数recv和recv_arg会被使用。recv所指向的函数在数据报被收到时会被调用执行。
由于UDP的简单性,输入输出处理基本上相同,并都遵守下面的一个直线流程(如图8所示)。发送数据时,应用程序调用udp_send(),udp_send()调用udp_output()。此时,必要的校验将被完成,并且UDP的头域会被填充。因为校验和包括IP包的IP源地址,函数ip_route()在一些情况下会被调用来寻找将被传输的包的网络接口。网络接口的IP地址将被用作包的源IP地址,最后这个包将被转交给ip_output_if()来进行传输。
当一个UDP数据报到达时,IP层调用udp_input()函数,此时,如果在会话中要使用校验和,UDP校验和会被检查,数据报则会被多路分解。当相应的UDP PCB被找到后,recv指向的函数会被调用。