dpdk有关kni的练习:
kni模块可以实现把不用dpdk处理的相关协议报文扔给内核处理,并接受返回结果用dpdk接管的网卡进行发送出去。
1:kni概述(内核网卡接口Kernel NIC Interface)
dpdk提供了kni模块,实现了dpdk应用程序与内核数据的交互。
dpdk接管网卡数据后,内核其实就无法识别到网卡数据了。
dpdk提供了kni模块,用虚拟网络适配器(模拟了一个网卡),实现了dpdk应用程序和内核网络模块的交互。
所以kni的模块主要就是提供了一个中间虚拟网络适配器,把dpdk不处理的网络包过滤给内核协议栈处理,并获取返回报文从网卡发出。
**dpdk通过net_device(网卡驱动)实现了一个虚拟网络适配器vEth,**即kni模块。
2:dpdk kni模块测试
2.1:运行脚本,执行45,插入kni模块
2.2:kni模块生成一个虚拟的vEth网络适配器
和内核的eth网络适配器相比较,传统的内核的网络适配器,/dev/下是没有设备文件的,不允许用户层操作。
dpdk的kni模块,在/dev/kni生成 一个文件,可以通过操作这个文件,实现与这个网络适配器的交互。(即:kni模块由一个虚拟的网络适配器和/dev/kni文件构成)
提供了接口,通过操作kni文件,实现了与虚拟网卡的交互,虚拟网卡正常与内核协议栈交互,即实现了dpdk与内核协议栈的交互。
2.3:kni测试代码实现
如实现用kni处理除了udp/tcp外的所有其他协议的报文:
这里可以测试arp的逻辑,在进行udp和tcp通信前,通信协议其实是需要arp的辅助的,可以通过发送udp报文,观察现象:
注意:arp的业务逻辑如果全部交给内核网络协议栈,dpdk接管udp和tcp时,需要适配arp表,不然也会有问题。
//初始化逻辑
//kni模块的初始化,打开kni设备 /dev/kni
if (-1 == rte_kni_init(gDpdkPortId)) {
rte_exit(EXIT_FAILURE, "kni init failed\n");
}
//有关网卡适配器的初始化 如接管网卡也要做这个,初始化网卡对应的内存缓冲区等 返回一个全局id
//以及一些设置 如开启混杂模式rte_eth_promiscuous_enable(gDpdkPortId);
ng_init_port(mbuf_pool);
// 返回一个rte_kni结构的指针,供 rte_kni_tx_burst和rte_kni_tx_burst使用
// rte_kni结构中需要构造: rte_kni_conf, 内存池, rte_kni_ops
// 调用rte_kni_alloc(mbuf_pool, &conf, &ops); 获取结构
// rte_kni_conf配置网卡名,mtu,mac地址等,从ng_init_port接口中获取
// rte_kni_ops设置网卡操作的一些回调函数,如执行ifconfig eth0 up/down时用
global_kni = ng_alloc_kni(mbuf_pool);
//使用逻辑
//对原先的测试代码中dpdk arp业务处理进行屏蔽,使用kni交给内核网络协议栈处理
//rte_kni_tx_burst(global_kni, mbufs, num_recvd); 放入kni中,执行ifconfig等命令也需要这个接口
//rte_kni_handle_request(global_kni); 这个接口才会触发global_kni中设置的一些回调函数的处理
//rte_kni_rx_burst()对应接收接口
2.4:测试代码
启动测试代码,ifconfig -a 可以看到增加了一个vEth0 的网络适配器。
在su用户下执行ifconfig vEth0 192.168.0.119 up 配置ip及启动端口。
hlp@ubuntu:/$ ll /dev/kni
crw------- 1 root root 10, 53 Dec 26 14:51 /dev/kni
ifconfig -a
ifconfig vEth0 192.168.0.109 up
ifconfig #已经可以看到生效的vEth0 以及相关配置和正常网卡无差异
vEth0 Link encap:Ethernet HWaddr 00:0c:29:20:c7:0d
inet addr:192.168.0.109 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:2 errors:0 dropped:1 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:416 (416.0 B) TX bytes:0 (0.0 B)
使用tcpdump -i vEth0抓 vEth0 网卡上的包,用udp连接或者ping命令触发,可以看到已经能接收到arp相关的包。(这里只是简单测试流程通)
root@ubuntu:/# tcpdump -i vEth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vEth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:11:32.772637 ARP, Request who-has 192.168.0.109 tell 192.168.0.105, length 46
15:11:32.950109 IP6 fe80::19f7:d1c:4534:36df.63932 > ff02::c.1900: UDP, length 146
15:11:33.766936 ARP, Request who-has 192.168.0.109 tell 192.168.0.105, length 46
15:11:34.766266 ARP, Request who-has 192.168.0.109 tell 192.168.0.105, length 46
15:11:35.774578 ARP, Request who-has 192.168.0.109 tell 192.168.0.105, length 46
15:11:36.019082 IP6 fe80::19f7:d1c:4534:36df.63932 > ff02::c.1900: UDP, length 146
2.5:初步测试后的优化
2.5.1:用命令开启混杂模式,和关闭混杂模式
ifconfig eth0 promisc 开启混杂模式
ifconfig eth0 -promisc 关闭混杂模式
2.5.2:加载rte_kni模块时,如果不指定参数
默认会启动线程,专门处理接收报文
默认是禁止回环模式,也就是dpdk通过kni写入内核,但是无法从内核中获取到结果数据
可以通过加载rte_kni模块时,带参数设置kni内核模式(内存使用(io_mode)、线程(kthread_mode),链路设置(carrier))等
2.5.3:kni可以写入内核但是收不到内核回复数据
需要设置链路状态的开与关 carrier=on carrier=off
1:insmod kmod/rte_kni.ko carrier=on
2:通过dpdk提供的函数接口进行设置 :rte_kni_update_link()
===>需要专门启动一个线程对其进行关注写入。
3:echo 1 >/sys/devices/virtual/net/vEth0/carrier 写入
2.5.4:可以利用tshark监听网卡进行数据监控验证
tshark -i vEth0 tcp
tshark -i vEth0 udp
tshark -i vEth0 icmp ===》然后使用ping命令,观察有icmp报文请求以及回复。
2.5.5:注意架构和内存泄漏
这里涉及网卡接收到得所有格式得报文,可以多线程+缓存进行协调控制。
同时,可以使用htop进行内存监控。
3:总结:
kni模块时dpdk提供得一个模块,可以供用户应用程序和内核进行交互。(虚拟网络适配器+一个设备文件的方式)
需要配置虚拟网络适配器,配置ip和打开或者关闭动作(ifconfig vEth0 192.168.0.109 hw ether 00:0c:29:20:c7:0d up)
需要设置网卡支持混杂模式,需要设置kni支持链路回环(carrier)
用到的一些指令:tcpdump, htop, tshark,ifconfig
注意设置kni时,arp对udp和tcp报文的影响(arp表的维护)
==>dpdk example下有kni的测试demo,可以参考代码理解。
相关demo源码和课程:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂 (qq.com)