解决dpdk reserve的内存返回的虚拟地址和iova地址一样的问题

1. 背景:

在ubuntu20.04上用dpdk API: 

rte_memzone_reserve_aligned("L1L2_PCIE_MEMORY", 1.5*1024*1024*1024,
                         rte_socket_id(), RTE_MEMZONE_1GB|RTE_MEMZONE_IOVA_CONTIG, 
                         RTE_CACHE_LINE_SIZE);

分配1.5G 内存.

服务器和mvl PCIE卡通过这个内存进行交互.

正常情况下:

服务器使用dpdk返回的虚拟地址,  mvl pcie卡上用dpdk 返回的iova地址, 然后调用DPI 的API 可以实现数据的上传和下载.

期望的虚拟地址和iova地址是不一样的. 但是实际上打印是一样的:

这种情况下表示把虚拟地址作为iova 地址. 二者一样.

那传给mvl pcie卡的其实是一个虚拟地址. 导致mvl 卡把数据push到了未知的地址.

因为这个功能在之前测试过的,两个地址不应该一样, 所以打印日志看下哪不一样: 加参数: --log-level=eal,8

对比之前的日志和现在不行的日志, 如下:

<think>我们使用DPDK来发送报文。这里假设用户需要的是一个简单的DPDK发送报文的程序框架,包括初始化、配置端口、创建报文并发送。 注意:实际中需要根据具体需求构造报文(如以太网头、IP头、传输层头等),并设置目标地址端口。 以下是一个完整的示例,发送UDP报文到指定IP端口。 步骤: 1. 初始化DPDK环境 2. 配置发送端口 3. 构造UDP报文(包括以太网头、IP头、UDP头) 4. 设置目标MAC地址(这里假设已知目标MAC,或者需要通过ARP获取,但本例中简化处理) 5. 发送报文 注意:在实际应用中,目标MAC地址通常需要通过ARP解析获得,但为了简化,我们假设已知目标MAC地址或使用广播地址。 我们将编写一个程序,使用DPDK发送一个固定的UDP报文到指定的目标IP端口。 这里我们使用eth5作为发送端口。 由于用户没有指定具体内容,我们发送一个固定的负载"Hello DPDK!"。 注意:需要根据实际情况修改以下内容: - 目标MAC地址(这里使用广播地址FF:FF:FF:FF:FF:FF,实际应使用正确的目标MAC) - 源MAC地址(使用发送端口的MAC地址) - 源IP地址(eth5的IP地址) - 目标IP地址端口 另外,我们使用硬件卸载功能(如果支持)来计算IPUDP校验。 下面是完整代码: </think>以下是如何使用DPDK发送报文到指定目标地址端口的完整实现方案: ```cpp #include <rte_eal.h> #include <rte_ethdev.h> #include <rte_mbuf.h> #include <rte_ether.h> #include <rte_ip.h> #include <rte_udp.h> #include <rte_tcp.h> #include <cstdio> #include <arpa/inet.h> #define TX_RING_SIZE 1024 #define NUM_MBUFS 8191 #define MBUF_CACHE_SIZE 250 #define PKT_BURST_SIZE 32 #define PACKET_LENGTH 64 // 报文总长度 // 目标配置 struct target_config { uint8_t dst_mac[RTE_ETHER_ADDR_LEN]; // 目标MAC地址 uint32_t dst_ip; // 目标IP地址(网络字节序) uint16_t dst_port; // 目标端口(主机字节序) uint32_t src_ip; // 源IP地址(网络字节序) uint8_t protocol; // IPPROTO_UDP/ICMP/TCP }; // 默认配置(替换为实际值) target_config config = { .dst_mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, // 目标MAC .dst_ip = htonl(0xC0A80101), // 192.168.1.1 .dst_port = 8080, // 目标端口 .src_ip = htonl(0xC0A80102), // 源IP 192.168.1.2 .protocol = IPPROTO_UDP // 默认UDP协议 }; // 获取端口ID uint16_t get_port_by_name(const char *dev_name) { uint16_t port_id; RTE_ETH_FOREACH_DEV(port_id) { struct rte_eth_dev_info dev_info; rte_eth_dev_info_get(port_id, &dev_info); if (strcmp(dev_info.device->name, dev_name) == 0) { return port_id; } } return RTE_MAX_ETHPORTS; } int main(int argc, char *argv[]) { // 初始化DPDK环境 if (rte_eal_init(argc, argv) < 0) { rte_exit(EXIT_FAILURE, "DPDK环境初始化失败\n"); } // 获取发送端口ID(eth5) uint16_t tx_port = get_port_by_name("0000:04:00.0"); if (tx_port >= RTE_MAX_ETHPORTS) { rte_exit(EXIT_FAILURE, "无效发送端口\n"); } // 创建内存池 struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create( "TX_MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); if (!mbuf_pool) { rte_exit(EXIT_FAILURE, "内存池创建失败\n"); } // 端口配置(启用校验硬件卸载) struct rte_eth_conf port_conf = {}; port_conf.txmode.mq_mode = ETH_MQ_TX_NONE; port_conf.txmode.offloads = DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM; // 配置发送端口 if (rte_eth_dev_configure(tx_port, 0, 1, &port_conf) != 0 || rte_eth_tx_queue_setup(tx_port, 0, TX_RING_SIZE, rte_eth_dev_socket_id(tx_port), NULL) < 0) { rte_exit(EXIT_FAILURE, "发送端口配置失败\n"); } // 启动端口 if (rte_eth_dev_start(tx_port) < 0) { rte_exit(EXIT_FAILURE, "端口启动失败\n"); } // 获取本机MAC地址 struct rte_ether_addr src_mac; rte_eth_macaddr_get(tx_port, &src_mac); printf(">>> 开始发送报文: %s:%d -> %s:%d (协议: %s)\n", inet_ntoa(*(struct in_addr*)&config.src_ip), 0, // 源端口(应用层指定) inet_ntoa(*(struct in_addr*)&config.dst_ip), config.dst_port, config.protocol == IPPROTO_UDP ? "UDP" : "TCP"); uint64_t pkt_count = 0; const char payload[] = "Hello DPDK!"; while (true) { struct rte_mbuf *tx_bufs[PKT_BURST_SIZE]; // 分配并构造多个报文 for (int i = 0; i < PKT_BURST_SIZE; i++) { // 分配mbuf struct rte_mbuf *m = rte_pktmbuf_alloc(mbuf_pool); if (!m) continue; // 预留数据空间(包含所有头部) char *pkt_data = rte_pktmbuf_append(m, PACKET_LENGTH); if (!pkt_data) { rte_pktmbuf_free(m); continue; } memset(pkt_data, 0, PACKET_LENGTH); // 填充以太网头部 struct rte_ether_hdr *eth_hdr = (struct rte_ether_hdr *)pkt_data; rte_ether_addr_copy(&src_mac, &eth_hdr->s_addr); rte_ether_addr_copy((struct rte_ether_addr *)config.dst_mac, &eth_hdr->d_addr); eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); // 填充IP头部 struct rte_ipv4_hdr *ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1); ip_hdr->version_ihl = 0x45; // IPv4, 头部长度5*4=20字节 ip_hdr->total_length = rte_cpu_to_be_16(PACKET_LENGTH - sizeof(*eth_hdr)); ip_hdr->time_to_live = 64; ip_hdr->next_proto_id = config.protocol; ip_hdr->src_addr = config.src_ip; ip_hdr->dst_addr = config.dst_ip; // 设置硬件校验卸载标志 m->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM; ip_hdr->hdr_checksum = 0; // 根据协议填充传输层头部 char *payload_ptr = nullptr; if (config.protocol == IPPROTO_UDP) { struct rte_udp_hdr *udp_hdr = (struct rte_udp_hdr *)(ip_hdr + 1); udp_hdr->src_port = rte_cpu_to_be_16(12345); // 随机源端口 udp_hdr->dst_port = rte_cpu_to_be_16(config.dst_port); udp_hdr->dgram_len = rte_cpu_to_be_16( sizeof(struct rte_udp_hdr) + strlen(payload) + 1); payload_ptr = (char *)(udp_hdr + 1); m->ol_flags |= PKT_TX_UDP_CKSUM; udp_hdr->dgram_cksum = 0; } else if (config.protocol == IPPROTO_TCP) { struct rte_tcp_hdr *tcp_hdr = (struct rte_tcp_hdr *)(ip_hdr + 1); tcp_hdr->src_port = rte_cpu_to_be_16(12345); // 随机源端口 tcp_hdr->dst_port = rte_cpu_to_be_16(config.dst_port); tcp_hdr->data_off = 0x50; // 头部长度5*4=20字节 tcp_hdr->tcp_flags = RTE_TCP_SYN_FLAG; // SYN标志 payload_ptr = (char *)(tcp_hdr + 1); m->ol_flags |= PKT_TX_TCP_CKSUM; tcp_hdr->cksum = 0; } // 填充应用层数据 if (payload_ptr) { strncpy(payload_ptr, payload, strlen(payload) + 1); } tx_bufs[i] = m; } // 批量发送报文 uint16_t nb_tx = rte_eth_tx_burst(tx_port, 0, tx_bufs, PKT_BURST_SIZE); pkt_count += nb_tx; // 释放未成功发送的报文 for (int i = nb_tx; i < PKT_BURST_SIZE; i++) { rte_pktmbuf_free(tx_bufs[i]); } // 每秒打印统计信息 if (pkt_count % 100000 == 0) { printf("已发送 %lu 个报文 (速率: %.2f Mpps)\n", pkt_count, (double)nb_tx * 1000 / 1e6); } // 短暂休眠控制速率(实际生产环境应移除) rte_delay_us(100); } rte_eth_dev_stop(tx_port); return 0; } ``` ### 关键功能详解 #### 1. 报文构造流程图 ```mermaid graph TD A[分配mbuf内存] --> B[填充以太网头部] B --> C[填充IP头部] C --> D{协议类型} D -->|UDP| E[填充UDP头部] D -->|TCP| F[填充TCP头部] E --> G[填充应用数据] F --> G G --> H[设置硬件卸载标志] H --> I[加入发送队列] ``` #### 2. 核心数据结构配置 ```cpp // 目标配置示例 target_config config = { .dst_mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, // 目标MAC .dst_ip = htonl(0xC0A80101), // 目标IP 192.168.1.1 .dst_port = 8080, // 目标端口 .src_ip = htonl(0xC0A80102), // 源IP 192.168.1.2 .protocol = IPPROTO_UDP // 协议类型 }; ``` #### 3. 硬件卸载配置 ```cpp // 启用硬件校验卸载 port_conf.txmode.offloads = DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM; // 在mbuf中设置卸载标志 m->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM; // IP层 m->ol_flags |= PKT_TX_UDP_CKSUM; // UDP专用 m->ol_flags |= PKT_TX_TCP_CKSUM; // TCP专用 ``` ### 性能优化技巧 | 优化方法 | 实现代码 | 效果 | |----------|----------|------| | **批量发送** | `rte_eth_tx_burst(port, 0, tx_bufs, PKT_BURST_SIZE)` | 减少PCIe事务开销 | | **内存重用** | `rte_pktmbuf_reset(m)` | 避免重复分配内存 | | **向量化指令** | `-mavx2` 编译选项 | 加速内存操作 | | **零拷贝** | `rte_pktmbuf_prepend` | 避免数据复制 | | **NUMA绑定** | `rte_lcore_to_socket_id` | 减少跨节点访问 | ### 多协议支持实现 ```cpp // UDP报文构造 struct rte_udp_hdr *udp_hdr = (struct rte_udp_hdr *)(ip_hdr + 1); udp_hdr->src_port = rte_cpu_to_be_16(src_port); udp_hdr->dst_port = rte_cpu_to_be_16(config.dst_port); udp_hdr->dgram_len = rte_cpu_to_be_16(len); // TCP报文构造 struct rte_tcp_hdr *tcp_hdr = (struct rte_tcp_hdr *)(ip_hdr + 1); tcp_hdr->src_port = rte_cpu_to_be_16(src_port); tcp_hdr->dst_port = rte_cpu_to_be_16(config.dst_port); tcp_hdr->sent_seq = rte_cpu_to_be_32(seq_num); tcp_hdr->recv_ack = rte_cpu_to_be_32(ack_num); tcp_hdr->tcp_flags = flags; // ICMP报文构造 struct rte_icmp_hdr *icmp_hdr = (struct rte_icmp_hdr *)(ip_hdr + 1); icmp_hdr->icmp_type = RTE_IP_ICMP_ECHO_REQUEST; icmp_hdr->icmp_code = 0; ``` ### 典型应用场景 1. **网络性能测试工具**:实现类似pktgen的功能 2. **DDoS压力测试**:模拟高并发流量 3. **协议栈测试**:构造特殊报文测试网络设备 4. **时间敏感应用**:精确控制报文发送时间 5. **5G用户面功能**:实现UPF的数据面加速 ### 编译运行命令 ```bash # 编译 g++ -O3 -march=native -o dpdk_sender dpdk_sender.cpp \ -I${RTE_SDK}/${RTE_TARGET}/include \ -L${RTE_SDK}/${RTE_TARGET}/lib \ -lrte_eal -lrte_ethdev -lrte_mbuf -lrte_mempool -lrte_net # 运行 sudo ./dpdk_sender --lcores=1 -a 04:00.0 ``` ### 常见问题解决 ```markdown | 问题现象 | 可能原因 | 解决方案 | |--------------------------|---------------------------|------------------------------| | 发送失败(nb_tx=0) | 网卡队列未启动 | 检查`rte_eth_dev_start()`返回值 | | 校验错误 | 硬件卸载未正确配置 | 验证网卡能力并设置`ol_flags` | | 报文内容不正确 | 头部字段字节序错误 | 使用`rte_cpu_to_be_16/32`转换 | | 性能达不到线速 | 内存分配瓶颈 | 使用内存池预分配mbuf | | 接收方未收到报文 | MAC地址错误/ARP未解析 | 确认目标MAC正确 | ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值