计算机网络实验目录
- eth协议实现
- ARP协议实现
- RIP路由配置和协议分析
- IP协议实现
- ICMP协议实现
- UDP协议实现
- NAT组网
- 邮件客户端的设计与实现
前言
HITSZ 2022春计算机网络实验完成记录
IP报文格式
- 版本:4位,指IP协议的版本,通信双方使用的版本必须一致,
一般的值为0100(IPv4),0110(IPv6)
- 首部长度: 四位,最高(1111)标识15,
但是每一单元对应于32位字即4字节
,首部固定长度有20字节,所以最小为5 - 区分服务:
本实验中,可将TOS设置为0
- 总长度:首部和数据长度之和,字段总长度为16位,单位为字节,最大位65535字节
分片重组部分
- 标识:当IP数据报需要分片的时候,这个标识字段的值会被拷贝到所有数据报文的标识字段中,相同标识字段的值使分片后的各数据报最后能重组为原来的数据报,
与序列号无关
,唯一地标识主机发送的每一个数据报,其初始值是随机的,每发送一个数据报其值就加1。同一个数据报的所有分片都具有相同的标识值。本实验中,标识字段可从0开始计数,然后往上自增。
- 标志,占3位,只有前两位有意义:
最低位 MF = 1:标识后面还有分片
中间位 DF = 1:不允许分片
;为 0允许分片
- 片偏移:分组之后,某片在原分组中的相对位置,是相对于用户数据的起点,该片(的起点)从何开始。片偏移以8个字节为单位,
所以分片长度一定是8字节(64位的整数倍)
,分片相对原始IP数据报数据部分的偏移。实际的偏移值为该值左移3位后得到的,所以除了最后一个IP数据报分片外,每个IP分片的数据部分的长度都必须是8的整数倍。 - 生存时间:TTL,又叫跳数,指数据报在互联网中至多可以经过多少个路由器,每次转发这个值减一;8位,最多255;
若初始值设为1,则只能在本局域网传送
;TTL值被发送端设置,常设置为64。数据报在转发过程中每经过一个路由该值就被路由器减1.当TTL值为0时,路由器就将该数据包丢弃,并向源端发送一个ICMP差错报文。
- 协议:占8位,指出此数据报携带的数据使用的是何种协议,以便目的主机的IP层知道应该将数据部分上交给哪个协议进行处理.区分IP协议上的上层协议。在Linux系统的/etc/protocols文件中定义了所有上层协议对应的协议字段,ICMP为1,TCP为6,UDP为17。
- 首部检验和:只检验首部数据,由发送端填充接收端对其使用CRC算法校验,检查IP数据报头部在传输过程中是否损坏。
- 可变部分:为了增加IP数据报的功能,一般不用
实验部分
遇到的一些问题:
- 关于大小端转化的问题:其实之前一直没有这个困惑,但是在做IP部分的实验的时候,由于要求首部校验和,而我们操作的时候都只是把部分(16位)数据大小端转化。所以开始的时候一直纠结收到IP数据报首部header后,如果不进行大小端转换,结构体(ip_hdr_t)中的数据顺序是否会影响最终的校验和。
问题的解答是:校验和本身不在乎你的数据顺序,它只在乎数据传输前后每一个bit有没有改变,并且由于它是按照16bit进行读取,所以只需要保证16位数据传输前后内部(2个字节)的顺序一致。所以最终决定我们是否能正确获得校验和的是发送端发送数据的顺序和接收端接收数据放到内存的数据
,这一部分可以参考大端小端 - 求校验和内容可以参考首部校验和算法
- 另一个问题就是函数参数用++的问题,例如
d = sum(a++, b++)
,我们会发现虽然我们可以正确的得到d值,但是a,b的值实际上没有变化,这说明++操作实际上是在函数形参中实现的
代码
#include "net.h"
#include "ip.h"
#include "ethernet.h"
#include "arp.h"
#include "icmp.h"
uint16_t ID = 0; //ID 自增
/**
* @brief 计算16位校验和
*
* @param buf 要计算的数据包
* @param len 要计算的长度
* @return uint16_t 校验和
*/
uint16_t checksum16(uint16_t *data, size_t len)
{
// TO-DO
//Step1 :把data看成是每16个bit(即2个字节)组成一个数,相加(注意,16位加法的结果可能会超过16位,因此加法结果需要用32位数来保存)。
uint32_t sum = 0;
size_t count = len;
// Step2 :如果最后还剩8个bit值,也要相加这个8bit值。
while(count > 1){
sum += *(uint16_t *) data++;
count -= 2;
}