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