手写TCP/IP协议栈——ARP超时重新请求

会当击水三千里,自信人生二百年。

1、以太网控制器在接收数据包的时候会检查数据包的MAC地址是否合法

① 广播地址 ② 本地网卡的MAC地址

如果是非法数据包,压根都不会进入协议栈,直接在硬件层面就丢包了

2、为什么需要设置ARP重传机制?为何要给每个ARP表项设置一个定时器?

arp_entry记录的是网络当中主机IP地址与MAC地址的映射关系,但是由于网卡绑定的IP地址是动态的。如果对方的机器下线,此时DHCP服务器可能会回收这个IP地址进而分配给别的网卡,这时候我们本机保存的arp表项当中维护的映射关系就出错了,需要重新进行修正。

总结:记录IP地址与MAC地址的映射关系是为了提高转换速率,而设置定时器,甚至是删除部分表项则是为了适应网络的动态变化。由于网络是不可靠的,所以我们需要在自己的协议栈当中添加ARP的超时重传机制。

3、协议栈实现超时重传机制

设置一个定时器,到时间了就重新向表项中的主机发送ARP请求。

除此之外,还需要设置另外一个超时值,因为我们重新发送ARP请求之后,还需要判断一下在指定的时间内有没有收到响应,从而避免的等待时间过长的情况的出现。如果超过这个时间还没有收到响应的话,我们就会再发送一个请求,直到对方给了回复或者超过最大请求次数,此时就可以将该表项删除。

代码实现部分

1)在port_pcap.c中添加时间的头文件:#include<time.h>

并且添加xsys_get_time函数:clock()表示的是程序从启动到调用clock()的时间

而clock() / CLOCKS_PER_SEC表示的是从程序启动到调用经过多少秒

cpp 复制代码
const xnet_time_t xsys_get_time(void) {
	//clock()表示的是从程序启动到调用这个函数的时间
	//clock()/CLOCKS_PER_SEC表示的是从程序启动到调用经过多少秒
	return  clock() / CLOCKS_PER_SEC;
}

2)在xnet_tiny.h中添加xnet_time_t的定义

cpp 复制代码
typedef uint32_t xnet_time_t;	//时间类型,返回当前系统跑了多少s
const xnet_time_t xsys_get_time(void);

在xnet_tiny.h中添加xarp_poll的定义

cpp 复制代码
void xarp_poll(void);

然后在xnet_poll函数中调用xarp_poll

cpp 复制代码
void xnet_poll(void) 
{
    ethernet_poll();
    xarp_poll();
}

3)在xnet_tiny.h中添加:

① arp表项的超时时间;

② arp表项挂起时最大的查询(重试)次数;

③ arp表项挂起时的超时时间(查一次最多等多久);

cpp 复制代码
#define XARP_CFG_ENTRY_OK_TMO		(5)		//arp表项的超时时间设置为5s
#define XARP_CFG_MAX_RETRIES		(4)		//arp表项挂起时最大的查询次数
#define XARP_CFG_ENTRY_PENDING_TMO	(1)		//arp表项挂起时的超时时间(查一次最多等多久)

在xnet_tiny.h中添加arp表项的几个状态:

cpp 复制代码
#define  XARP_ENTRY_FREE		  0		//arp表项空闲
#define  XNET_ENT_OK			  1		//arp表项解析成功
#define	 XARP_ENTRY_RESOLVING     2		//arp表项正在解析

注:OK表示ARP表项的IP地址与MAC地址之间的转换关系是正确的,RESOLVING表示需要确认/解析映射关系是否正确

4)接下来实现xarp_poll函数:

协议栈每隔1s扫描一次整个ARP表中的表项(当然本项目只设置了一个arp表项)

每个正常状态的arp表项如果被扫描五次,则需要被设置为RESOLVING状态

在解析状态如果连续5次没有请求成功,就会被回收表项

cpp 复制代码
void xarp_poll(void)
{
	if (xnet_check_tmo(&arp_timer, XARP_TIMER_PERIOD))
	{
		switch (arp_entry.state)
		{
		case XARP_ENTRY_OK:
			if (--arp_entry.tmo == 0)
			{
				xarp_make_request(&arp_entry.ipaddr);
				arp_entry.state = XARP_ENTRY_RESOLVING;
				arp_entry.tmo = XARP_CFG_ENTRY_PENDING_TMO;
			}
			break;
		case XARP_ENTRY_RESOLVING:
			if (--arp_entry.tmo == 0)
			{
				if (arp_entry.retry_cnt-- == 0)
					arp_entry.state = XARP_ENTRY_FREE;
				else
				{
					xarp_make_request(&arp_entry.ipaddr);
					arp_entry.state = XARP_ENTRY_RESOLVING;
					arp_entry.tmo = XARP_CFG_ENTRY_PENDING_TMO;
				}
			}
			break;
		}
	}
}

同时在update_arp_entry函数当中也需要添加对于arp表项其他字段的初始化

cpp 复制代码
static void update_arp_entry(uint8_t* src_ip, uint8_t* mac_addr)
{
	memcpy(arp_entry.ipaddr.array, src_ip, XNET_IPV4_ADDR_SIZE);
	memcpy(arp_entry.macaddr, mac_addr, XNET_MAC_ADDR_SIZE);
	arp_entry.state = XARP_ENTRY_OK;
	arp_entry.tmo = XARP_CFG_ENTRY_OK_TMO;
	arp_entry.retry_cnt = XARP_CFG_MAX_RETRIES;
}

5)定义一个宏XARP_TIME_PERIOD 来设置arp表的扫描时间,即多久调用一次xarp_poll

cpp 复制代码
#define	 XARP_TIME_PERIOD		1		//arp扫描周期(1s扫描一次)   

6)实现xnet_check_tmo函数,用来检查arp表项是否超时,同时通过指针可以更新timer的值

特别地:在这里指定sec=0表示用于获取系统当前运行的秒数;非0的时候才进行arp的超时检查

cpp 复制代码
//检查是否超时,同时通过指针可以更新timer的值
int xnet_check_tmo(xnet_time_t* timer, uint32_t sec) {
	xnet_time_t cur = xsys_get_time();
	if (sec == 0) {		//0特殊地,用于获取当前的时间
		*timer = cur;
		return 0;
	}
	else if (cur - *timer >= sec) {		//非0检查超时
		*timer = cur;		//当超时的时候,才更新时间
		return 1;
	}
	return 0;
}

下面让我们一起进入愉快的调试环节吧🤷‍♀️:

①首先检查一下计时器是否正常工作

能够正常进入,得到的时间单位是秒,没问题

②使用虚拟机ping 192.168.254.2,观察5s之后能否进入XARP_ENTRY_OK的处理函数

③5s超时之后,紧接着就会进入XARP_ENTRY_RESOLVING状态

④把网卡禁用观察能否进入清除ARP表项的分支

发现程序在进入5次RESOLVING状态之后还未进入OK状态,则清空了该ARP表项(置为FREE状态),后续将不会进入任何case当中

如果上面的三个断点你都能进入的话,那么恭喜你完成了本章的所有内容的🎉🎉

至此我们已经实现了arp超时重传的所有功能!!!

下一节课我们会继续带领大家实现IP协议的相关功能,期待的话多多关注吧 🤞💕💕

相关推荐
京井1 小时前
从中序与后序遍历序列构造二叉树解题思路
c语言·算法
方块A1 小时前
轻量级的 HTTP 跳转服务
网络·网络协议·http
水天需0101 小时前
VS Code C++ 环境配置及 HelloWorld 程序
c++
初圣魔门首席弟子1 小时前
第六章、[特殊字符] HTTP 深度进阶:报文格式 + 服务器实现(从理论到代码)
linux·网络·c++
永远都不秃头的程序员(互关)1 小时前
查找算法深入分析与实践:从线性查找到二分查找
数据结构·c++·算法
Sunsets_Red1 小时前
二项式定理
java·c++·python·算法·数学建模·c#
爱跑步的程序员~1 小时前
TCP三次握手
网络·网络协议·tcp/ip
好评1242 小时前
C/C++ 内存管理:摆脱野指针和内存泄漏
开发语言·c++·内存管理·c/c++
威哥爱编程2 小时前
【鸿蒙开发案例篇】NAPI 实现 ArkTS 与 C++ 间的复杂对象传递
c++·harmonyos·arkts