在前面实现的arp协议中,我们只能被动回复对方发来的arp请求。在网络通讯中,一个设备应该有一张自己的arp表,同时可以广播自己的arp。所以现在需要实现一个arp表,同时可以广播自己的arp。
工作模式
PC 1 只知道PC3 的IP地址是10.1.1.3, 但是不知道PC3的MAC, 现在想获取PC3 的MAC地址:
发送者PC1:PC 1 会发送一个广播帧, 源IP和源MAC是PC1, 目的IP是PC3,目的MAC为FF-FF-FF-FF-FF-FF, 这个帧是广播发送的, 该网络内所有主机都会接收到,这个报文的载荷内容是一个ARP请求报文,意思是我的MAC地址是MAC1,我想给10.1.1.3发送数据, 谁是10.1.1.3, 请回复一下我。
非目的主机PC2:由于这个广播帧网络内主机都可以收到,PC2 收到这个帧, 提取IP地址, 发现, 它要找的IP地址是10.1.1.3, 我的IP地址是10.1.1.2, 这个数据包不是发给我的, 丢弃, 不回复。
目的主机PC3:PC 3 收到这个广播数据帧后,拆开数据帧, 提取IP地址, 发现和我本地IP地址是一样的, 这个数据包就是发给我的, 然后再拆, 提取报文后, 了解到对方要请求我自己的MAC地址,然后就打包一个ARP的应答报文,源IP和源MAC都是自己的,目的IP和目的MAC是对方的,单播的发送给接收者,这样PC1 就有了PC3的MAC地址。
数据结构
在Linux内核中有两种数据结构被频繁使用;一个是rb_tree
,还有一个是list
。也就是红黑树和链表,机器人里的arp不算多,使用链表就足够了。暂且使用一个头插list
作为arp表。
/*
* 版权所有: Copyright (c) 2024-2025 XXX Company. All rights reserved.
* 系统名称: Ubuntu20.04.6
* 文件名称: arp.h
* 内容摘要: 单向链表实现的arp表
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-22
*/
#ifndef __ARP_H__
#define __ARP_H__
#define ARP_ENTRY_STATUS_DYNAMIC 0
#define ARP_ENTRY_STATUS_STATIC 1
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
if (list != NULL) list->prev = item; \
list = item; \
}while(0)
#define LL_REMOVE(item, list) do { \
if (item->prev != NULL) item->prev->next = item->next; \
if (item->next != NULL) item->next->prev = item->prev; \
if (list == item) list = item->next; \
item->prev = item->next = NULL; \
}while(0)
struct arp_entry {
uint32_t ip;
uint8_t hwaddr[RTE_ETHER_ADDR_LEN];
uint8_t type;
struct arp_entry* prev;
struct arp_entry* next;
};
struct arp_table {
struct arp_entry* entries;
int count;
};
static struct arp_table* arpt = NULL;
/*
* 函数名称: get_arp_instace
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-22
* 功能描述: 单例模式,获取arp_table的单例
* 返 回 值: arp_table句柄
*/
struct arp_table* get_arp_instace() {
if (arpt == NULL) {
arpt = (struct arp_table*)rte_malloc("arp table", sizeof(struct arp_table), 0);
if (arpt == NULL) {
rte_exit(EXIT_FAILURE, "Malloc arp table failed");
}
memset(arpt, 0, sizeof(struct arp_table));
}
return arpt;
}
/*
* 函数名称: get_mac_from_arp
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-22
/*
* 函数名称:
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-22
* 功能描述: 根据ip查找arp表
* 返 回 值: ip对应的mac地址
*/
* 功能描述:
* 返 回 值:
*/
uint8_t* get_mac_from_arp(uint32_t ip) {
struct arp_table* ins = get_arp_instace();
struct arp_entry* ite = NULL;
for(ite = ins->entries; ite != NULL; ite = ite->next) {
if (ite->ip == ip)
return ite->hwaddr;
}
return NULL;
}
#endif
arp表采用单例模式,使用的时候获取arp表头的句柄,再进行arp表的增删查改。
LL_ADD
和LL_REMOVE
使用宏实现头插的链表,这个链表后面还有用,可以复用。
新增函数
rte_timer_subsystem_init
:用于初始化定时器子系统。rte_get_timer_hz
:获取系统时钟频率,即每秒的时钟周期数。rte_lcore_id
:返回当前线程/核心的逻辑核 ID。rte_timer_reset(struct rte_timer* tim, uint64_t ticks, enum rte_timer_type type, unsigned int tim_lcore, rte_timer_cb_t fct, void* arg)
tim:指向要重置的定时器结构体的指针。
ticks:定时器的触发时间,以系统时钟周期为单位。当系统经过指定数量的周期后,定时器将被触发。
type:定时器类型。可以是单次触发 (
RTE_TIMER_SINGLE
) 或循环触发 (RTE_TIMER_PERIODICAL
)。tim_lcore:定时器关联的逻辑核心 ID。在指定的逻辑核心上触发定时器回调函数。
fct:定时器回调函数指针。当定时器触发后,将会执行此回调函数。
arg:用户自定义参数,作为回调函数的参数传入。
rte_rdtsc
:用于获取当前处理器的时间戳计数器 (TSC) 值。
定时器和回调函数设计
/*
* 函数名称: arp_request_timer_cb
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-22
* 功能描述: 定时器回调函数,广播arp
* 返 回 值: None
*/
static void arp_request_timer_cb(__attribute__((unused)) struct rte_timer* t, void* arg) {
struct rte_mempool* mbuf_pool = (struct rte_mempool*)arg;
int i = 0;
for (i = 0; i < 255; i++) {
uint32_t dstip = (nLocalIp & 0x00FFFFFF) | (0xFF000000 & (i << 24));
struct in_addr addr;
addr.s_addr = dstip;
printf("<---- arp dst : %s\n", inet_ntoa(addr));
struct rte_mbuf* arpbuf = NULL;
uint8_t* dstmac = get_mac_from_arp(dstip);
if (dstip == NULL) {
arpbuf = ln_arp_send(mbuf_pool, RTE_ARP_OP_REQUEST, nDefaultArpMac, nLocalIp, dstip);
}
else {
arpbuf = ln_arp_send(mbuf_pool, RTE_ARP_OP_REQUEST, dstmac, nLocalIp, dstip);
}
rte_eth_tx_burst(nDevPortId, 0, &arpbuf, 1);
rte_pktmbuf_free(arpbuf);
}
}
rte_timer_subsystem_init();
struct rte_timer arp_timer;
rte_timer_init(&arp_timer);
uint64_t hz = rte_get_timer_hz();
unsigned lcore_id = rte_lcore_id();
rte_timer_reset(&arp_timer, hz, PERIODICAL, lcore_id, arp_request_timer_cb, mbuf_pool);
static uint64_t prev_tsc = 0, cur_tsc;
uint64_t dirr_tsc;
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
if(diff_tsc > TIMER_RESOLUTION_CYCLES) {
rte_timer_manage();
prev_tsc = cur_tsc;
}
arp相关修改
结果演示
项目地址
评论