DPDK实现数据包的收发

DPDK(Data Plane Development Kit)允许应用程序直接访问网卡,从网卡数据缓冲区获取数据。在开始之前,需要安装并配置DPDK,可以参考以下基本步骤准备:
环境准备
1.配置hugepage
参考文章:hugepage配置
2.安装DPDK
参考文章:dpdk无坑环境搭建
示例代码
initConfiguration.h

#include <rte_mbuf.h>
#include <rte_ethdev.h>
#include <rte_eal.h>
#include <rte_mempool.h>

#define NB_MBUFS 1024
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32


static struct rte_mempool *mbuf_pool;   //
static uint16_t portid;    //
static const struct rte_eth_conf port_conf_default ={
    .rxmode={.max_lro_pkt_size=RTE_ETHER_MAX_LEN}
};

static int initPort();
static int EALinit(int argc,char *argv[]);
static int lcore_main(__rte_unused void *arg);
static void recv_packages(uint16_t portid);
static void send_packages(uint16_t portid);

PORTmanage.c

#include "initConfiguration.h"

static int initPort()
{
    int rte;
    uint16_t nrq = 1024;
    uint16_t ntq = 1024;
    struct rte_eth_conf port_conf = port_conf_default;
    uint16_t rx_rings=1;
    uint16_t tx_rings=1;
    if (!rte_eth_dev_is_valid_port(portid))
    {
        printf("no valid port\n");
        return -1;
    }
    //configure the ethernet device
    rte=rte_eth_dev_configure(portid,rx_rings,tx_rings,&port_conf);
    if (rte!=0) {
        printf("configure ethernet device failed\n");
        return rte;
    }
    //set up rx queue
    for (int  i = 0; i < rx_rings; i++)
    {
        rte = rte_eth_rx_queue_setup(portid,i,nrq,rte_eth_dev_socket_id(portid),NULL,mbuf_pool);
    }
    if (rte!=0)
    {
        printf("set up rx queue failed\n");
        return rte;
    }
    //set up tx queue
    for (int  i = 0; i < tx_rings; i++)
    {
        rte = rte_eth_tx_queue_setup(portid,i,ntq,rte_eth_dev_socket_id(portid),NULL);
    }
    if (rte!=0)
    {
        printf("set up tx queue failed\n");
        return rte;
    }
    //start ethernet port
    rte = rte_eth_dev_start(portid);
    if (rte<0)
    {
        printf("rte_eth_dev_start failed\n");
        return rte;
    }
    return 0;
    /* Enable RX in promiscuous mode for the Ethernet device */
    //rte_eth_promiscuous_enable(portid);
    
}

packageManage.c

#include "initConfiguration.h"

static void recv_packages(uint16_t portid)
{
    uint16_t nb_rx;
    struct rte_mbuf *bufs[BURST_SIZE];
    nb_rx=rte_eth_rx_burst(portid,0,bufs,BURST_SIZE);
    if (unlikely(nb_rx==0))
    {
       return;
    }
    for (uint16_t i = 0; i < nb_rx; i++)
    {
        struct rte_mbuf *mbuf = bufs[i];
        //read eth header
        struct rte_ether_hdr *eth_hdr;
        eth_hdr = rte_pktmbuf_mtod(mbuf,struct rte_ether_hdr*);

        rte_pktmbuf_free(mbuf);
    }
    
}


static void send_packages(uint16_t portid)
{
    struct rte_mbuf *bufs[BURST_SIZE];
    uint16_t nb_tx;
     for (uint16_t i = 0; i < BURST_SIZE; i++) {
        struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
        if (unlikely(mbuf == NULL)) {
            printf("Failed to allocate mbuf\n");
            return;
        }
        /* Fill Ethernet header */
        struct rte_ether_hdr *eth_hdr;
        eth_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
        rte_ether_addr_copy(&eth_hdr->src_addr, &eth_hdr->dst_addr);
        eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);

        /* Set packet length */
        mbuf->data_len = RTE_ETHER_HDR_LEN;
        mbuf->pkt_len = mbuf->data_len;

        bufs[i] = mbuf;
    }
     /* Transmit packets */
    nb_tx = rte_eth_tx_burst(portid, 0, bufs, BURST_SIZE);
    if (unlikely(nb_tx < BURST_SIZE)) {
        printf("Failed to transmit all packets\n");
        for (uint16_t i = nb_tx; i < BURST_SIZE; i++) {
            rte_pktmbuf_free(bufs[i]);
        }
    }

}

EALmanage.c

#include "initConfiguration.h"

int EALinit(int argc,char *argv[])
{
    uint16_t nb_ports;
    int rte = rte_eal_init(argc,argv);
    if (rte<0)
    {
        rte_exit(EXIT_FAILURE,"rte_eal_init failed\n");
    }
    nb_ports = rte_eth_dev_count_avail();
    if (nb_ports==0)
    {
        rte_exit(EXIT_FAILURE,"no available ports\n");
    }
    mbuf_pool = rte_pktmbuf_pool_create("mbufpool",NB_MBUFS*nb_ports,MBUF_CACHE_SIZE,0,RTE_MBUF_DEFAULT_BUF_SIZE,rte_socket_id());
    if (mbuf_pool==NULL)
    {
        rte_exit(EXIT_FAILURE,"rte_pktmbuf_pool_create failed\n");
    }
    RTE_ETH_FOREACH_DEV(portid);
        if (initPort()!=0)
        {
             rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n", portid);
        }
     /* Launch per-lcore function on every lcore */
    rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
    unsigned lcore_id;
    RTE_LCORE_FOREACH_WORKER(lcore_id) {
        rte_eal_wait_lcore(lcore_id);
    } 
    /* Cleanup */
    RTE_ETH_FOREACH_DEV(portid) {
        printf("Closing port %d...", portid);
        rte_eth_dev_stop(portid);
        rte_eth_dev_close(portid);
        printf(" Done\n");
    }
}


static int lcore_main(__rte_unused void *arg)
{
     RTE_ETH_FOREACH_DEV(portid) {
            recv_packages(portid);
            send_packages(portid);
            }
}

DPDK代码解析
结构体
1.rte_ether_hdr

struct rte_ether_hdr {
	struct rte_ether_addr dst_addr;
	struct rte_ether_addr src_addr; /
	rte_be16_t ether_type;
} __rte_aligned(2);
 //用于表示以太网帧头的结构体
  • dst_addr 表示以太网帧的目标 MAC 地址。
  • src_addr 表示以太网帧的源 MAC 地址。
  • ether_type 用于指定以太网帧的协议类型。


1.rte_pktmbuf_mtod

#define rte_pktmbuf_mtod(m, t) ((t)((m)->buf_addr + (m)->data_off))
//用于从内存缓冲区(mbuf)中获取数据包的指针
  • m: 一个指向 rte_mbuf 结构体的指针,表示数据包缓冲区。
  • t:目标数据类型的指针类型,用于指定你希望将缓冲区中的数据转换成的类型。
  • 返回值:返回一个指向指定类型的数据指针,通过它你可以访问 mbuf 中的实际数据
  1. rte_cpu_to_be_16
#define rte_cpu_to_be_16(x) rte_bswap16(x)
//用于将 16 位整数从主机字节序(CPU 本地字节序)转换为网络字节序(大端字节序)

函数
1.rte_pktmbuf_free

static inline void rte_pktmbuf_free(struct rte_mbuf *m);
//用于释放之前通过 DPDK 内存池(mempool)分配的内存缓冲区(mbuf)
  • m: 一个指向 rte_mbuf 结构体的指针,它表示要释放的 mbuf 缓冲区。

2.rte_pktmbuf_alloc

static inline struct rte_mbuf *rte_pktmbuf_alloc(struct rte_mempool *mp);
//用于从内存池中分配一个新的内存缓冲区
  • mp: 一个指向 rte_mempool 结构体的指针,表示要从中分配 mbuf 的内存池。

3.rte_ether_addr_copy

static inline void
rte_ether_addr_copy(const struct rte_ether_addr *__restrict ea_from,struct rte_ether_addr *__restrict ea_to);
//用于复制以太网地址的函数。这个函数可以安全地将一个以太网地址复制到另一个地址结构中
		    
  • ea_from:复制的以太网地址
  • ea_to:复制到的位置
    4.rte_eth_rx_burst
static inline uint16_t rte_eth_rx_burst(uint16_t port_id, uint16_t queue_id, struct rte_mbuf **rx_pkts, const uint16_t nb_pkts);
//用于从指定网卡的接收队列中接收数据包
  • port_id:表示网卡端口的标识符。端口 ID 是从 0 开始的序号,用于标识当前应用程序中可用的物理或虚拟网卡。
  • queue_id:指定要接收数据包的接收队列。每个网卡端口可以有多个接收队列以支持多核环境。
  • rx_pkts:指向 rte_mbuf指针数组的指针,用于存储接收到的数据包
  • nb_pkts:表示希望接收的最大数据包数量,即 rx_pkts 数组的大小。
  • 返回值:返回实际接收到的数据包数量

5.rte_eth_tx_burst

static inline uint16_t rte_eth_tx_burst(uint16_t port_id, uint16_t queue_id,struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
//将数据包从用户空间传输到网卡
  • port_id:表示要发送数据包的网卡端口 ID。此端口 ID 是从 0 开始的序号,用于标识当前应用程序中可用的物理或虚拟网卡。
  • queue_id:指定发送队列 ID。每个网卡端口可以有多个发送队列,以支持多核环境.
  • tx_pkts:指向 rte_mbuf 指针数组的指针
  • nb_pkts:希望发送的数据包数量,即 tx_pkts 数组的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值