昨晚搞到快两点,今天9.30才起床,希望11点半之前能搞完去吃饭😭
第八章 IP:网际协议
IP分组
参照上图,有几个基本概念(本例中的传输层协议为UDP):
- 报文:由传输层送至IP的数据报,本身包含传输层首部和数据
- 数据报:IP在报文的前面加上自己的IP首部,且根据长度考虑是否分片(上图1-2行)
- 帧:IP将分好片的数据报交给数据链路层,数据链路填上自己的首部后变为帧(上图3行)
- 分组:一个分片的数据报或者一个不需分片的数据报为分组
需注意的是IP数据报结构存储不受机器大小端的影响,均按照下面地址结构存储
IP数据报结构如上图所示,上面的个部分含义已在图中标出
可能需注意的地方:
ip_hl
常用4字节度量,即当ip_hl == 5
时,实际首部长度为5*4=20。其中选项的部分为0~40不等,所以ip_hl
的最大值为:(20+40)/4 = 15,最小值为:20/4=5。另外,由于ip_hl为4的倍数,所以选项的长度一般也为4的倍数。
ipintr函数
1 | void ipintr () |
该函数的基本框架如上,大致就是依靠两个goto实现对分组处理的循环,直到队列为空结束。中间省略的代码主要做下面几件事(实在太长了):
- 验证:把分组从
ipintrq
中取出,验证它们的内容,损坏和有差错的分组自动被丢弃。 - 转发或不转发:调用
ip_dooptions
来处理IP选项,如果没到达最终目的地就尝试转发,反之交给对应传输层协议 - 重装(重装在第10章讲,那我就不管了,开摆!)
- 分用:重装完全的数据报,ip指针设置为到达目的地的完整数据报,反之为空
ip_forward函数
到达非目的地的分组需要被转发。只有当ipforwarding非零或当分组中包含源路由时,ipintr才调用该函数ip_forward
实现转发。ip_forward依靠route结构体与路由表接口
1 | struct route{ |
ro_rt
指向与路由表相关的rtentry结构体ro_dst
指向存放目的地址信息的sockaddr结构体
ip_forward()
的基本流程分为两部分:
- 确定系统是否需要转发此分组,修改IP首部,位分组选择路由
- 处理ICMP重定向报文,并把分组交给
ip_output
进行转发
流程中可能需注意的点:
- IP转发算法把最近的路由缓存在全局route结构的ipforward_rt中
- 当转发错误时,由且仅由路由器向源主机返回ICMP重定向报文,以此更新主机路由表
ip_output
ip_output
为IP输出代码,主要为三部分:
- 首部初始化
- 路由选择
- 源地址选择和分片
第23章 UDP
整体数据传输框架
整体框架如上图所示,从上到下分别是:
用户进程(应用层)–>UDP(传输层)–>IP(网络层)–>数据链路层–>硬件网卡(物理层)
UDP 首部
UDP首部如上图所示,对应域的含义也有标记。需要注意的是IP首部和UDP首部一般是紧挨在一起,我们在CH1-CH3中分析mbuf时已经可以注意到这种现象。而实际上在udp内相关函数(如udp_input
,udp_output
)处理时也会将两者放在一起使用,他们共同构成了结构体udpiphdr
,如下所示:
1 | struct udpiphdr{ |
20字节的IP首部被定义为 ippvly结构体,如下所示,可自行对照IP首部结构图:
1 | struct ipov1y { |
udp_init函数
初始化函数,用来构建PCB双向链表的起始,让头部PCB的next指针和prev指针都指向自己。需注意UDP临时端口号由1024开始分配
前0~1023端口为常用端口,一般都有固定对应服务,想要监听这些端口时也需要sudo权限
PCB是22章的概念:PCB协议控制块存放各UDP和TCP插口所要求的多个信息片,UDP所需要的所有信息都可以在Internet PCB中找到,不存在UDP控制块
udp_out函数
当程序调用send,sendto,sendmsg,write或writev时会发生UDP输出。基本流程如下图:
udp_output
主要干这几件事:
- 添加IP/UDP首部,具体过程可以看前三章分析mbuf的部分
- 检验和计算和伪首部
我们在前三章分析mbuf时说到过,udp会填写一部分首部,ip会填写一部分首部,这里就是分别调用udp_output
,ip_output
实现的
下图中阴影部分由ip_output()
填写,其余浅色部分交由udp_output()
填写
UDP在检验和时会包含三部分内容:12字节的伪首部,8字节的UDP首部,UDP数据
伪首部和UDP首部如上图所示,下图是填充首部和检验和的整个示意图
CRC校验码
这部分机组的时候学过,自行适当复习一下,课件例题如下
udp_input函数
当IP在它的协议字段指定为UDP的输入队列上收到一个IP数据报时,才发生UDP的输入。IP通过协议交换表中的pr_input
函数调用函数udp_input
。因为IP的输入是在软件中断级,所以udp_input
也在这一级上执行。udp_input
的目标是把UDP数据报放置到合适的插口的缓存内,唤醒该插口上因输入阻塞的所有进程。udp_input
函数的主要分三个部分:
UDP对收到的数据报完成一般性的确认;
处理目的地是单播地址的UDP数据报:找到合适的PCB,把数据报放到插口的缓存内,
处理目的地是广播或多播地址的UDP数据报:必须把数据报提交给多个插口。
确认部分可以继续复习前三章mbuf分析部分,个人感觉这本章和前三章关系还是比较紧密的,可以多对照看看