内核组件架构
- rte_eal+libc:内存的统一组织管理者,但是在这它不只是做内存工作。
- librte_malloc:对外提供分配释放内存的API,分配的内存都是rte_eal中所管理的内存。
- librte_ring:提供无锁队列,它使用了rte_eal管理的内存。
- librte_mempool:利用rte_eal管理的内存和rte_ring提供内存池的功能。
- librte_mbuf:提供dpdk应用程序数据存储释放操作的一系列接口。
- librte_timer:提供时间操作的接口,主要用于各种服务间同步作用。
启动抓包程序之前,首先要利用dpdk sdk中dpdk端口查看和绑定工具pci_unbind进行抓包端口绑定。然后,申请huge页并挂载huge分区。应用dpdk进行数据包捕获时,主要利用rte_eal_init()
、init_port()
和init_mbuf_pools()
进行参数、端口及内存缓冲区初始化,利用do_packet_forwarding_bypass()
函数中的一个for循环不断查询各端口,在端口上接收数据包。然后采用自定义的包处理函数,对每个到达的数据包进行相应的处理。此外,可以设置信号对各物理端口加以控制以及用于输出数据包捕获的状态。
数据包捕获的整体流程图如图所示。
数据包捕获基本流程:
1、初始化EAL环境,函数rte_eal_init(dpdk_argc,dpdk_argv)
是对dpdk参数的处理,初始化EAL,初始化失败则退出程序。
2、初始化内存池等,利用函数init_mbuf_pools()
初始化dpdk的mbuf与pool队列。
3、初始化接口,函数init_port(ports->id[i])
用来初始化端口配置、查看端口状态。主要有三部分工作:
- 首先配置接口的属性-
rte_eth_dev_configure()
,如支持大帧下的包长检查等,然后很重要的是配置发送和接收队列。 - 当以上的配置完整以后,接下来就是启动队列-
rte_eth_rx_queue_setup()
。 - 最后一步就是当都准备好了以后,就可以启动设备了-
rte_eth_dev_start()
,同时还可以使能混杂模式等设置。有时也会设置接收和发送的回调函数-rte_eth_add_rx_callback()
。
4、函数rte_eth_rx_burst(port_num,0,buf,PACKET_READ_SIZE)
用来从端口接收数据包。若是多端口收包,可以用一个for循环对抓包端口的状态监控,来循环接收数据包。
5、自定义函数process_packets()
完成对每一个数据包的初步处理,用来分析数据包所属协议类型和端口,并进行队列分发。
6、启动所有核上的线程rte_eal_mp_remote_launch()
。
至此,包处理函数已完成对数据包的物理层、链路层、网络层信息的初步解析,即能够区分出数据包的MAC地址、目的或源IP、端口号、TCP/UDP类型等信息。为了提高数据包应用层数据解析速率,在每个数据包队列分发前需要采用五元组做哈希进行队列区分,这样做达到了数据包并行解析的目的,增加了单位时间内数据包处理个数,提升了系统整体效率。