一些工具
工具 | 用途 | 版本 |
---|---|---|
Ubuntu虚拟机 | 运行dpdk代码 | 16 Server |
vmware | 虚拟机软件 | 17 |
Source Insight | 阅读代码、编写代码 | |
NetAssist | 模拟发包 | 5.0.14 |
xshell | 远程连接 | 8(free for home/school) |
wireshark | 抓包,看包到底发出去了没有 | 4.2.4 |
Samba | 在虚拟机里装,共享文件 |
代码
// 存放位置:share/dpdk-stable-19.08.2/examples/recv_send/recv.c
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <arpa/inet.h>
#include <stdio.h>
// 内存池大小,4096-1的含义是超过了4K就放别处
#define NUM_MBUFS (4096-1)
#define BURST_SIZE 32
int gDpdkPortId = 0;
static const struct rte_eth_conf port_conf_default = {
.rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};
// 设置网口接收数据,关联网口和dpdk程序
static void ng_init_port(struct rte_mempool *mbuf_pool) {
// 1. 检测端口是否可用 (此处要运行dpdk的setup.sh,绑定端口)
uint16_t nb_sys_ports= rte_eth_dev_count_avail(); //
if (nb_sys_ports == 0) {
rte_exit(EXIT_FAILURE, "没找到支持的eth端口\n");
}
// 获取 eth0 原生信息,方便后面用
struct rte_eth_dev_info dev_info;
rte_eth_dev_info_get(gDpdkPortId, &dev_info);
const int num_rx_queues = 1;//最大可用8,在此用1
const int num_tx_queues = 0;//最大可用8,在此不用,也就是没有发送数据包的中断。因为本文件是recv,只接收不发送
struct rte_eth_conf port_conf = port_conf_default;
rte_eth_dev_configure(gDpdkPortId, num_rx_queues, num_tx_queues, &port_conf);
if (rte_eth_rx_queue_setup(gDpdkPortId, 0 , 128, //0号接收队列共能囤积128条数据
rte_eth_dev_socket_id(gDpdkPortId),NULL, mbuf_pool) < 0) {
rte_exit(EXIT_FAILURE, "不能启动RX队列\n");
}
// 启动
if (rte_eth_dev_start(gDpdkPortId) < 0 ) {
rte_exit(EXIT_FAILURE, "无法启动\n");
}
rte_eth_promiscuous_enable(gDpdkPortId);
}
int main(int argc, char *argv[]) {
// 初始化,主要是检查hugepage的设置,内存、cpu的分配等。就是在/usertools/dpdk-setup.sh中的一通设置
if (rte_eal_init(argc, argv) < 0) {
rte_exit(EXIT_FAILURE, "Error with EAL init\n");
}
// 内存池
struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", NUM_MBUFS,
0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL) {
rte_exit(EXIT_FAILURE, "无法创建内存池\n");
}
ng_init_port(mbuf_pool);
while (1) {
struct rte_mbuf *mbufs[BURST_SIZE]; //数组指针, mbufs尚未分配实际空间,空间从内存池来
unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId, 0, mbufs, BURST_SIZE);// 接收的是以太网数据帧
// 参数1:端口<注意此端口不是tcp/udp的端口号,而是网络适配器eth0的端口>
//参数2:从哪个队列接收
//参数3:接收后放到哪
if (num_recvd > BURST_SIZE) {
rte_exit(EXIT_FAILURE, "接收出错,溢出\n");
}
unsigned i = 0;
for (i = 0;i < num_recvd;i ++) {
struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(mbufs[i], struct rte_ether_hdr*);// rte_pktmbuf_mtod是宏定义。参数1:内存地址 参数2:转换成什么类型
//// ehdr就是拿出的以太网数据
// 此处可看到对接口处理函数封装的已经很完善了
if (ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
// 如果不是ip协议,跳过
continue;
}
// 如果是ip协议,取出
struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_ipv4_hdr *,
sizeof(struct rte_ether_hdr));
if (iphdr->next_proto_id == IPPROTO_UDP) {
struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr + 1);
uint16_t length = ntohs(udphdr->dgram_len); //经验:网络字节序2字节以上必须转,不管实际是什么序
*((char*)udphdr + length) = '\0';
struct in_addr addr;
addr.s_addr = iphdr->src_addr;
printf("src: %s:%d, ", inet_ntoa(addr), udphdr->src_port);
addr.s_addr = iphdr->dst_addr;
printf("dst: %s:%d, %s length=%d\n", inet_ntoa(addr), udphdr->dst_port,
(char *)(udphdr+1), length);
rte_pktmbuf_free(mbufs[i]);
}
}
}
}
接收成功需保证以下几个事情
- EAL初始化成功(不然直接报错)
- 代码中,使用
rte_eth_promiscuous_enable()
函数,用于启用网络端口的混杂模式。 - NetAssist中,本地主机地址(就是发送地址)选那个跟eth0ip地址在一个网段中的。比如我的eth0信息为
inet addr:192.168.3.20 Bcast:192.168.3.255 Mask:255.255.255.0
,那么本地主机地址就选192.168.3.xxx的。如果选了其他的,可以用wireshark检测,包是发不出的,试过了。 - windows的apr表中,要准备好一条表项,是在192.168.3.xxx之下的,
192.168.3.20 00-0c-29-14-56-cf 静态
。添加命令:(cmd管理员)arp -s 192.168.3.20 00-0c-29-14-56-cf
总的大致流程是
(下载下来dpdk源码之后)
-
/usertools/dpdk-setup.sh,运行39,等几分钟。
-
在/example下新建目录,叫send_recv,在里面编写代码
-
从/example其他目录下cp一个Makefile到/example/send_recv,改头两行,一个是目标的名字,一个是源文件名称
- 进行一些绑定:/usertools/dpdk-setup.sh,运行43~49,在46、47设置hugepage中设置参数,都写512即可
- 设置两个环境变量
export RTE_SDK=/home/king/share/dpdk/dpdk-stable-19.08.2
。在/example/send_recv执行make,如果代码上有一些报错挨个解决
export RTE_TARGET=x86_64-native-linux-gcc - 运行/example/send_recv/build/dpdk-recv.c,其中dpdk-recv.c是Makefile中给目标起的名字。
- 如果初始化成功了,就会进入不断接收数据包的阶段。此时先退出,检查windows的arp表后,用NetAssist发数据包测试。
结果
# ./build/dpdk_recv
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: Probing VFIO support...
EAL: VFIO support initialized
EAL: PCI device 0000:02:01.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 8086:100f net_e1000_em
EAL: PCI device 0000:02:06.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 8086:100f net_e1000_em
EAL: PCI device 0000:03:00.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 15ad:7b0 net_vmxnet3
EAL: PCI device 0000:0b:00.0 on NUMA socket -1
EAL: Invalid NUMA socket, default to 0
EAL: probe driver: 15ad:7b0 net_vmxnet3
src: 192.168.3.19:47138, dst: 192.168.3.20:47138, 你好,我的冰糖雪梨 length=35