1 原理
问题:ARP表项是否一直有效?
实现原理如下:
2 代码实现
先看下代码组织结构:
prot_pcap.c中增加如下代码:
/**
* 获取自程序启动以来,过去了多长时间
* @return 程序的系统时间
*/
const xnet_time_t xsys_get_time(void) {
static uint32_t pre = 0;
// 以下部分仅供调试时使用
#if 0
uint32_t c = clock() / CLOCKS_PER_SEC;
if (c != pre) {
printf("sec: %d, 100ms: %d\n", c, (xnet_time_t)(clock() * 10 / CLOCKS_PER_SEC));
pre = c;
}
#endif
return (xnet_time_t)(clock() / CLOCKS_PER_SEC);
}
xnet_tiny.h中增加如下代码:
#define XARP_CFG_ENTRY_OK_TMO (5) // ARP表项超时时间
#define XARP_CFG_ENTRY_PENDING_TMO (1) // ARP表项挂起超时时间
#define XARP_CFG_MAX_RETRIES 4 // ARP表挂起时重试查询次数
#define XARP_ENTRY_FREE 0 // ARP表项空闲
#define XARP_ENTRY_OK 1 // ARP表项解析成功
#define XARP_ENTRY_RESOLVING 2 // ARP表项正在解析
#define XARP_TIMER_PERIOD 1 // ARP扫描周期,1s足够
void xarp_poll(void);
xnet_tiny.c中增加如下代码:
static xnet_time_t arp_timer; // ARP扫描定时
/**
* 检查是否超时
* @param time 前一时间
* @param sec 预期超时时间,值为0时,表示获取当前时间
* @return 0 - 未超时,1-超时
*/
int xnet_check_tmo(xnet_time_t * time, uint32_t sec) {
xnet_time_t curr = xsys_get_time();
if (sec == 0) { // 0,取当前时间
*time = curr;
return 0;
} else if (curr - *time >= sec) { // 非0检查超时
*time = curr; // 当超时时,才更新时间
return 1;
}
return 0;
}
/**
* ARP初始化
*/
void xarp_init(void) {
arp_entry.state = XARP_ENTRY_FREE;
// 获取初始时间
xnet_check_tmo(&arp_timer, 0);
}
/**
* 查询ARP表项是否超时,超时则重新请求
*/
void xarp_poll(void) {
if (xnet_check_tmo(&arp_timer, XARP_TIMER_PERIOD)) {
switch (arp_entry.state) {
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;
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;
}
}
}
/**
* 更新ARP表项
* @param src_ip 源IP地址
* @param mac_addr 对应的mac地址
*/
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, 6);
arp_entry.state = XARP_ENTRY_OK;
arp_entry.tmo = XARP_CFG_ENTRY_OK_TMO;
arp_entry.retry_cnt = XARP_CFG_MAX_RETRIES;
}
/**
* 轮询处理数据包,并在协议栈中处理
*/
void xnet_poll(void) {
ethernet_poll();
xarp_poll();
}