baron tech blog

学びをアウトプットしていきたいです

Cisco's TRexのLayer 3 modeを試してみました

概要

TRexCisco謹製のオープンソースのDPDKトラフィックジェネレータです。 TRexでは、Scapyやpcap fileからパケットを生成します。また、Stateless/Statefullともにサポートしています。

TRexの導入からLayer3 modeでトラフィックを流してみたので、備忘録としてまとめておきます。

検証環境

TRexサーバとDUT (Device Under Test: 測定対象) を直接接続した、最小単位の構成にしました。

f:id:foobaron:20190309213319p:plain
TRexの検証環境

TRexサーバとDUTの環境は次のとおりです。2台ともVMware ESXi 6.7上の仮想マシンで動作させました。NICのアダプタタイプはすべてVMXNET3です。

TRexは推奨であるCentOS 7.4に導入しました (参考)。 DUTは何でもよいのですが、今回はUbuntu 18.04にしました。

  • TRexサーバ
    • CentOS 7.4
    • vCPU x 2, RAM: 4GB
    • TRex v2.53
  • DUT
    • Ubuntu 18.04
    • vCPU x 1, RAM: 1GB

検証環境の図の矢印のとおりに、TRexサーバで生成したトラフィックを一方のポートからトラフィックを送信し、もう一方のポートで受信させます。

環境構築

TRexの導入

基本的には公式のInstallation Guideにしたがって導入を進めていきます。

まずTRexを適当な場所にダウンロードして解凍します。今回利用するTRexのバージョンは v2.53 です。

TRex:~$ sudo su -
TRex:~# mkdir -p /opt/trex
TRex:~# curl -LO http://trex-tgn.cisco.com/trex/release/latest
TRex:~# cd /opt/trex
TRex:~# tar xvf latest

次にDPDKのセットアップをしていきます。

セットアップに必要なツールをインストールします。

TRex:~# yum install -y pciutils kernel-devel-`uname -r`
TRex:~# yum group install "Development tools"

そして、TRexが提供するPythonスクリプトファイルを -i の対話形式オプションで実行します。 次の内容を入力します。

あとは必要なカーネルモジュールのロードなどをこのスクリプトが実施してくれます。

TRex:~# cd /opt/trex/v.2.53
TRex:~# ./dpdk_setup_ports.py -i

By default, IP based configuration file will be created. Do you want to use MAC based config? (y/N)N
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
| ID | NUMA |   PCI   |        MAC        |            Name             | Driver  | Linux IF |  Active  |
+====+======+=========+===================+=============================+=========+==========+==========+
| 0  | -1   | 0b:00.0 | 00:0c:29:84:3f:d4 | VMXNET3 Ethernet Controller | vmxnet3 | ens192   | *Active* |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
| 1  | -1   | 13:00.0 | 00:0c:29:84:3f:de | VMXNET3 Ethernet Controller | vmxnet3 | ens224   |          |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
| 2  | -1   | 1b:00.0 | 00:0c:29:84:3f:e8 | VMXNET3 Ethernet Controller | vmxnet3 | ens256   |          |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
Please choose even number of interfaces from the list above, either by ID , PCI or Linux IF
Stateful will use order of interfaces: Client1 Server1 Client2 Server2 etc. for flows.
Stateless can be in any order.
Enter list of interfaces separated by space (for example: 1 3) : 1 2

For interface 1, assuming loopback to it's dual interface 2.
Putting IP 1.1.1.1, default gw 2.2.2.2 Change it?(y/N).y
Please enter IP address for interface 1: 1.1.1.1
Please enter default gateway for interface 1: 1.1.1.3
For interface 2, assuming loopback to it's dual interface 1.
Putting IP 2.2.2.2, default gw 1.1.1.1 Change it?(y/N).y
Please enter IP address for interface 2: 2.2.2.2
Please enter default gateway for interface 2: 2.2.2.3
Print preview of generated config? (Y/n)Y
### Config file generated by dpdk_setup_ports.py ###

- version: 2
  interfaces: ['13:00.0', '1b:00.0']
  port_info:
      - ip: 1.1.1.1
        default_gw: 1.1.1.3
      - ip: 2.2.2.2
        default_gw: 2.2.2.3

  platform:
      master_thread_id: 0
      latency_thread_id: 1
      dual_if:
        - socket: 0
          threads: [2,3]


Save the config to file? (Y/n)Y
Default filename is /etc/trex_cfg.yaml
Press ENTER to confirm or enter new file:
Saved to /etc/trex_cfg.yaml.

これでTRexを動かす準備が完了しました。

DUTの設定

検証環境の構成に従って、IPアドレスを設定しておきます。

DUT:~# cat /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    ens192:
      addresses:
      - 1.1.1.3/24
    ens224:
      addresses:
      - 2.2.2.3/24
DUT:~# netplan apply

TRexを動かす

以降のコマンド入力はTRexを配置したディレクトリ ( /opt/trex/v2.53 ) で実施しています。

TRexの起動

TRexサーバの起動

動作確認のために、対話形式でTRexを起動します。

TRex:~# ./t-rex-64 -i

TRexが起動すると、TRexで利用するインタフェースがDPDKで利用されるようになり、 ip address show しても見えなくなります。 この状態でTRexサーバのそれぞれにインタフェースにDUTからpingしてみると、 TRexとDUTの間で疎通が確認できます。

DUT:~# ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=128 time=47.0 ms

--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 47.008/47.008/47.008/0.000 ms
DUT:~# ping -c 1 2.2.2.2
PING 2.2.2.2 (2.2.2.2) 56(84) bytes of data.
64 bytes from 2.2.2.2: icmp_seq=1 ttl=128 time=8.70 ms

--- 2.2.2.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 8.703/8.703/8.703/0.000 ms

TRexコンソールの起動

./t-rex-64 -i でTRexサーバが起動している状態で別のターミナルを起動し、TRex consoleからTRexサーバに接続します。

TRex:~# ./trex-console

そのTRexコンソールからPythonで作成したパケットを流してみます。

TRexコンソールからStatelessトラフィック1フローを流す

トラフィックの生成ファイル

stl/ 配下にTRexのStateless traffic用のサンプルファイルがいくつも配置されています。 stl/udp_1pkt.py というシンプルなUDPパケットを送信するファイルを今回の環境用に書き換えます。

書き換えたPythonファイルは次のとおりです。

stl/udp_1pkt_2.py

from trex_stl_lib.api import *

class STLS1(object):
    def __init__ (self):
        self.fsize  = 64;

    def create_pkt_base (self):
        return Ether()/IP(src="1.1.1.1",dst="2.2.2.2")/UDP(dport=50000,sport=50001)

    def create_stream (self):
        # Create base packet and pad it to size
        size = self.fsize - 4; # HW will add 4 bytes ethernet FCS
        base_pkt = self.create_pkt_base ()
        pad = max(0, size - len(base_pkt)) * 'x'
        pkt = STLPktBuilder(pkt = base_pkt/pad, vm = [])
        return STLStream(packet = pkt, mode = STLTXCont())

    def get_streams (self, direction = 0, **kwargs):
        # create 1 stream
        return [ self.create_stream() ]


# dynamic load - used for trex console or simulator
def register():
    return STLS1()

不要な箇所は削り、シンプルにしています。

Scapyで次のようなパケットを生成しています。

Ethernetフレームの最小サイズは64バイトであるため、 L7で64バイトになるようデータとして xxxxxxx を詰めておきます。

トラフィックは送信し続けるモード ( STLTXCont() ) にしておきます。

パケットの確認

TRexでトラフィックを流す前に、作成したPythonファイルで生成されるパケットをpcapファイルに出力して動作確認します。 TRexでは、 stl-sim が準備されているため、それを利用します。

TRex:~# ./stl-sim -f stl/udp_1pkt_2.py -o /usr/tmp/udp_1pkt_2.pcap
executing command: '/opt/trex/v2.53/bp-sim-64-debug --pcap --sl --cores 1 --limit 5000 -f /tmp/tmpNYMdGQ -o /usr/tmp/udp_1pkt_2.pcap'
 set dpdk queues mode to ONE_QUE

General info:
------------

image type:               debug
I/O output:               /usr/tmp/udp_1pkt_2.pcap
packet limit:             5000
core recording:           merge all

Configuration info:
-------------------

ports:                    2
cores:                    1

Port Config:
------------

stream count:             1
max PPS    :              1.00  pps
max BPS L1 :              672.00  bps
max BPS L2 :              512.00  bps
line util. :              0.00  %


Starting simulation...


Simulation summary:
-------------------

core index 0
-----------------

    simulated packets  : 5000
    non active packets : 0
    on-wire packets    : 5000

Total:
-----------------

    simulated packets  : 5000
    non active packets : 0
    on-wire packets    : 5000

written 5000 packets to '/usr/tmp/udp_1pkt_2.pcap'

生成されたファイルをWiresharkで開くと、想定したとおりのパケットが確認できます。

f:id:foobaron:20190310004630p:plain
TRexで生成されるパケット

TRexコンソールからトラフィックを流す

先ほどから起動している trex-console から stl/udp_1pkt_2.py を動かしてみます。

100k bpsトラフィックを流してみます。送信ポートは ID: 0 (IPアドレス 1.1.1.1 を割り当て中) を指定します。

trex>start -p 0 -m 100kbps -f stl/udp_1pkt_2.py -p 0

Removing all streams from port(s) [0]:                       [SUCCESS]


Attaching 1 streams to port(s) [0]:                          [SUCCESS]


Starting traffic on port(s) [0]:                             [SUCCESS]

9.53 [ms]

TRexサーバ側で、送信 (Tx) と受信 (Rx) がそれぞれ100K bps前後になっていることが確認できます。

-Per port stats table
      ports |               0 |               1
 -----------------------------------------------------------------------------------------
   opackets |       770824266 |        65986289
     obytes |     49332753176 |      4223122610
   ipackets |               8 |       470350760
     ibytes |             664 |     30102450602
    ierrors |               0 |            7498
    oerrors |               0 |               0
      Tx Bw |     100.06 Kbps |       0.00  bps

-Global stats enabled
 Cpu Utilization : 0.0  %
 Platform_factor : 1.0
 Total-Tx        :     100.06 Kbps
 Total-Rx        :     100.06 Kbps
 Total-PPS       :     195.43  pps
 Total-CPS       :       0.00  cps

 Expected-PPS    :       0.00  pps
 Expected-CPS    :       0.00  cps
 Expected-BPS    :       0.00  bps

 Active-flows    :        0  Clients :        0   Socket-util : 0.0000 %
 Open-flows      :        0  Servers :        0   Socket :        0 Socket/Clients :  -nan
 Total_queue_full : 203155427
 drop-rate       :       0.00  bps
 current time    : 7247.9 sec
 test duration   : 0.0 sec

DUTの ens224tcpdump すると、パケットが転送されている様子が確認されます。

DUT:~# tcpdump -i ens224
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens224, link-type EN10MB (Ethernet), capture size 262144 bytes
15:23:06.048913 ARP, Request who-has 2.2.2.2 tell test-ubuntu-01, length 28
15:23:06.049366 ARP, Reply 2.2.2.2 is-at 00:0c:29:84:3f:e8 (oui Unknown), length 46
15:23:06.960427 IP one.one.one.one.50001 > 2.2.2.2.50000: UDP, length 18
15:23:06.960449 IP one.one.one.one.50001 > 2.2.2.2.50000: UDP, length 18
15:23:07.960437 IP one.one.one.one.50001 > 2.2.2.2.50000: UDP, length 18
15:23:07.960466 IP one.one.one.one.50001 > 2.2.2.2.50000: UDP, length 18

また、TRexコンソールでも、 tui によって送受信レート等を確認できます。

trex>tui

Global Statistitcs

connection   : localhost, Port 4501                  total_tx_L2  : 99.96 Kb/sec
version      : STL @ v2.53                           total_tx_L1  : 131.2 Kb/sec
cpu_util.    : 0.05% @ 1 cores (1 per port)          total_rx     : 99.96 Kb/sec
rx_cpu_util. : 0.0% / 195.24 pkt/sec                 total_pps    : 195.24 pkt/sec
async_util.  : 0.05% / 1.47 KB/sec                   drop_rate    : 0 b/sec
                                                     queue_full   : 0 pkts

Port Statistics

   port    |         0         |         1         |       total
-----------+-------------------+-------------------+------------------
owner      |              root |              root |
link       |                UP |                UP |
state      |      TRANSMITTING |              IDLE |
speed      |           10 Gb/s |           10 Gb/s |
CPU util.  |             0.05% |              0.0% |
--         |                   |                   |
Tx bps L2  |        99.96 Kbps |             0 bps |        99.96 Kbps
Tx bps L1  |        131.2 Kbps |          0.01 bps |        131.2 Kbps
Tx pps     |        195.24 pps |             0 pps |        195.24 pps
Line Util. |               0 % |               0 % |
---        |                   |                   |
Rx bps     |             0 bps |        99.96 Kbps |        99.96 Kbps
Rx pps     |             0 pps |        195.24 pps |        195.24 pps
----       |                   |                   |
opackets   |             33906 |                 6 |             33912
ipackets   |                 0 |             33912 |             33912
obytes     |           2169984 |               384 |           2170368
ibytes     |                 0 |           2170368 |           2170368
tx-pkts    |       33.91 Kpkts |            6 pkts |       33.91 Kpkts
rx-pkts    |            0 pkts |       33.91 Kpkts |       33.91 Kpkts
tx-bytes   |           2.17 MB |             384 B |           2.17 MB
rx-bytes   |               0 B |           2.17 MB |           2.17 MB
-----      |                   |                   |
oerrors    |                 0 |                 0 |                 0
ierrors    |                 0 |                 0 |                 0

status:  /

Press 'ESC' for navigation panel...
status:

TRexコンソールで開始したトラフィックは、 stop コマンドで停止します。

trex>stop

Stopping traffic on port(s) [0]:                             [SUCCESS]

2.83 [ms]

次に複数フローを流してみます。 その前に、起動しているTRexサーバとTRexコンソールを一旦終了しておきます。

TRexコンソールからStatelessトラフィックを複数フローを流す

これまではTRexで1フローのみ ( 1.1.1.1 から 2.2.2.2 宛) でトラフィックを流してきました。 次は複数のフローを流してみます。

YAMLファイルを記述することで、詳細な設定でTRexサーバを動かすことができます。 今回は、予め用意されている cap2/dns_tw.yaml を動かしてみます。

- duration : 10.0
  generator :
          distribution : "seq"
          clients_start : "16.0.0.1"
          clients_end   : "16.0.1.255"
          servers_start : "48.0.0.1"
          servers_end   : "48.0.0.255"
          clients_per_gb : 201
          min_clients    : 101
          dual_port_mask : "1.0.0.0"
          tcp_aging      : 1
          udp_aging      : 1
  tw :                             # set timer wheel configuration options
     buckets : 32768
     levels  : 2
     bucket_time_usec : 20.0
  cap_info :
     - name: cap2/dns.pcap
       cps : 1.0
       ipg : 10000
       rtt : 10000
       w   : 1

pcapファイルで用意されたトラフィックを流していきます。

Layer3 modeで 1.1.1.0/24 から 2.2.2.0/24 宛にトラフィックを流すことはできない (参考:http://trex-tgn.cisco.com/trex/doc/trex_config_guide.html#slide-3) ようです。 なので、TRexのIPアドレスとclients/serversのIPアドレスプレフィックスは別のものを利用します (参考:http://trex-tgn.cisco.com/trex/doc/trex_config_guide.html#slide-2)

TRexサーバが流すトラフィックをDUTで中継できるように、DUTに経路情報を追加します。

DUT:~# ip -4 route add 16.0.0.0/24 via 1.1.1.1 dev ens192
DUT:~# ip -4 route add 48.0.0.0/24 via 2.2.2.2 dev ens224

t-rex-64 から直接TRexでトラフィックを生成していきます。今回もID: 0のポートからトラフィックを流します。

TRex:~# ./t-rex-64 -p 0 -m 100kbps -f cap2/dns_tw.yaml 


-Per port stats table
      ports |               0 |               1
 -----------------------------------------------------------------------------------------
   opackets |             190 |             191
     obytes |           16150 |           16227
   ipackets |              96 |              95
     ibytes |            8899 |            7315
    ierrors |               0 |               0
    oerrors |               0 |               0
      Tx Bw |      33.70 Kbps |      33.33 Kbps

-Global stats enabled
 Cpu Utilization : 0.0  %
 Platform_factor : 1.0
 Total-Tx        :      67.03 Kbps
 Total-Rx        :      33.58 Kbps
 Total-PPS       :      98.53  pps
 Total-CPS       :      49.51  cps

 Expected-PPS    :     200.00  pps
 Expected-CPS    :     100.00  cps
 Expected-BPS    :     136.00 Kbps

 Active-flows    :        1  Clients :      511   Socket-util : 0.0003 %
 Open-flows      :      191  Servers :      255   Socket :      111 Socket/Clients :  0.2
 drop-rate       :      33.45 Kbps
 current time    : 3.0 sec
 test duration   : 3597.0 sec

最後に、DUTの ens224tcpdump もしてみます。

DUT:~# tcpdump -i ens224
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens224, link-type EN10MB (Ethernet), capture size 262144 bytes
17:28:03.426173 ARP, Reverse Request who-is 00:0c:29:84:3f:e8 (oui Unknown) tell 00:0c:29:84:3f:e8 (oui Unknown), length 46
17:28:03.426187 ARP, Reverse Request who-is 00:0c:29:84:3f:e8 (oui Unknown) tell 00:0c:29:84:3f:e8 (oui Unknown), length 46
17:28:04.425679 ARP, Reverse Request who-is 00:0c:29:84:3f:e8 (oui Unknown) tell 00:0c:29:84:3f:e8 (oui Unknown), length 46
17:28:04.425690 IP 0.0.0.0 > all-systems.mcast.net: igmp query v3 [max resp time 1.0s]
17:28:04.434847 ARP, Request who-has 2.2.2.2 (01:03:05:07:09:00 (oui Unknown)) tell 2.2.2.2, length 46
17:28:04.434855 ARP, Request who-has test-ubuntu-01 (01:03:05:07:09:01 (oui Unknown)) tell 2.2.2.2, length 46
17:28:04.434863 ARP, Reply test-ubuntu-01 is-at 00:0c:29:e8:81:6d (oui Unknown), length 28
17:28:04.548155 IP bpe-ssl.ams.hpecore.net.41668 > 48.0.0.1.domain: 48 A? www.cisco.com. (31)
17:28:04.557095 IP 48.0.0.1.domain > bpe-ssl.ams.hpecore.net.41668: 48* 1/0/0 A 100.100.100.100 (47)
17:28:04.558116 IP 16.0.0.2.59073 > 48.0.0.2.domain: 48 A? www.cisco.com. (31)
17:28:04.568080 IP 16.0.0.3.10942 > 48.0.0.3.domain: 48 A? www.cisco.com. (31)
17:28:04.577086 IP 48.0.0.3.domain > 16.0.0.3.10942: 48* 1/0/0 A 100.100.100.100 (47)
17:28:04.578092 IP 16.0.0.4.28347 > 48.0.0.4.domain: 48 A? www.cisco.com. (31)
17:28:04.588079 IP 16.0.0.5.45752 > 48.0.0.5.domain: 48 A? www.cisco.com. (31)
17:28:04.597085 IP 48.0.0.5.domain > 16.0.0.5.45752: 48* 1/0/0 A 100.100.100.100 (47)

終わりに