【dpdk】5.dpdk实现arp-table

10 篇文章 3 订阅
  • 基于udp协议
  • 在上一次实现的代码上加功能


前言

定时主动发送arp request,响应其他机器的arp reply,记录在arp table中。方便在我们调用udp send的时候,有一个查表的过程。

arp有两个作用:

  • 1.响应其他机器的arp request (在前面已经实现)
  • 2.定时主动发送arp request,响应其他机器的arp reply,记录在arp table中 (本章主要实现此功能)

一、先添加相关标识

#define ENABLE_ARP_REPLY 	1

然后实现这个宏

二、判断接到的数据是请求/响应

这是在main函数中,根据之前的arp请求处理的代码上添加相应判断条件

if (ahdr->arp_data.arp_tip == gLocalIp)
{
	// 处理arp请求的代码
	if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REQUEST)) // rte_cpu_to_be_16 转换大小端
	{
		struct rte_mbuf *arpbuf = ng_send_arp(mbuf_pool, ahdr->arp_data.arp_sha.addr_bytes,
											  ahdr->arp_data.arp_tip, ahdr->arp_data.arp_sip);

		rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
		rte_pktmbuf_free(arpbuf);

		rte_pktmbuf_free(mbufs[i]);
	}
	// 处理arp响应(本机定时给别人发送arp请求,对端将arp值返回过来,本机将其记录在arp表中)
	else if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REPLY))
	{
		(见后)
	}
}

三、定义arp表的数据结构

这里创建一个头文件arp.h,在这里定义arp表的结构,并且实现reply中的表。
首先看到Windows里面的arp表,然后组织出arp表的结构。包含:

  • IP地址
  • 物理地址
  • 类型
    在这里插入图片描述

1. 定义入口以及整个表的结构(双向链表)

#define ARP_ENTRY_STATUS_DYNAMIC 0
#define ARP_ENTRY_STATUS_STATIC 1

struct arp_entry //入口
{ 
    uint32_t ip;
    uint8_t hwaddr[RTE_ETHER_ADDR_LEN];
    uint8_t status;

    // 多条记录用双向链表
    struct arp_entry *next;
    struct arp_entry *prev;
};

struct arp_table //arp表
{
    struct arp_entry *entries;
    int count;
};

2.对链表进行添加(头插法)

#define LL_ADD(item, list) do {		\
	item->prev = NULL;				\
	item->next = list;				\
	if (list != NULL) list->prev = item; \
	list = item;					\
} while(0)

注意:

  1. do{ }while(0) 语句的意义是调用这个宏定义时是以函数的形式执行的,会加一个,这里是为了匹配这个分号。
  2. 这里的\占位符是为了使得对应的宏定义处于一行。

3.对链表进行删除

#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)

4.查找item

查找是否已存在对应的arp信息,如果没有就添加到表上。

(1)定义单例模式

// 定义单例模式
static struct arp_table *arpt = NULL;
static struct arp_table *arp_table_instance(void)
{

    if (arpt == NULL)
    {

        arpt = rte_malloc("arp table", sizeof(struct arp_table), 0); //dpdk的malloc,这里0的意思是对齐
        if (arpt == NULL)
        {
            rte_exit(EXIT_FAILURE, "rte_malloc arp table failed\n");
        }
        memset(arpt, 0, sizeof(struct arp_table)); //每次malloc之后都要memeset
    }

    return arpt;
}

(2)匹配得到目的ip对应的MAC地址

// 匹配目的mac地址,匹配上了就不添加,没有匹配就可以添加到arp表中
static uint8_t *get_dst_macaddr(uint32_t dip)
{
    struct arp_entry *iter;
    struct arp_table *table = arp_table_instance();

    for (iter = table->entries; iter != NULL; iter = iter->next)
    {
        if (dip == iter->ip)
        {
            return iter->hwaddr;
        }
    }

    return NULL;
}

四、回到arptable.c处理请求

// 处理arp响应(本机定时给别人发送arp请求,对端将arp值返回过来,本机将其记录在arp表中)
else if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REPLY))
{
	printf("arp --> reply\n");

	struct arp_table *table = arp_table_instance();

	uint8_t *hwaddr = get_dst_macaddr(ahdr->arp_data.arp_sip);
	if (NULL == hwaddr)
	{
		struct arp_entry *entry = rte_malloc("arp_entry", sizeof(struct arp_entry), 0);
		if (entry)
		{
			memset(entry, 0, sizeof(struct arp_entry));

			entry->ip = ahdr->arp_data.arp_sip;
			rte_memcpy(entry->hwaddr, ahdr->arp_data.arp_sha.addr_bytes, RTE_ETHER_ADDR_LEN);
			entry->status = 0;

			LL_ADD(entry, table->entries);
			table->count++;
		}
	}
}

五、进行调试

添加一个全局的打印mac地址的方法,可复用

static void
print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
{
	char buf[RTE_ETHER_ADDR_FMT_SIZE];
	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
	printf("%s%s", name, buf);
}

调试代码,在arp表中printf出来mac和对应的ip

#if ENABLE_DEBUG
	struct arp_entry *iter;
	for (iter = table->entries; iter != NULL; iter = iter->next)
	{

		struct in_addr addr;
		addr.s_addr = iter->ip;

		print_ethaddr("arp table --> mac: ", (struct rte_ether_addr *)iter->hwaddr);

		printf(" ip: %s \n", inet_ntoa(addr));
	}
#endif

六、启动定时器

这里实现定时发送request请求
添加头文件:

#include <rte_malloc.h>
#include <rte_timer.h>
#define ENABLE_TIMER 1

#define TIMER_RESOLUTION_CYCLES 120000000000ULL // 10ms * 1000 = 10s * 6

在main函数外:

#if ENABLE_TIMER

static void
arp_request_timer_cb(__attribute__((unused)) struct rte_timer *tim,
					 void *arg)
{

	struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;

#if 0
	struct rte_mbuf *arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, ahdr->arp_data.arp_sha.addr_bytes, 
		ahdr->arp_data.arp_tip, ahdr->arp_data.arp_sip);

	rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
	rte_pktmbuf_free(arpbuf);

#endif

要给局域网内每一台机器都发送,因此循环

	int i = 0;
	for (i = 1; i <= 254; i++)
	{

		uint32_t dstip = (gLocalIp & 0x00FFFFFF) | (0xFF000000 & (i << 24)); //把gLocalIp的最高位变为1~254(i左移24位就到了最高位)

		struct in_addr addr;
		addr.s_addr = dstip;
		printf("arp ---> src: %s \n", inet_ntoa(addr));

		struct rte_mbuf *arpbuf = NULL;
		uint8_t *dstmac = ng_get_dst_macaddr(dstip);
		if (dstmac == NULL)
		{

			arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, gDefaultArpMac, gLocalIp, dstip);
		}
		else
		{

			arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, dstmac, gLocalIp, dstip);
		}

		rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
		rte_pktmbuf_free(arpbuf);
	}
}

#endif

注:
arphdr中的默认mac形式是FF:FF:FF:FF:FF:FF
ethdr中的默认mac形式是00:00:00:00:00:00

参数引入optioncode (之前都默认为2)
在这里插入图片描述

开启这个定时器

#if ENABLE_TIMER

		static uint64_t prev_tsc = 0, cur_tsc;
		uint64_t diff_tsc;

		cur_tsc = rte_rdtsc();
		diff_tsc = cur_tsc - prev_tsc;
		if (diff_tsc > TIMER_RESOLUTION_CYCLES)
		{
			rte_timer_manage();
			prev_tsc = cur_tsc;
		}

#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值