Pktgen-DPDK
前言
最近在做NFV相关的优化工作。在进行优化过程中需要对优化结果进行实时测试,以来确定优化手段是否有效果。由于公司并没有专业的发包机。而传统的netperf/iperf在10G场景下,64的小包很难发到限速。所以转而寻找其他工具来进行代替。刚好遇到了DPDK-Pktgen这个工具,完美的解决了我遇到的问题。
DPDK-Pktgen的安装
DPDK-Pktgen其实就是DPDK的一个应用,它类似于linux原生的pktgen,通过自己构造数据包,然后发送。而DPDK-Pktgen做的更强大,他可以通过用lua脚本或者json编辑自己的测试过程,同时输出自己关心的数据,比如发送,接收的数据包数量,流量带宽等等。
这里先简单介绍下安装DPDK-Pktgen DPDK-Pktgen的安装和DPDK的其他应用其实是一样的。 首先需要安装DPDK。这个就不在赘述了。
export RTE_SDK=<installDir>/Pktgen-DPDK/dpdk
export RTE_TARGET=x86_64-pktgen-linuxapp-gcc
然后
cd examples/pktgen
make
这样就编译完成了,在目录app/build/pktgen 就是编译出来的程序。
DPDK-Pktgen的使用
DPDK-Pktgen可以自己定义数据包的发送方式 下面就是使用的一个实例,
./app/build/pktgen -c f -n 3 --proc-type auto --socket-mem 256,256 -- -T -P -m "[1:3].0, [2:4].1" -f test/set_seq.lua
-c 是指选择的core的掩码,f等于1111也就是选择 1、2 、3、4 core;
--proc-type 选择的auto ,如果是当前系统第一执行的dpdk相关的程序,选择primary模式,如果是第二是secondary 模式;
--file-prefix pg 设置/mnt/huge/内存分配模块的文件名前缀;
-P 使能网络混装模式,
-m "2.0, 3.1" 是指一个矩阵模型,2.0是指,在2号lcore上绑定的端口0 , 3.1是指在lcore3上绑定端口1
-f test/set_seq.lua 导入pktgen的执行配置文件;在执行pktgen时,利用配置产生数据包;
这里对于数据包的发送就是在set_seq.lua中进行描述的。
local seq_table = { -- entries can be in any order
["eth_dst_addr"] = "0011:4455:6677",
["eth_src_addr"] = "0011:1234:5678",
["ip_dst_addr"] = "10.12.0.1",
["ip_src_addr"] = "10.12.0.1/16", -- the 16 is the size of the mask value
["sport"] = 9, -- Standard port numbers
["dport"] = 10, -- Standard port numbers
["ethType"] = "ipv4", -- ipv4|ipv6|vlan
["ipProto"] = "udp", -- udp|tcp|icmp
["vlanid"] = 1, -- 1 - 4095
["pktSize"] = 128 -- 64 - 1518
};
-- seqTable( seq#, portlist, table );
pktgen.seqTable(0, "all", seq_table );
pktgen.set("all", "seqCnt", 1);
可以看到lua文件中就是描述了 要如何发送数据包,包括IP,mac,协议等等。其实就是对pktgen的命令进行了一系列编排。
RFC2544
在我们的工作中,主要是测试虚拟机中的转发性能。测试模型如下:
+-------------------------------+
| +----------------------+ |
| | | |
| | VM | |
| | <--------------> | |
| +--^----------------^--+ |
| +----------------------+ |
| | | OVS | | |
| +----------------------+ |
| | ^ |
| +---+-+ +--+--+ |
| | P0 | | P1 | |
+---+---+-+-----------+--+--+---+
| |
+-------------------------------+
| | TOR | |
+-------------------------------+
| |
| |
+---+---v-+-----------+--v--+---+
| | P0 | | P1 | |
| +-----+ +-----+ |
| |
| 发 包 机 |
| |
| |
+-------------------------------+
这里标准的发包机中都有一个rfc2544的测试套,他就是专门来进行这种转发模型的测试。 DPDK-Pktgen也提供了这个测试方法,在scripts/rfc2544.lua中就定义了这种测试方法。 我们刚好就采用了这一方式来进行测试。 简单描述下rfc2544的测试方式。比如我们测试10G的网络。首先使用64字节的包以5G的带宽进行发送,判断发送的包-接收到的包,如果丢包率超过我们设置的值,则带宽减半,使用2.5G的发送。如果丢包率少于我们设置的值,则使用7.5G进行发送来测试。这样使用2分法,最终得到我们在丢包率允许的情况下,我们的带宽。
在使用中直接使用这个文件会有一些问题,所以还是需要根据实际情况进行修改。 下面把这个文件 简答的解释下
-- RFC-2544 throughput testing.
--
package.path = package.path ..";?.lua;test/?.lua;app/?.lua;../?.lua"
require "Pktgen";
-- 这里定义了我们要发送的包的大小,这里可以自己随便定义。
local pktSizes = { 64, 128, 256, 512, 1024, 1280, 1518 };
local firstDelay = 3;
local delayTime = 30; -- Time in seconds to wait for tx to stop
local pauseTime = 1;
local sendport = "0";
local recvport = "1";
-- 发包机接收端的IP
local dstip = "192.168.1.1";
-- 发包机发送端的IP
local srcip = "192.168.0.1";
local netmask = "/24";
local pktCnt = 4000000;
-- 虚拟机接收端的mac地址
local mac = "00:00:00:00:00:01"
local foundRate;
pktgen.set_mac(sendport, mac);
pktgen.set_ipaddr(sendport, "dst", dstip);
pktgen.set_ipaddr(sendport, "src", srcip..netmask);
pktgen.set_ipaddr(recvport, "dst", srcip);
pktgen.set_ipaddr(recvport, "src", dstip..netmask);
pktgen.set_proto(sendport..","..recvport, "udp");
--关闭pktgen的实时显示
pktgen.screen("off");
local function doWait(port)
local stat, idx, diff;
-- Try to wait for the total number of packets to be sent.
local idx = 0;
while( idx < (delayTime - firstDelay) ) do
stat = pktgen.portStats(port, "port")[tonumber(port)];
diff = stat.ipackets - pktCnt;
print(idx.." ipackets "..stat.ipackets.." delta "..diff);
idx = idx + 1;
if ( diff == 0 ) then
break;
end
local sending = pktgen.isSending(sendport);
if ( sending[tonumber(sendport)] == "n" ) then
break;
end
pktgen.delay(pauseTime * 1000);
end
end
-- 开始进行测试的函数
local function testRate(size, rate)
local stat, diff;
pktgen.set(sendport, "rate", rate);
pktgen.set(sendport, "size", size);
pktgen.clr();
pktgen.delay(500);
pktgen.start(sendport);
pktgen.delay(firstDelay * 1000);
doWait(recvport);
pktgen.stop(sendport);
pktgen.delay(pauseTime * 1000);
stat = pktgen.portStats(recvport, "port")[tonumber(recvport)];
diff = stat.ipackets - pktCnt;
--printf(" delta %10d", diff);
return diff;
end
local function GetPreciseDecimal(nNum, n)
if type(nNum) ~= "number" then
return nNum;
end
n = n or 0;
n = math.floor(n)
if n < 0 then
n = 0;
end
local nDecimal = 1/(10 ^ n)
if nDecimal == 1 then
nDecimal = nNum;
end
local nLeft = nNum % nDecimal;
return nNum - nLeft;
end
local function midpoint(imin, imax)
return (imin + ((imax - imin) / 2));
end
local function doSearch(size, minRate, maxRate)
local diff, midRate;
if ( maxRate < minRate ) then
return 0.0;
end
-- 查找本次需要发送的速率
midRate = midpoint(minRate, maxRate);
--printf(" Testing Packet size %4d at %3.0f%% rate", size, midRate);
--printf(" (%f, %f, %f)\n", minRate, midRate, maxRate);
-- 对带宽进行测试
diff = testRate(size, midRate);
-- 这里允许配置如果 最大速率和最小速率相差 0.0001 就直接退出。
if (maxRate - minRate < 0.0001) then
return foundRate;
end
if ( diff < 0 ) then
printf("\n");
return doSearch(size, minRate, midRate);
elseif ( diff > 0 ) then
printf("\n");
return doSearch(size, midRate, maxRate);
else
if ( midRate > foundRate ) then
foundRate = midRate;
end
if ( (foundRate == 100.0) or (foundRate == 1.0) ) then
return foundRate;
end
if ( (minRate == midRate) and (midRate == maxRate) ) then
return foundRate;
end
return doSearch(size, midRate, maxRate);
end
end
function main()
local size;
pktgen.clr();
pktgen.set(sendport, "count", pktCnt);
print("\nRFC2544 testing... (Not working Completely) ");
-- 根据配置的包,对不同包大小进行测试,并输出最后结果
for _,size in pairs(pktSizes) do
foundRate = 0.0;
printf(" >>> %d Max Rate %3.0f%%\n", size, doSearch(size, 1.0, 100.0));
end
end
main();
测试方式
启动虚拟机,主要是创建如下网卡。这里mac地址和我们rfc2544中配置要对应起来。
<interface type='vhostuser'>
<mac address='00:00:00:00:00:01'/>
<source type='unix' path='/tmp/vhost1' mode='server'/>
<model type='virtio'/>
<driver queues='1'>
<host mrg_rxbuf='off'/>
</driver>
</interface>
<interface type='vhostuser'>
<mac address='00:00:00:00:00:02'/>
<source type='unix' path='/tmp/vhost2' mode='server'/>
<model type='virtio'/>
<driver queues='1'>
<host mrg_rxbuf='off'/>
</driver>
</interface>
在发包机上执行
app/app/x86_64-native-linuxapp-gcc/pktgen -c 0x1f -n 3 -- -P -m "[1:3].0,[2:4].1" -f scripts/rfc2544.lua
pktgen 与 dpdk 安装
https://blog.csdn.net/shaoyunzhe/article/details/111560782
要安装:
https://help.aliyun.com/zh/ecs/use-pktgen-to-test-the-network-performance-of-ecs-instances
--legacy-mem
是 DPDK Pktgen 工具的一个命令行参数,用于指示 Pktgen 在初始化内存时使用传统的(legacy)内存管理模式。这个参数的含义如下:
-
含义:
--legacy-mem
参数用于指示 Pktgen 在初始化内存时使用传统的内存管理模式。传统内存管理模式是一种较早的内存管理方法,与 DPDK 的环境中通常使用的 Huge Pages 内存管理方式不同。 -
用途:使用
--legacy-mem
参数可以在某些情况下解决与内存管理相关的问题。这可能与特定硬件或操作系统配置有关。 -
注意事项:使用
--legacy-mem
参数时,请注意以下事项:- 传统内存管理模式可能会导致性能下降,因此应该仅在必要时使用。
- 如果您的环境支持 Huge Pages 内存管理,并且性能要求较高,通常建议使用 Huge Pages,而不是传统内存管理模式。
- 在使用
--legacy-mem
参数时,建议仔细测试性能,以确保它满足您的需求。
请注意,--legacy-mem
参数的具体效果可能会因您的环境和用例而异。通常情况下,只有在特定问题或限制需要使用传统内存管理模式时才会考虑使用此参数。如果您不确定是否需要使用 --legacy-mem
参数,请参考 DPDK Pktgen 的文档或相关资源,以获取更多信息和建议。
不成功,使用了NUMA 1 网卡。 core 0-5 在NUMA 0上。导致失败。
文本将介绍DPDK与Pktgen的安装。
安装包下载
DPDK与DPDK-Pktgen下载:
# git clone https://dpdk.org/git/dpdk
# git clone http://dpdk.org/git/apps/pktgen-dpdk
安装DPDK
首先安装DPDK的必备库。
#!/bin/sh
yum update -y --allowerasing --skip-broken --nobest
yum install -y gcc-toolset-9
yum install -y zlib-devel
yum install -y libnl3-devel
yum install -y boost-devel
yum install -y systemd-devel
yum install -y yasm
yum install -y lz4-devel
yum install -y elfutils-libelf-devel
yum install -y openssl-devel
yum install -y numactl-devel
yum install -y python3
pip3 install mwcp # python3需要安装 mwcp , 才可以继续安装meson build,
pip3 install meson # Build DPDK和DPDK-pktgen的工具
pip3 install ninja # 安装DPDK和DPDK-pktgen的工具
meson 如果安装不上去,
设置大页内存
设置大页内存有两种方式,通过挂载或者通过修改grub。
挂载的方式如下:
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
mkdir /mnt/huge
mount -t hugetlbfs pagesize=1GB/mnt/huge
# modprobe vfio
# modprobe vfio_pci
# modprobe uio_pci_generic
# modprobe vfio enable_unsafe_noiommu_mode=1
# echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
修改grub的方式如下:进入/etc/default/grub
,把 default_hugepagesz=1GB hugepagesz=1G hugepages=100 intel_iommu=on iommu=pt
添加到GRUB_CMDLINE_LINUX:
# vim /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet default_hugepagesz=1GB hugepagesz=1G hugepages=100 intel_iommu=on iommu=pt"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
重新载入grub并重启:
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
通过grep Huge /proc/meminfo
检查是否设置成功:
AnonHugePages: 149504 kB
ShmemHugePages: 0 kB
HugePages_Total: 100
HugePages_Free: 100
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 1048576 kB
Hugetlb: 104857600 kB
有时候会出现这个错误:
[root@localhost grub2]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
/usr/bin/grub2-editenv: error: environment block too small.
[root@localhost grub2]# ls
grub.cfg grub.cfg.new grub.cfg.old grubenv
这一般是grubenv文件配置导致的,该文件是environment block, 环境锁,在该文件中将default_hugepagesz=1GB hugepagesz=1G hugepages=100 intel_iommu=on iommu=pt
添加到kernelopts中(与grub.cfg一致)。
# GRUB Environment Block
#saved_entry=4df9ab5a7ada4793a22d0d4668f7e914-4.18.0-193.el8.x86_64
#saved_entry=1
kernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet default_hugepagesz=1GB hugepagesz=1G hugepages=100 intel_iommu=on iommu=pt
boot_success=0
设置大页内存,可以参考:
安装Pktgen
Pktgen安装必须要有的库有:
libpcap # 数据包捕获函数库
meson
ninja
pkg-config # 指向头文件和库文件的工具,给编译器使用,类似快捷方式
进入pktgen源文件目录进行安装:
cd pktgen-dpdk
meson build # meson编译时容易报错
ninja
ninja install
在执行meson build时,可能会出现以下问题:
Found pkg-config: /usr/bin/pkg-config (1.4.2)
Found CMake: /usr/bin/cmake (3.18.2)
Run-time dependency libdpdk found: NO (tried pkgconfig and cmake)
meson.build:62:0: ERROR: Dependency "libdpdk" not found, tried pkgconfig and cmake
显示没有找到dpdk相关的库,原因在于dpdk编译时生成的库并没有放入/usr/lib与/lib中。dpdk相关的库数量非常多,如果直接拖动到这两个文件夹中,不方便管理。因此可以采用ld.so的方式进行类似"快捷方式"的连接,dpdk的库不需要移动位置就可以被其他的程序调用。
解决方法如下:
先将已安装的dpdk全部删除干净,目的是重新安装DPDK且其lib有一个固定的位置。
如果是只删除原来用meson和ninja安装的DPDK, 直接执行:
ninja uninstall
再删除创建的dpdk-xxx/build文件夹。
但是有可能机器里安装了DPDK在其他位置。
find /usr/ -type d -name "dpdk" -exec rm -rf {} #在/usr/下寻找所有带有"dpdk"字符的目录 并且删除所有结果
find /lib/ -type d -name "dpdk" -exec rm -rf {}
find /lib64/ -type f -name "librte" -exec rm -rf {} #在/lib64/下寻找带有"librte"字符的文件,并且删除所有结果
find /usr/ -type f -name "librte" -exec rm -rf {}
find /lib/ -type f -name "librte" -exec rm -rf {}
执行完再重新安装DPDK,一般安装完之后的库在/lib64/下(如果在其他的地方也没有问题,仍然使用ldconfig 进行配置)。安装过程上文已写出。关键在于安装完毕之后配置libdpdk的路径。新版的DPDK安装完之后并没有名叫“libdpdk”的包,而是各种分为了许许多多名字不一的包,可以在dpdk-xxx/build/lib中看到,需要将该目录中的所有库文件被ld.so指向。
配置ldconfig文件:
# vim /etc/ld.so.conf.d/pktgen.conf
# 在该配置文件中加入需要指向的库文件目录
/usr/local/lib64
/dpdk-20.11/build/lib
在命令行模式下执行export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
与ldconfig
,之后就可以顺利编译pktgen了:
# cd dpdk-pktgen
yum install -y libpcap-devel
meson -Denable_kmods=true -Dexamples=all build
ninja
ninja install
https://www.cnblogs.com/goto2091/p/15939960.html 安装遇到问题
参考引用:
http://ssdxiao.github.io/linux/2017/08/23/Docker-Pktgen.html
https://blog.csdn.net/shaoyunzhe/article/details/111560782
https://blog.csdn.net/kelxLZ/article/details/114289332 pktgen指南01
https://www.alibabacloud.com/help/zh/ecs/use-pktgen-to-test-the-network-performance-of-ecs-instances ECS实例如何使用Pktgen测试网络性能
https://blog.csdn.net/jpmsdn/article/details/79928880 多网卡多进程发送数据
https://pktgen-dpdk.readthedocs.io/en/latest/getting_started.html pktgen 文档
https://www.cnblogs.com/goto2091/p/15939960.html
0
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理