基于dpdk实现一个存数据的小例子

学习手撸dpdk,从写最简单的例子开始。

功能说明

数据包: ETHER HEADER | TYPE | LENGTH | VALUE

TYPE = 0x03   write req

TYPE = 0x04   write reply

收到write req的单播请求,保存value, 回复write reply消息。

 

测试客户端

利用python3 scapy库来构造符合规则的二层数据包

[root@localhost ~]# python3
Python 3.6.8 (default, Apr  2 2020, 13:34:55) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> from scapy.all import *
>>> pkt1 = Ether(dst='00:0C:29:0D:CD:6C', src='00:0c:29:0d:cd:62')/'\3\5'/"Hello" 
>>> pkt2 = Ether(dst='00:0C:29:0D:CD:6C', src='00:0c:29:0d:cd:62')/'\3\25'/"What is wrong with u" 
>>> 
>>> pkt1.show()
###[ Ethernet ]### 
  dst       = 00:0C:29:0D:CD:6C
  src       = 00:0c:29:0d:cd:62
  type      = LOOP
###[ Raw ]### 
     load      = '\x03\x05'
###[ Raw ]### 
        load      = 'Hello'

>>> sendp(pkt1, iface='ens160')
.
Sent 1 packets.
>>> pkt2.show()
###[ Ethernet ]### 
  dst       = 00:0C:29:0D:CD:6C
  src       = 00:0c:29:0d:cd:62
  type      = LOOP
###[ Raw ]### 
     load      = '\x03\x15'
###[ Raw ]### 
        load      = 'What is wrong with u'

>>> sendp(pkt2, iface='ens160')
.
Sent 1 packets.
>>> 
>>> 

运行结果

[root@localhost newproto]# ./build/ckproto -c 2 -n 4 
Hello proto

EAL: Detected 8 lcore(s)
EAL: Detected 1 NUMA nodes
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: No available hugepages reported in hugepages-1048576kB
EAL: Probing VFIO support...
EAL:   Invalid NUMA socket, default to 0
EAL:   Invalid NUMA socket, default to 0
EAL: Probe PCI driver: net_e1000_em (8086:10d3) device: 0000:0b:00.0 (socket 0)
EAL:   Invalid NUMA socket, default to 0
EAL: Probe PCI driver: net_e1000_em (8086:10d3) device: 0000:13:00.0 (socket 0)
EAL: No legacy callbacks, legacy socket not created
Port 0:  0000:0b:00.0  Mac address: 00:0C:29:0D:CD:6C

Port 1:  0000:13:00.0  Mac address: 00:0C:29:0D:CD:76

==Config done ==
service 1 running
Data save Len:5 data:Hello
Data save Len:21 data:What is wrong with u

抓包检查

recv: 03 05 hello

send: 04 05 hello

recv: 03 21 What is wrong with u

send: 04 21 What is wrong with u

 

代码

/*************************************************************************
Author: 小湿哥
Date:   Oct 7th, 2020
*************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/queue.h>
#include <signal.h>

#include <rte_memory.h>
#include <rte_memzone.h>
#include <rte_malloc.h>
#include <rte_launch.h>
#include <rte_tailq.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>
#include <rte_ether.h>
#include <rte_common.h>
#include <rte_ethdev.h>

#define USE_PORT 0
#define NUM_RX_QUEUES 1
#define NUM_TX_QUEUES 1

#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250

#define NUM_RX_DESC 1024
#define NUM_TX_DESC 1024
#define BURST_SIZE  32

#define MAX_PKT_BURST 32
#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
#define MEMPOOL_CACHE_SIZE 256

typedef struct global_config
{
    uint8_t local_mac[6];
    FILE* f;
} global_config_t;

static volatile bool force_quit;

static  struct rte_eth_conf port_conf_default;

static struct rte_eth_dev_tx_buffer *tx_buffer;

static global_config_t g_config;


static void print_mac(uint8_t* mac)
{
    printf("%02X:%02X:%02X:%02X:%02X:%02X",
        mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
}

static void print_eth_mac(unsigned int port_id)
{
    struct rte_ether_addr dev_eth_addr;
    rte_eth_macaddr_get(port_id, &dev_eth_addr);
    printf("Mac address: ");
    print_mac(dev_eth_addr.addr_bytes);
    printf("\n\n");
    if (port_id == USE_PORT) {
        memcpy(g_config.local_mac, dev_eth_addr.addr_bytes, 6);
    }
}

static inline int use_port_init(uint8_t port, struct rte_mempool *mbuf_pool)
{
    int ret = 0, i = 0;
    uint32_t lcore = 0;
    uint16_t rx_q = NUM_RX_QUEUES;
    uint16_t tx_q = NUM_TX_QUEUES;
    uint16_t rx_d = NUM_RX_DESC;
    uint16_t tx_d = NUM_TX_DESC;

    struct rte_eth_dev_info dev_info;

    uint8_t nb_ports = rte_eth_dev_count_avail();

    if (port < 0 || port >= nb_ports)
    {
        printf("port is not right \n");
        return -1;
    }

    port_conf_default.rxmode.max_rx_pkt_len = RTE_ETHER_MAX_LEN;
    struct rte_eth_conf port_conf = port_conf_default;

    
    rte_eth_dev_info_get(port, &dev_info);
    if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
    {
        port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MBUF_FAST_FREE;
    }
    
    ret = rte_eth_dev_configure(port, rx_q, tx_q, &port_conf);
    if (ret != 0)
    {
        printf("Port %d rx/tx queues config failed \n", port);
        return ret;
    }

    rte_eth_dev_adjust_nb_rx_tx_desc(port, &rx_d, &tx_d);

    for (i = 0; i < rx_q; i++) 
    {
        ret= rte_eth_rx_queue_setup(port, i, rx_d, rte_eth_dev_socket_id(port),NULL, mbuf_pool);
        if (ret < 0)
        {
            printf("rx queue desc congfig failed \n");
            return ret;
        }
    }

    for (i = 0; i < tx_q; i++) 
    {
        ret= rte_eth_tx_queue_setup(port, i, tx_d, rte_eth_dev_socket_id(port), NULL);
        if (ret < 0)
        {
            printf("tx queue desc congfig failed \n");
            return ret;
        }
    }
    
    tx_buffer = rte_zmalloc_socket("tx_buffer",
            RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
            rte_eth_dev_socket_id(USE_PORT));
    if (tx_buffer == NULL)
        rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
                USE_PORT);

    rte_eth_tx_buffer_init(tx_buffer, MAX_PKT_BURST);

    ret = rte_eth_dev_start(port);
    if (ret < 0)
    {
        printf("rte_eth_dev_start failed \n");
        return ret;
    }

    //rte_eth_promiscuous_enable(port);

    return 0;
}

static void
csignal_handler(int signum)
{
	if (signum == SIGINT || signum == SIGTERM) {
		printf("\n\nSignal %d received, preparing to exit...\n",
				signum);
		force_quit = true;
	}
}

static int data_save(uint8_t* data, uint32_t len)
{
    printf("Data save Len:%u data:%s\n", len, data);

    /*write to memory or disk, to be done */

    return 0;
}

int data_process(struct rte_mbuf *mbuf)
{
	struct rte_ether_hdr *eth;
    uint32_t len = 0;
	eth = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);

	memcpy(eth->d_addr.addr_bytes, eth->s_addr.addr_bytes, 6);
    memcpy(eth->s_addr.addr_bytes, g_config.local_mac, 6);

    uint8_t* data = (uint8_t*)(eth + 1);

    if (data[0] == 0x03) {      // write req
        len = data[1] & 0xff;
        data_save(&data[2], len);
        data[0] = 0x04;         // reply type
    }
    rte_eth_tx_buffer(USE_PORT, 0, tx_buffer, mbuf);
    return 0;
}

int service_lcore(void *arg)
{
    int i = 0;
    struct rte_mbuf *buffs[BURST_SIZE];
    uint16_t nb_rx;
    uint16_t nb_tx;
    uint16_t buf;
    uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
    uint32_t lcore_id = rte_lcore_id();
 	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S *
			BURST_TX_DRAIN_US;
    printf("service %u running\n", lcore_id);
    while (!force_quit) 
    {
		cur_tsc = rte_rdtsc();
		diff_tsc = cur_tsc - prev_tsc;
		if (unlikely(diff_tsc > drain_tsc)) {

			rte_eth_tx_buffer_flush(USE_PORT, 0, tx_buffer);
			prev_tsc = cur_tsc;
		}

        nb_rx = rte_eth_rx_burst(USE_PORT, 0, buffs, BURST_SIZE);

        if (unlikely(nb_rx == 0))
            continue;

        for (i= 0; i < nb_rx; i++)
        {
            struct rte_ether_hdr *eth;
            void *tmp;
            eth = rte_pktmbuf_mtod(buffs[i], struct rte_ether_hdr *);
            #if 0
            printf("Recv packet:\nDst Mac: "); 
            print_mac(eth->d_addr.addr_bytes);
            printf("\nSrc Mac: "); 
            print_mac(eth->s_addr.addr_bytes);
            printf("\n");
            #endif
            if (memcmp(eth->d_addr.addr_bytes, g_config.local_mac, 6) == 0) {
                data_process(buffs[i]);
            } else {
                rte_pktmbuf_free(buffs[i]);
            }
        }
    }

    //RTE_LOG(INFO, APP, "lcore %u exiting\n", lcore_id);
    return 0;
}

int main(int argc, char **argv)
{
    int ret = 0, i = 0;
    uint32_t lcore_id;
    struct rte_mempool *mbuf_pool;
    printf("Hello proto\n\n");

    ret = rte_eal_init(argc, argv);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "Cannot init EAL\n");
    argc -= ret;
    argv += ret;

    force_quit = false;
    signal(SIGINT, csignal_handler);
    signal(SIGTERM, csignal_handler);

    uint8_t nb_ports = rte_eth_dev_count_avail();
    for (i = 0; i < nb_ports; i++) 
    {
        char dev_name[RTE_DEV_NAME_MAX_LEN];
        rte_eth_dev_get_name_by_port(i, dev_name);
        printf("Port %d:  %s  ", i, dev_name);
        print_eth_mac(i);
    }

    mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
                            NUM_MBUFS * nb_ports,
                            MBUF_CACHE_SIZE, 0, 
                            RTE_MBUF_DEFAULT_BUF_SIZE,
                            rte_socket_id());

    if (mbuf_pool == NULL) {
        rte_exit(EXIT_FAILURE, "mbuf_pool create failed\n");
    }

    if (use_port_init(USE_PORT, mbuf_pool) != 0) {
        rte_exit(EXIT_FAILURE, "port init failed\n");
    }

    printf("==Config done ==\n");
	/* launch per-lcore init on every lcore */
	rte_eal_mp_remote_launch(service_lcore, NULL, CALL_MASTER);
	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
		if (rte_eal_wait_lcore(lcore_id) < 0) {
			ret = -1;
			break;
		}
	}

	printf("Closing port %d...", USE_PORT);
	rte_eth_dev_stop(USE_PORT);
	rte_eth_dev_close(USE_PORT);
	printf(" Done\n");
	printf("Bye...\n");

    return 0;
}

小结

基于dpdk写一个小例子,初始化和接口参数设置好,就可以收发二层包了。二层以上也可以为所欲为了。

而实际上多核心,多线程,多队列的绑定和数据调度这些要自己开发还得继续学习了,想要做到更好的框架,就不是那么容易了。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于DPDK(Data Plane Development Kit)实现服务端和客户端的主要步骤如下: 1. 环境准备:首先需要确保DPDK已经正确安装并配置好了开发环境,包括正确安装可用的网卡驱动程序、配置正确的内和共享内等。 2. 服务端实现:在服务端的代码中,需要使用DPDK提供的API来初始化DPDK,包括初始化设备、设置设备属性,以及创建和配置接收数据包的队列等。然后,服务端需要创建一个socket并绑定到指定的IP地址和端口上。接下来,使用DPDK提供的API接收和处理客户端发送的数据包。 3. 客户端实现:在客户端的代码中,同样需要使用DPDK提供的API来初始化DPDK,并创建一个socket。然后,通过socket数据包发送给服务端。客户端还可以通过DPDK提供的API来设置、修改发送数据包的属性,如目标IP地址和端口等。 4. 编译和运行:完成服务端和客户端的代码编写后,需要将代码编译成可执行文件。在编译时,需要使用DPDK提供的工具和指定相关的编译参数,如指定DPDK的路径、调整内核参数等。最后,将生成的可执行文件分别运行在服务端和客户端的主机上。 基于DPDK实现服务端和客户端可以提供高性能的网络连接和数据交互,减少了网络的延迟和负载。同时,DPDK还提供了丰富的API和功能,如多核支持、零拷贝等,可以进一步优化网络性能和提升系统吞吐量。因此,在需要处理大量网络数据流的场景下,基于DPDK实现服务端和客户端是一个很好的选择。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值