Open SourceのTraffic GeneratorのTRexでSRv6のTrafficを作ってみる


1. はじめに
2. TRexってなに？
3. TRexのInstall編
4. SRv6のヘッダー形式ってどうなってるのか
5. おわりに

## はじめに

この記事はシスコの同志による Advent Calendar の一部として投稿しています

2017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2019年版: https://qiita.com/advent-calendar/2019/cisco (<<< 今年)

最近Dataplane Programming的な話が多いのですが、そこにどうやってTraffic印加するのか困ることがあります。
本業とは言えない内容に関してわざわざIXIA等用意するほどリソースないですし、特にSRv6とかのパケット作ろうとするとライセンスがなかったりします。
じゃあSRHをつけるのにCisco Routerで構成作ろうかと思っても、SRHをつけられる構成を作るのはわりと大変です。(これは後述します)
代わりにLinuxや[VPP(Vector Packet Processing)](https://wiki.fd.io/view/VPP)を間に挟んでもPingくらいしか打てないし、Installするのもめんどくさいし、微妙だなーと思ってたら、[TRex](https://trex-tgn.cisco.com/)というOpen SourceのTraffic generatorを使っている人がいたので、これでSRv6のパケット作ってみようかなと試してみました。

## TRexってなに？

TRexとは知名度の低い穴場中の穴場ではない方のCisco本社にて開発しているOpen sourceのTraffic generatorです。
DPDKで仮想にしてはかなりのパフォーマンスを出せるはずです。
コントロールプレーンのエミュレーション機能はないのが少し残念ですが、わりと学習コストも低めに思えたので、TGNがなくて困っている人にはおすすめです。

## TRex install編

TRexのInstall自体はやっている人がわりといて、ググるといくつか記事が出てきます。(が、あえてここではリンク貼りません。)
基本的に皆さん公式の[Installation guide](https://trex-tgn.cisco.com/trex/doc/trex_manual.html#_download_and_installation)を元にInstallしているようです。
最新のInstall GuideにCentOS 7.6とあるので、今回はこちらで進めました。

> CentOS/RHEL 7.6, 64-bit kernel (not 32-bit) (Recommended) — This is the only working option for ConnectX-4.

まずは使えるIFを確認します。ESXi上でやるとこんな出力になります。
SSHしているMgmtポートは*Active*と出力されて選べないので最低3ポート必要になります。

```
[root@localhost v2.71]# sudo ./dpdk_setup_ports.py -s

Network devices using DPDK-compatible driver
============================================
<none>

Network devices using kernel driver
===================================
0000:03:00.0 'VMXNET3 Ethernet Controller' if=ens160 drv=vmxnet3 unused=igb_uio,vfio-pci,uio_pci_generic *Active*
0000:0b:00.0 'VMXNET3 Ethernet Controller' drv=igb_uio unused=vmxnet3,vfio-pci,uio_pci_generic
0000:13:00.0 'VMXNET3 Ethernet Controller' drv=igb_uio unused=vmxnet3,vfio-pci,uio_pci_generic

Other network devices
=====================
<none>
```

基本設定ファイルを簡単に出力する場合は-iオプションで対話型に設定するのがよさそうです。
ここらへんもググるとわりと記事が出てきます。
以下はデフォルト設定で打ち込んだ場合の出力

```
[root@localhost v2.71]# ./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   | 03:00.0 | 00:50:56:a4:8a:c7 | VMXNET3 Ethernet Controller | vmxnet3 | ens160   | *Active* |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
| 1  | -1   | 0b:00.0 |                   | VMXNET3 Ethernet Controller | igb_uio |          |          |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
| 2  | -1   | 13:00.0 |                   | VMXNET3 Ethernet Controller | igb_uio |          |          |
+----+------+---------+-------------------+-----------------------------+---------+----------+----------+
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).N
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).N
Print preview of generated config? (Y/n)Y
### Config file generated by dpdk_setup_ports.py ###

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

  platform:
      master_thread_id: 0
      latency_thread_id: 1
      dual_if:
        - socket: 0
          threads: [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]


Save the config to file? (Y/n)Y
```

これが初期設定で、Trafficを印加する準備ができました。
TRexはかなり親切なので、シナリオファイルのサンプルがcap2の中にあります。YamlファイルとPCAPファイルがあるので、これでトラフィックが作成できますし、作りたいTrafficもこれで近いものをいじるだけでOKです。

## SRv6のヘッダー形式

さて、SRv6のパケットを生成する上でTRexではScapyまたはPCAPからパケットを生成することができます。
しかし、やっぱりサンプルパケットから生成したほうが楽なのでSRv6のパケットのPCAPから生成するのが楽かなと思いました。(色々SRHの内容間違えそうだし。)

SRv6のパケットヘッダーとなるSRHはdraft-ietf-6man-segment-routing-headerで定義されており、つい最近Proposed StandardになったばかりのHeader形式です。

![Kobito.FRbTTk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143031/32ddaff4-8983-4e41-f8f5-a8ac5101fe94.png "Kobito.FRbTTk.png")

この形式のHeaderをCaptureしたいなと思ったんですが、Ciscoルータでこれを取るのがなかなか手間がかかりました。
というのはこのHeaderの処理を定義しているのが、これまた最近Proposed StandardへのLast Call中のdraft-ietf-spring-srv6-network-programmingなのですが、ここでは
> The push of the SRH MAY be omitted when the SRv6 Policy only contains
   one segment and there is no need to use any flag, tag or TLV.

と1Segmentしかない場合にはSRHを省いてよいですよと定義されています。
Cisco Routerではこの動きに忠実なので普通に動かすと、SRHなしのただのIPinIPの形式でパケットが来ちゃいます。
ということで、このCaptureを取るために(Linuxでも良かったんですが、)VPPを使ってSRv6のPolicyを作成してパケットをCaptureしてみました。
最初からVPPでパケット生成すればよかった気がしてきましたし、ここらへんで日付が変わってしまいましたが、こちらを元からあったシナリオのipv6.yamlをちょっと編集して流してみました。

```
[root@localhost v2.71]# ./t-rex-64 -f ./cap2/ipv6.yaml -d 10 --ipv6

-Per port stats table
      ports |               0 |               1
 -----------------------------------------------------------------------------------------
   opackets |              55 |               0
     obytes |            4950 |               0
   ipackets |               3 |              58
     ibytes |             192 |            5142
    ierrors |               0 |               0
    oerrors |               0 |               0
      Tx Bw |       6.87 Kbps |       0.00  bps

-Global stats enabled
 Cpu Utilization : 0.1  %
 Platform_factor : 1.0
 Total-Tx        :       6.87 Kbps
 Total-Rx        :       7.21 Kbps
 Total-PPS       :       9.54  pps
 Total-CPS       :       0.64  cps

 Expected-PPS    :      10.00  pps
 Expected-CPS    :      10.00  cps
 Expected-BPS    :       7.20 Kbps

 Active-flows    :       20  Clients :      511   Socket-util : 0.0001 %
 Open-flows      :       20  Servers :      255   Socket :       20 Socket/Clients :  0.0
 drop-rate       :       0.00  bps
 current time    : 6.7 sec
 test duration   : 3.3 sec

```

無事流れているということで、今回はこれで良しとしておこうと思います。

## 終わりに

とりあえずTRex使ってみたかったので使ってみました。元からあるシナリオに沿うだけなら1,2時間でセットアップできそうです。
PCAPかSCAPYがある状態から始めるのであれば、学習コスト少なくて使いやすいTGNかもしれません。