TRex Stateless support
======================
:author: TRex team
:email: trex.tgen@gmail.com 
:revnumber: 2.0
:quotes.++:
:numbered:
:web_server_url: http://trex-tgn.cisco.com/trex
:local_web_server_url: csi-wiki-01:8181/trex
:github_stl_path: https://github.com/cisco-system-traffic-generator/trex-core/tree/master/scripts/stl
:github_stl_examples_path: https://github.com/cisco-system-traffic-generator/trex-core/tree/master/scripts/automation/trex_control_plane/stl/examples
:toclevels: 6

ifdef::backend-docbook[]
:p_width: 450
:p_width_1: 200
endif::backend-docbook[]

ifdef::backend-xhtml11[]
:p_width: 800
:p_width_1: 400
endif::backend-xhtml11[]


== Stateless support (Alpha stage)

=== High level functionality 

* High scale - line rate 14MPPS per core, linear scale with number of cores
* Support 1/10/25/40/100 Gb/sec interfaces 
* Interface can be configured with multi traffic profiles 
* Traffic Profile can support multi streams. Scale to 10K streams in parallel 
* Each Stream
** Packet template - ability to build any packet using Scapy (e.g. MPLS/IPv4/Ipv6/GRE/VXLAN/NSH)
** Field engine program
*** Ability to change any field inside the packet, for example src_ip = 10.0.0.1-10.0.0.255
*** Ability to change the packet size (e.g. Random packet size 64-9K)
** Mode - Continuous/Burst/Multi burst support
** Rate can be specified in:
*** Packet per second -(e.g. 14MPPS)
*** L1 bandwidth (e.g. 500Mb/sec)
*** L2 bandwidth (e.g. 500Mb/sec)
*** Interface link percentage,( e.g. 10%)
** Support HLTAPI like profile definition  
** Action- stream can trigger a stream 
* Interactive support-  Fast Console,  GUI 
* Statistic per interface
* Statistic per stream done in hardware
* Latency and Jitter per stream
* Blazing fast Automation support 
** Python 2.7/3.0 Client API 
** Python HLTAPI  Client API
* Multi user support - multiple users can interact with the same TRex simultaneously

==== Traffic profile example

image::images/stl_streams_example.png[title="Streams example",align="left",width={p_width}, link="images/stl_streams_example.png"]

==== High level functionality - near future

* ARP emulation - learn server MAC. Support unlimited of MAC addresses per port

==== High level functionality - roadmap

* Add emulation support 
** RIP/BGP/ISIS/SPF


=== IXIA IXExplorer vs TRex 

TRex has limited functionality compared to IXIA, but has some advantages. The following table summarized the difference

.TRex vs IXExplorer
[cols="1^,3^,3^,5^", options="header"]
|=================
| Feature       |  IXExplorer  |TRex | Description 
| Line rate       | Yes |Almost ~15MPPS/core|
| Multi stream    | 255 | [green]*Unlimited* |
| Packet build flexibility | Limited | [green]*Scapy- Ulimited* | e.g GRE/VXLAN/NSH is supported. Can be extended to future protocols
| Packet Field engine      | limited | [green]*Unlimited* |
| Tx Mode | Continuous/Burst/Multi burst | Continuous/Burst/Multi burst|
| ARP Emulation | Yes | Not yet - workaround |
| Automation  | TCL/Python wrapper to TCL | [green]*native Python/Scapy*  |
| Automation speed sec| 30sec | [green]*1msec* | test of load/start/stop/get counters 
| HLTAPI | Full support. 2000 pages of documentation |  Limited 20 page of documentation|
| Per Stream statistic | 255  streams with 4 global mask | 128 rules for XL710/X710 hardware and software impl for 82599/I350/X550| in case of XL710/X710 there are some restrictions for the packet type
| Latency Jitter |  Yes | Yes |
| Multi user support | Yes | Yes |
| GUI  | very good | WIP, packet build is scapy based. Not the same as IXIA |
| Cisco pyATS support | Yes | Yes Python 2.7, Python 64bits, WIP to port it to Python 3.0|    
| Emulation | Yes | Not yet |
| Port Ids  | Base on IXIA numebrs  | Depends on PCI enumeration  
|=================


=== RPC Architecture 

To support interactive mode, JSON-RPC2 thread added to the TRex Control Plane core.

The following diagram illustrates the RPC server/client components

image::images/trex_2_stateless.png[title="RPC Server Position",align="left",width={p_width}, link="images/trex_2_stateless.png"]

* The Control transport protocol is ZMQ working in REQ/RES mode
* JSON-RPC2 is the RPC protocol on top of the ZMQ REQ/RES 
* Async transport is ZMQ working SUB/PUB mode. It is for async event such as interface change mode, counters etc.
* Python is the first Client to implement the Python automation API 
* Console utilizes the Python API to implement a user interface to TRex
* Number of users can control one TRex server in parallel as long as they control different Interfaces. TRex Interface can be acquired by a user. For example a TRex with four ports can be used by two users. User A can acquire Interface  0/ 1 and User B can acquire Interface 3/4
* There could be only *one* control Console/GUI (R/W) entity for a specific user. User A with two interfaces could have only one R/W Control session in specific time. By that we can cache the TRex Server interface  information in the Client.
* For one user there could be many read-only clients for getting statistics.
* Client should sync with the server to get the state in connection time and cache the server information locally once the state was changed 
* In case of crash/exit of the Client it should sync again at connection time.
* Client has the ability to get a statistic in real time (with ASYNC ZMQ). It gives the option to have number of ways to look into the statistics (GUI and Console) at the same time.

image::images/trex_stateless_multi_user.png[title="Multi user-per interface",align="left",width={p_width}, link="images/trex_stateless_multi_user.png"]

For more detailed see RPC specification link:trex_rpc_server_spec.html[here]  

This Architecture provides the following advantages:

* Fast interaction with TRex server. very fast load/start/stop profiles to an interface (~2000 cycles/sec  for load/start/stop profile)
* Leveraging Python/Scapy for building a packet/Field engine 
* HLTAPI compiler complexity is done in Python

=== TRex Entities 

image::images/stateless_objects.png[title="TRex Entities",align="left",width={p_width_1}, link="images/stateless_objects.png"]

* *TRex*: Each TRex instance includes a number of interfaces   
* *Interface*: For each Interface it is possible to add/remove a number of traffic profiles (TP) 
* *Traffic profile*: Each traffic profile includes a number of streams. This is the basic building block  of activation. It is possible to add/remove to an interface a profile while other profile already exists. A profile can be looked as a "program" with dependency between streams. It is not possible to change a profile while it is running except changing the rates. 
* *Stream*: Each stream includes 
** *Packet*: Packet template up to 9K bytes 
** *Field Engine*:  which field to change, do we want to change packet size
** *Mode*: how to send the packet. Continuous/Burst/Multi Burst 
** *Rx Stats* Which Statstistic to collect for each stream 
** *Rate*: Specified in Packet Per Second (pps) or bandwidth (bps)
** *Action*:  The next stream to go after this stream is finished. Valid for Burst/Continuous mode


=== Statful vs Stateless 

TRex Stateless support is basic L2/L3 tests more for Switch/Router. 
With Stateless it is possible to define a Stream that has a *one* packet template, define a program to change any fields in the packet and run it in continues/burst/multi-burst mode.
With Statless you *can't* learn NAT translation because there is no context of flow/client/server. In Stateful the basic building block is a flow/application (That compose from many packets).
However, Using Stateless mode, it is much more flexible as you can define any type of packets and build simple program and in a way you can mimic Stateful but not everything.
For example, you can load a pcap with the number of packets as a link of streams 
a->b->c->d-> back to a
And create a program for each stream to change src_ip=10. 0.0.1-10.0.0.254 this will create something similar to Stateful but the underline is totally different.  
If you are confused you probably need Stateless.

.Statful vs Stateless 
[cols="1^,3^,3^", options="header"]
|=================
| Feature       |  Stateless  |Statful 
| Flow base       | No | Yes
| NAT             | No | Yes
| Tunnel          | Yes | Only specific 
| L7 App emulation | No | Yes
| Any type of packet | Yes | No 
| Latency Jitter | Per Stream | Global/Per flow
|=================


=== TRex package folders 

[cols="5,5", options="header",width="100%"]
|=============================
| Location        | Description   
| /               | t-rex-64/dpdk_set_ports/stl-sim 
| /stl            | Stateless native (py) profiles 
| /stl/yaml       | Stateless YAML profiles 
| /stl/htl        | Stateless HTL profiles 
| /ko             | Kernel modules for DPDK
| /external_libs  | Python external libs used by server/clients
| /exp            | Golden pcap file for unit-tests
| /cfg            | Examples of config files
| /cap2           | Stateful profiles 
| /avl            | Stateful profiles - SFR profile
| /automation     | Python client/server code for both Stateful and Stateless
| /automation/regression     | Regression for Stateless and Stateful
| /automation/config     | Regression setups config files
| /automation/trex_control_plane/stl     | Stateless lib and Console 
| /automation/trex_control_plane/stl/trex_stl_lib     | Stateless lib
| /automation/trex_control_plane/stl/examples     | Stateless Examples
|=================

=== Basic Tutorials

This tutorial will walk you through basic but complete TRex Stateless use cases that will show you common concepts as well as slightly more advanced ones.

==== Tutorial: Simple IPv4/UDP packet - TRex 

*Goal*:: send a simple UDP packet from all the ports

*Traffic profile*::  

Traffic profile (TP) is a way to define *how* to generate the traffic. It defines the traffic templates the rate the mode and which fields in the packet to change. The following example defines a profile with one stream. The stream is with IP/UDP packet template with 10 bytes of 'x'(0x78) of payload. to get more example how to define packets using scapy see here link:http://www.secdev.org/projects/scapy/doc/[Scapy]

*file*:: link:{github_stl_path}/udp_1pkt_simple.py[stl/udp_1pkt_simple.py]

[source,python]
----
from trex_stl_lib.api import *                                  

class STLS1(object):

    def create_stream (self):

        return STLStream( 
            packet = 
                    STLPktBuilder(
                        pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/
                                UDP(dport=12,sport=1025)/(10*'x')                       <1>                
                    ),
             mode = STLTXCont())                                                        <2>


    def get_streams (self, direction = 0):                                              <3>
        # create 1 stream 
        return [ self.create_stream() ]


# dynamic load - used for trex console or simulator
def register():                                                                         <4>        
    return STLS1()
----
<1> Define the packet, in this case it IP/UDP with 10 bytes of 'x'(0x78) .See more here link:http://www.secdev.org/projects/scapy/doc/[Scapy]
<2> Mode is Continuous with a rate of 1 pps (default rate is 1 PPS)
<3> get_streams function is mandatory 
<4> Each Traffic profile module should have a `register` function


*Start TRex as a server*::   

[NOTE] 
=====================================================================
There is no need to install any python packages (including scapy). just download the TRex package
=====================================================================


[source,bash]
----
$sudo ./t-rex-64 -i
----

*Connect with Console*::

From the same machine in a different terminal (either open a new window using `xterm`, or `ssh` again) run the folowing command

[source,bash]
----
$trex-console                                                           #<1>

Connecting to RPC server on localhost:4501                   [SUCCESS]
connecting to publisher server on localhost:4500             [SUCCESS]
Acquiring ports [0, 1, 2, 3]:                                [SUCCESS]

125.69 [ms]

trex>start -f stl/udp_1pkt_simple.py -m 10mbps -a                      #<2>

Removing all streams from port(s) [0, 1, 2, 3]:              [SUCCESS]
Attaching 1 streams to port(s) [0, 1, 2, 3]:                 [SUCCESS]
Starting traffic on port(s) [0, 1, 2, 3]:                    [SUCCESS]

# pause  the traffic on all port
>pause -a                                                               #<3>

# resume  the traffic on all port
>resume -a                                                              #<4>

# stop traffic on all port      
>stop -a                                                                #<5>

# show dynamic statistic 
>tui
----
<1> Connect to TRex server assume server at local machine 
<2> Start the traffic on all the ports in 10mbps. you can try with 14MPPS
<3> Pause the traffic 
<4> Resume
<5> Stop on all the ports


To look into the streams using `streams -a`

.Streams
[source,bash]
----
trex>streams -a
Port 0:

    ID |     packet type     |  length  |       mode       |  rate     | next stream 
  -----------------------------------------------------------------------------------
    1  | Ethernet:IP:UDP:Raw |       56 |    Continuous    |  1.00 pps |      -1     

Port 1:

    ID |     packet type     |  length  |       mode       |  rate     | next stream 
  -----------------------------------------------------------------------------------
    1  | Ethernet:IP:UDP:Raw |       56 |    Continuous    |  1.00 pps |      -1     

Port 2:

    ID |     packet type     |  length  |       mode       |  rate     | next stream 
  -----------------------------------------------------------------------------------
    1  | Ethernet:IP:UDP:Raw |       56 |    Continuous    |  1.00 pps |      -1     

Port 3:

    ID |     packet type     |  length  |       mode       |  rate     | next stream 
  -----------------------------------------------------------------------------------
    1  | Ethernet:IP:UDP:Raw |       56 |    Continuous    |  1.00 pps |      -1     
----


to get help on a command run `command --help` 

to look into general statistics

[source,bash]
----
TRex >tui
Global Statistics

Connection  : localhost, Port 4501 
Version     : v1.93, UUID: N/A     
Cpu Util    : 0.2%                 
            :                      
Total Tx L2 : 40.01 Mb/sec         
Total Tx L1 : 52.51 Mb/sec         
Total Rx    : 40.01 Mb/sec         
Total Pps   : 78.14 Kpkt/sec       
            :                      
Drop Rate   : 0.00 b/sec           
Queue Full  : 0 pkts               

Port Statistics

   port    |         0          |         1          |     
 --------------------------------------------------------
 owner      |             hhaim |             hhaim |    
 state      |            ACTIVE |            ACTIVE |    
 --         |                   |                   |    
 Tx bps L2  |        10.00 Mbps |        10.00 Mbps |    
 Tx bps L1  |        13.13 Mbps |        13.13 Mbps |    
 Tx pps     |        19.54 Kpps |        19.54 Kpps |    
 Line Util. |            0.13 % |            0.13 % |    
 ---        |                   |                   |    
 Rx bps     |        10.00 Mbps |        10.00 Mbps |    
 Rx pps     |        19.54 Kpps |        19.54 Kpps |    
 ----       |                   |                   |    
 opackets   |           1725794 |           1725794 |    
 ipackets   |           1725794 |           1725794 |    
 obytes     |         110450816 |         110450816 |    
 ibytes     |         110450816 |         110450816 |    
 tx-bytes   |         110.45 MB |         110.45 MB |    
 rx-bytes   |         110.45 MB |         110.45 MB |    
 tx-pkts    |        1.73 Mpkts |        1.73 Mpkts |    
 rx-pkts    |        1.73 Mpkts |        1.73 Mpkts |    
 -----      |                   |                   |    
 oerrors    |                 0 |                 0 |    
 ierrors    |                 0 |                 0 |    

 status:  /

 browse:     'q' - quit, 'g' - dashboard, '0-3' - port display
 dashboard:  'p' - pause, 'c' - clear, '-' - low 5%, '+' - up 5%, 
----

[NOTE] 
=====================================================================
The SRC/DST MAC addrees are taken from /etc/trex_cfg.yaml. if you want to change them to be different just add Ether(dst="00:00:dd:dd:00:01") with your destination
=====================================================================

==== Tutorial: Connect from a remote server 

*Goal*:: Console connect from a remote machine to TRex server 

*Check that TRex server is up*::

Make sure TRex server is running, if not run trex in interactive mode

[source,bash]
----
$sudo ./t-rex-64 -i
----

*Connect with Console*::

From remote machine you can run this with `-s` flag

[source,bash]
----
$trex-console -s csi-kiwi-02  #<1>
----
<1> TRex server is csi-kiwi-02

if the default python is not 64bit/2.7.x you can change the *PYTHON* environment variable using

.tcsh
[source,bash]
----
setenv PYTHON /bin/python     #tcsh
----

.bash
[source,bash]
----
extern PYTHON=/bin/mypython    #bash
----

[NOTE]
=====================================================================
Client machine should run Python 2.7 and Python 64bit version. Cisco CEL/ADS is supported. Python 3.0 support in WIP
=====================================================================

==== Tutorial: Source and Destination MAC address

*Goal*:: Change source/destination MAC addrees 

Each TRex port has a source and destination MAC (DUT) configured in /etc/trex_cfg.yaml. 
The source MAC is not necessarily the hardware MAC address configured in eeprom.
By default those MAC (source and destination) is taken.
In case a user configures a source or destination MAC explicitly this MAC will take precedence  


.MAC addrees
[format="csv",cols="2^,2^,2^", options="header",width="100%"]
|=================
Scapy , Source MAC,Destination MAC
Ether() , trex_cfg (src),trex_cfg(dst)
Ether(src="00:bb:12:34:56:01"),"00:bb:12:34:56:01",trex_cfg(dst)
Ether(dst="00:bb:12:34:56:01"),trex_cfg(src),"00:bb:12:34:56:01"
|=================

For example 

*file*:: link:{github_stl_path}/udp_1pkt_1mac_override.py[stl/udp_1pkt_1mac_override.py]

[source,python]
----
    def create_stream (self):

        base_pkt =  Ether(src="00:bb:12:34:56:01")/      <1>
                    IP(src="16.0.0.1",dst="48.0.0.1")/
                    UDP(dport=12,sport=1025)  
----
<1> Don't take TRex port src interface MAC instead replace it with 00:bb:12:34:56:01

[IMPORTANT]
=====================================
TRex ports  will receive a packet only when the packet will have a destination MAC of port defined in the `/etc/trex_cfg.yaml`. To configure the port to be promiscuous  and get all the packets on the line you can configure it from API or from Console with `portattr -a --prom`  
=====================================

To show the port mode 

[source,bash]
----
trex>portattr -a --prom                                             #<1> 
trex>stats --ps
Port Status

     port       |          0           |          1           |     
  ---------------------------------------------------------------
driver          |    rte_ixgbe_pmd     |    rte_ixgbe_pmd     |     
maximum         |       10 Gb/s        |       10 Gb/s        |     
status          |         IDLE         |         IDLE         |     
promiscuous     |         off          |         off          |     #<2>
  --            |                      |                      | 
HW src mac      |  90:e2:ba:36:33:c0   |  90:e2:ba:36:33:c1   | 
SW src mac      |  00:00:00:01:00:00   |  00:00:00:01:00:00   | 
SW dst mac      |  00:00:00:01:00:00   |  00:00:00:01:00:00   | 
  ---           |                      |                      |     
PCI Address     |     0000:03:00.0     |     0000:03:00.1     |     
NUMA Node       |          0           |          0           |   
----
<1> Configure all the ports to be promiscuous
<2> Check port promiscuous mode

==== Tutorial: Python automation 

*Goal*:: Simple automation test using Python from remote or local machine 

Python API examples are located here: `automation/trex_control_plane/stl/examples`.

Python API library is located here: `automation/trex_control_plane/stl/trex_stl_lib`

The Console is using the python API library to interact with TRex server and the protocol is JSON-RPC2 over ZMQ

*file*:: link:{github_stl_examples_path}/stl_bi_dir_flows.py[stl_bi_dir_flows.py]


[source,python]
----
import stl_path                                                            <1>
from trex_stl_lib.api import *                                             <2>               

import time
import json

# simple packet creation                                                   <3>
def create_pkt (size, direction):

    ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"},
                'dst': {'start': "8.0.0.1",  'end': "8.0.0.254"}}

    if (direction == 0):
        src = ip_range['src']
        dst = ip_range['dst']
    else:
        src = ip_range['dst']
        dst = ip_range['src']

    vm = [
        # src                                                               <4>
        STLVmFlowVar(name="src",
                     min_value=src['start'],
                     max_value=src['end'],
                     size=4,op="inc"),
        STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src"),

        # dst
        STLVmFlowVar(name="dst",
                     min_value=dst['start'],
                     max_value=dst['end'],
                     size=4,op="inc"),
        STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst"),

        # checksum
        STLVmFixIpv4(offset = "IP")
        ]


    base = Ether()/IP()/UDP()
    pad = max(0, len(base)) * 'x'

    return STLPktBuilder(pkt = base/pad,
                         vm  = vm)

                                                                               <5>
def simple_burst ():
 
    # create client
    c = STLClient() 
                    # username/server can be changed those are the default
                    # username = common.get_current_user(),
                    # server = "localhost"
                    # STLClient(server = "my_server",username ="trex_client") for example 
    passed = True

    try:
        # turn this on for some information
        #c.set_verbose("high")

        # create two streams
        s1 = STLStream(packet = create_pkt(200, 0),
                       mode = STLTXCont(pps = 100))

        # second stream with a phase of 1ms (inter stream gap)
        s2 = STLStream(packet = create_pkt(200, 1),
                       isg = 1000,
                       mode = STLTXCont(pps = 100))


        # connect to server
        c.connect()                                                                <5>
                                                                                        
        # prepare our ports (my machine has 0 <--> 1 with static route)
        c.reset(ports = [0, 1]) #  Acquire port 0,1 for $USER                      <6>

        # add both streams to ports
        c.add_streams(s1, ports = [0])
        c.add_streams(s2, ports = [1])

        # clear the stats before injecting
        c.clear_stats()

        # choose rate and start traffic for 10 seconds on 5 mpps
        print "Running 5 Mpps on ports 0, 1 for 10 seconds..."
        c.start(ports = [0, 1], mult = "5mpps", duration = 10)                     <7>

        # block until done
        c.wait_on_traffic(ports = [0, 1])                                          <8>

        # read the stats after the test
        stats = c.get_stats()                                                      <9>

        print json.dumps(stats[0], indent = 4, separators=(',', ': '), sort_keys = True)
        print json.dumps(stats[1], indent = 4, separators=(',', ': '), sort_keys = True)

        lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
        lost_b = stats[1]["opackets"] - stats[0]["ipackets"]                       

        print "\npackets lost from 0 --> 1:   {0} pkts".format(lost_a)
        print "packets lost from 1 --> 0:   {0} pkts".format(lost_b)

        if (lost_a == 0) and (lost_b == 0):
            passed = True
        else:
            passed = False

    except STLError as e:
        passed = False
        print e

    finally:
        c.disconnect()                                                             <10>    

    if passed:
        print "\nTest has passed :-)\n"
    else:
        print "\nTest has failed :-(\n"


# run the tests
simple_burst()
----
<1> import the stl_path. you should *fix* the path to point to your stl_trex library path 
<2> import trex Stateless library. path should be fixed 
<3> create packet per direction using Scapy
<4> This is something more advanced will be explained later
<5> Connect to local TRex   username , server can be added 
<6> Acquire the ports  
<7> load the profile and start the traffic 
<8> Wait for the traffic to, be finished. There is a polling function so you can test do something while waiting 
<9> Get port statistics
<10> Disconnect


==== Tutorials HLT Python API 

HLT Python API is a layer on top the native layer. It supports the standard Cisco traffic generator API
See more in Cisco/IXIA/Spirent documentation
TRex supported a limited number of HLTAPI arguments and the recommendation is to use the native API due to the flexibility and simplicity. 
IXIA for example, has a book of ~2000 pages for specifying all the HLTAPI mode of operations. One of the reasons for the 2000 pages is that in the API there is no clear separation between the definition of the template packet, and the fields that need to be changed and the mode of transmission. This creates a bloat of arguments that need to be documented.

The supported classs are:

* Device Control
** connect
** cleanup_session
** device_info
** info
* Interface
** interface_config
** interface_stats
* Traffic
** traffic_config - not all arguments are supported  
** traffic_control
** traffic_stats


*file*:: link:{github_stl_examples_path}/hlt_udp_simple.py[hlt_udp_simple.py]


[source,python]
----

import sys
import argparse
import stl_path
from trex_stl_lib.api import *                                          <1>
from trex_stl_lib.trex_stl_hltapi import *                              <2>


if __name__ == "__main__":
    parser = argparse.ArgumentParser(usage=""" 
    Connect to TRex and send burst of packets

    examples

     hlt_udp_simple.py -s 9000 -d 30

     hlt_udp_simple.py -s 9000 -d 30 -rate_percent 10

     hlt_udp_simple.py -s 300 -d 30 -rate_pps 5000000

     hlt_udp_simple.py -s 800 -d 30 -rate_bps 500000000 --debug

     then run the simulator on the output 
       ./stl-sim -f example.yaml -o a.pcap  ==> a.pcap include the packet

    """,
    description="Example for TRex HLTAPI",
    epilog=" based on hhaim's stl_run_udp_simple example");

    parser.add_argument("--ip", 
                        dest="ip",
                        help='Remote trex ip',
                        default="127.0.0.1",
                        type = str)

    parser.add_argument("-s", "--frame-size", 
                        dest="frame_size",
                        help='L2 frame size in bytes without FCS',
                        default=60,
                        type = int,)

    parser.add_argument('-d','--duration', 
                        dest='duration',
                        help='duration in second ',
                        default=10,
                        type = int,)

    parser.add_argument('--rate-pps', 
                        dest='rate_pps',
                        help='speed in pps',
                        default="100")

    parser.add_argument('--src', 
                        dest='src_mac',
                        help='src MAC',
                        default='00:50:56:b9:de:75')

    parser.add_argument('--dst', 
                        dest='dst_mac',
                        help='dst MAC',
                        default='00:50:56:b9:34:f3')

    args = parser.parse_args();

    hltapi = CTRexHltApi()
    print 'Connecting to TRex'
    res = hltapi.connect(device = args.ip, port_list = [0, 1], reset = True, break_locks = True)
    check_res(res)
    ports = res['port_handle']
    if len(ports) < 2:
        error('Should have at least 2 ports for this test')
    print 'Connected, acquired ports: %s' % ports

    print 'Creating traffic'

    res = hltapi.traffic_config(mode = 'create', bidirectional = True,
                                port_handle = ports[0], port_handle2 = ports[1],
                                frame_size = args.frame_size,
                                mac_src = args.src_mac, mac_dst = args.dst_mac,
                                mac_src2 = args.dst_mac, mac_dst2 = args.src_mac,
                                l3_protocol = 'ipv4',
                                ip_src_addr = '10.0.0.1', ip_src_mode = 'increment', ip_src_count = 254,
                                ip_dst_addr = '8.0.0.1', ip_dst_mode = 'increment', ip_dst_count = 254,
                                l4_protocol = 'udp',
                                udp_dst_port = 12, udp_src_port = 1025,
                                stream_id = 1, # temporary workaround, add_stream does not return stream_id
                                rate_pps = args.rate_pps,
                                )
    check_res(res)

    print 'Starting traffic'
    res = hltapi.traffic_control(action = 'run', port_handle = ports[:2])
    check_res(res)
    wait_with_progress(args.duration)

    print 'Stopping traffic'
    res = hltapi.traffic_control(action = 'stop', port_handle = ports[:2])
    check_res(res)

    res = hltapi.traffic_stats(mode = 'aggregate', port_handle = ports[:2])
    check_res(res)
    print_brief_stats(res)
    
    res = hltapi.cleanup_session(port_handle = 'all')
    check_res(res)

    print 'Done' 
----
<1> import Native TRex API
<2> import HLT   TRex



                
==== Tutorial: Simple IPv4/UDP packet - Simulator 

*Goal*:: Demonstrates the most basic use case using TRex simulator


The simulator is a tool called `stil-sim` that is part of the TRex package. 
It is a python script that calls an executable. 
The executable should run on the same machine that TRex image run (it won't run on an older Linux distributions).

Using the simulator you can :

* Test your traffic profiles before running it on TRex. 
* It can generate the output pcap file 
* Simulate number of threads 
* Convert from one type of profile to another 
* Convert any profile to JSON (API)

let's take this profile  

*file*:: link:{github_stl_path}/udp_1pkt_simple.py[stl/udp_1pkt_simple.py]

[source,python]
----
from trex_stl_lib.api import *                                  

class STLS1(object):

    def create_stream (self):

        return STLStream( 
            packet = 
                    STLPktBuilder(
                        pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/
                                UDP(dport=12,sport=1025)/(10*'x')                       <1>                
                    ),
             mode = STLTXCont())                                                        <2>


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


# dynamic load - used for trex console or simulator
def register():                                                                         <3>        
    return STLS1()
----
<1> Define the packet, in this case it IP/UDP with 10 bytes of 'x'
<2> Mode is Continuous with rate of 1 PPS (default rate is 1 PPS)
<3> Each Traffic profile module should have a `register` function

Now let try to run it throw TRex simulator limiting the number of packets to 10 

[source,bash]
----
$ ./stl-sim -f stl/udp_1pkt_simple.py -o b.pcap -l 10 
  executing command: 'bp-sim-64-debug --pcap --sl --cores 1 --limit 5000 -f /tmp/tmpq94Tfx -o b.pcap'

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

  image type:               debug
  I/O output:               b.pcap
  packet limit:             10
  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:
  -------------------

  simulated 10 packets
  written 10 packets to 'b.pcap'
----


The following figure presents the output pcap file

image::images/stl_tut_1.png[title="Wireshark Tutorial 1 output",align="left",width={p_width}, link="images/stl_tut_1.png.png"]

.To look into the JSON command to the server 
[source,bash]
----
$./stl-sim -f stl/udp_1pkt_simple.py --json
[
    {
        "id": 1,
        "jsonrpc": "2.0",
        "method": "add_stream",
        "params": {
            "handler": 0,
            "port_id": 0,
            "stream": {
                "action_count": 0,
                "enabled": true,
                "flags": 0,
                "isg": 0.0,
                "mode": {
                    "rate": {
                        "type": "pps",
                        "value": 1.0
                    },
                    "type": "continuous"
                },
                "next_stream_id": -1,
                "packet": {
                    "binary": "AAAAAQAAAAAAAgAACABFAAAmAA",
                    "meta": ""
                },
                "rx_stats": {
                    "enabled": false
                },
                "self_start": true,
                "vm": {
                    "instructions": [],
                    "split_by_var": ""
                }
            },
            "stream_id": 1
        }
    },
    {
        "id": 1,
        "jsonrpc": "2.0",
        "method": "start_traffic",
        "params": {
            "duration": -1,
            "force": true,
            "handler": 0,
            "mul": {
                "op": "abs",
                "type": "raw",
                "value": 1.0
            },
            "port_id": 0
        }
    }
]
----

For more detailed on Stream definition see RPC specification link:trex_rpc_server_spec.html#_add_stream[here]  

.To convert the profile into YAML format
[source,bash]
----
$./stl-sim -f stl/udp_1pkt_simple.py --yaml
- stream:
    action_count: 0
    enabled: true
    flags: 0
    isg: 0.0
    mode:
      pps: 1.0
      type: continuous
    packet:
      binary: AAAAAQAAAAAAAgAACABFAAAmAAEAAEARO
      meta: ''
    rx_stats:
      enabled: false
    self_start: true
    vm:
      instructions: []
      split_by_var: ''
----

To look into the Packet detail try --pkt option (using scapy)

[source,bash]
----
$./stl-sim -f stl/udp_1pkt_simple.py --pkt
 =======================
 Stream 0
 =======================
###[ Ethernet ]###
  dst       = 00:00:00:01:00:00
  src       = 00:00:00:02:00:00
  type      = IPv4
###[ IP ]###
     version   = 4L
     ihl       = 5L
     tos       = 0x0
     len       = 38
     id        = 1
     flags     = 
     frag      = 0L
     ttl       = 64
     proto     = udp
     chksum    = 0x3ac5
     src       = 16.0.0.1
     dst       = 48.0.0.1
     \options   \
###[ UDP ]###
        sport     = blackjack
        dport     = 12
        len       = 18
        chksum    = 0x6161
###[ Raw ]###
           load      = 'xxxxxxxxxx'
0000   00 00 00 01 00 00 00 00  00 02 00 00 08 00 45 00   ..............E.
0010   00 26 00 01 00 00 40 11  3A C5 10 00 00 01 30 00   .&....@.:.....0.
0020   00 01 04 01 00 0C 00 12  61 61 78 78 78 78 78 78   ........aaxxxxxx
0030   78 78 78 78                                        xxxx
----

To convert any profile type to native again use the `--native` option

.Input YAML format
[source,python]
----
$more stl/yaml/imix_1pkt.yaml
- name: udp_64B
  stream:
    self_start: True
    packet:
      pcap: udp_64B_no_crc.pcap  # pcap should not include CRC
    mode:
      type: continuous
      pps: 100
----

.Convert to Native
[source,bash]
----
$./stl-sim -f stl/yaml/imix_1pkt.yaml --native
----


.Output Native
[source,python]
----
# !!! Auto-generated code !!!
from trex_stl_lib.api import *

class STLS1(object):
    def get_streams(self):
        streams = []
        
        packet = (Ether(src='00:de:01:0a:01:00', dst='00:50:56:80:0d:28', type=2048) / 
                  IP(src='101.0.0.1', proto=17, dst='102.0.0.1', chksum=28605, len=46, flags=2L, ihl=5L, id=0) / 
                  UDP(dport=2001, sport=2001, len=26, chksum=1176) / 
                  Raw(load='\xde\xad\xbe\xef\x00\x01\x06\x07\x08\x09\x0a\x0b\x00\x9b\xe7\xdb\x82M'))
        vm = CTRexScRaw([], split_by_field = '')
        stream = STLStream(packet = CScapyTRexPktBuilder(pkt = packet, vm = vm),
                           name = 'udp_64B',
                           mac_src_override_by_pkt = 0,
                           mac_dst_override_mode = 0,
                           mode = STLTXCont(pps = 100))
        streams.append(stream)

        return streams

def register():
    return STLS1()
----

*Discussion*::

The following are the main traffic profiles formats. The native is the preferred one. There is a separation between how the traffic is defined and how to control/activate it. The API/Console/GUI can load a traffic profile and start/stop/get a statistic. Due to this separation it is possible to share traffic profiles.

.Traffic profiles formats
[cols="1^,1^,10<", options="header",width="80%"]
|=================
| Profile Type       | Format | Description  
| Native             | Python | A native Python like. Have the most flexibility. any format can be converted to native using `stl-sim` using --native option
| HLT                | Python | HLT arguments like
| YAML               | YAML   | It is the common denominator traffic profile. We suggest not to use it by human as it is not possible to compose packet using scapy. it is used to move profile between GUI and Console or API. It can be converted to native using the stl-sim using --native switch 
|=================


=== Traffic profile Tutorials

==== Tutorial: Simple Interleave streams

*Goal*:: Demonstrate number of interleave streams

The following example demonstrates 3 streams with different rates (pps=10,20,40) and different start time ISG (0,25msec,50msec)

*file*:: link:{github_stl_path}/simple_3pkt.py[stl/simple_3pkt.py]

[source,python]
----
    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)       <1>
        base_pkt1 =  Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt2 =  Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'


        return STLProfile( [ STLStream( isg = 0.0, 
                                        packet = STLPktBuilder(pkt = base_pkt/pad),
                                        mode = STLTXCont( pps = 10),                         <2>
                                        ), 

                             STLStream( isg = 25000.0, #defined in usec, 25 msec
                                        packet  = STLPktBuilder(pkt = base_pkt1/pad),
                                        mode    = STLTXCont( pps = 20),                      <3>
                                        ),

                             STLStream(  isg = 50000.0,#defined in usec, 50 msec
                                         packet = STLPktBuilder(pkt = base_pkt2/pad),
                                         mode    = STLTXCont( pps = 40)                      <4>
                                         
                                        )
                            ]).get_streams()
----
<1> Define template packets using scapy
<2> Define streams with rate of 10
<3> Define streams with rate of 20
<4> Define streams with rate of 40


The output::
The folowing figure present the output 

image::images/stl_inter.png[title="Interleave streams",align="left",width={p_width}, link="images/stl_inter.png"]
              
Discussion:: 

1. stream #1 schedule a packet each 100msec 
2. stream #2 schedule a packet each 50msec
3. stream #3 schedule a packet each 25msec
4. Stream #2 start after 25msec relative to stream #1
5. Stream #3 start after 50msec relative to stream #1

You can use the simulator to look into the details (pcap file)

[source,bash]
----
$./stl-sim -f stl/simple_3pkt.py -o b.pcap -l 200
----

or run it from Console on a TRex

[source,bash]
----
trex>start -f stl/simple_3pkt.py -m 10mbps -a 
----

==== Tutorial:  Multi burst streams - action next stream   

*Goal*:: profile with stream that trigger a stream 

The following example demonstrates 

1. More than one stream 
2. Burst of 10 packets
3. Stream activate a Stream (self_start=False)

*file*:: link:{github_stl_path}/burst_3pkt_60pkt.py[stl/burst_3pkt_60pkt.py]


[source,python]
----
    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt1 =  Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt2 =  Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'


        return STLProfile( [ STLStream( isg = 10.0, # star in delay 
                                        name    ='S0',
                                        packet = STLPktBuilder(pkt = base_pkt/pad),
                                        mode = STLTXSingleBurst( pps = 10, total_pkts = 10),      <1>
                                        next = 'S1'), # point to next stream 

                             STLStream( self_start = False, # stream is  disabled enable trow S0  <2>
                                        name    ='S1',
                                        packet  = STLPktBuilder(pkt = base_pkt1/pad),
                                        mode    = STLTXSingleBurst( pps = 10, total_pkts = 20),
                                        next    = 'S2' ),                                         

                             STLStream(  self_start = False, # stream is  disabled enable trow S0 <3>
                                         name   ='S2',
                                         packet = STLPktBuilder(pkt = base_pkt2/pad),
                                         mode = STLTXSingleBurst( pps = 10, total_pkts = 30 )
                                        )
                            ]).get_streams()

----
<1> Stream S0 is with self_start=True start after 10 sec 
<2> S1 with self_start=False. S0 activate it
<3> S2 is activate by S1

To run the simulator run this command

[source,bash]
----
$ ./stl-sim -f stl/stl/burst_3pkt_600pkt.py -o b.pcap 
----

The pcap file should have 60 packets. The first 10 packets has src_ip=16.0.0.1. The next 20 packets has src_ip=16.0.0.2. The next 30 packets has src_ip=16.0.0.3

This profile can be run from Console using this command 

[source,bash]
----
TRex>start -f stl/stl/burst_3pkt_600pkt.py --port 0
----

==== Tutorial: Multi Burst mode

*Goal* : Learn Multi burst transmit mode  

*file*:: link:{github_stl_path}/multi_burst_2st_1000pkt.py[stl/multi_burst_2st_1000pkt.py]

[source,python]
----

    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt1 =  Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'


        return STLProfile( [ STLStream( isg = 10.0, # star in delay                                        <1>
                                        name    ='S0',
                                        packet = STLPktBuilder(pkt = base_pkt/pad),
                                        mode = STLTXSingleBurst( pps = 10, total_pkts = 10),
                                        next = 'S1'), # point to next stream 

                             STLStream( self_start = False, # stream is  disabled enable trow S0           <2>
                                        name    ='S1',
                                        packet  = STLPktBuilder(pkt = base_pkt1/pad),
                                        mode    = STLTXMultiBurst( pps = 1000,
                                                                   pkts_per_burst = 4,
                                                                   ibg = 1000000.0,                         
                                                                   count = 5)
                                        )

                            ]).get_streams()

----
<1> Stream S0 wait 10 usec(isg) and send burst of 10 packet in 10 PPS rate
<2> Multi burst of 5 bursts of 4 packets with a inter burst gap of one second 
 

image::images/stl_tut_4.png[title="Streams example",align="left",width={p_width}, link="images/stl_tut_4.png"]


==== Tutorial: Loops of streams

*Goal* : Demonstrate a limited loop of streams

*file*:: link:{github_stl_path}/burst_3st_loop_x_times.py[stl/burst_3st_loop_x_times.py]

[source,python]
----
    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt1 =  Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        base_pkt2 =  Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'


        return STLProfile( [ STLStream( isg = 10.0, # star in delay 
                                        name    ='S0',
                                        packet = STLPktBuilder(pkt = base_pkt/pad),
                                        mode = STLTXSingleBurst( pps = 10, total_pkts = 1),
                                        next = 'S1'), # point to next stream 

                             STLStream( self_start = False, # stream is  disabled enable trow S0
                                        name    ='S1',
                                        packet  = STLPktBuilder(pkt = base_pkt1/pad),
                                        mode    = STLTXSingleBurst( pps = 10, total_pkts = 2),
                                        next    = 'S2' ),

                             STLStream(  self_start = False, # stream is  disabled enable trow S0
                                         name   ='S2',
                                         packet = STLPktBuilder(pkt = base_pkt2/pad),
                                         mode = STLTXSingleBurst( pps = 10, total_pkts = 3 ),
                                         action_count = 2, # loop 2 times                       <1>
                                         next    = 'S0' # back to S0 loop
                                        )
                            ]).get_streams()

----
<1> go back to S0 but limit it to 2 loops


==== Tutorial: IMIX with UDP packets directional 

*Goal* : Demonstrate how to create IMIX 

This profile has 3 streams each with different size packet. The rate is different for each stream/size see link:https://en.wikipedia.org/wiki/Internet_Mix[here]

*file*:: link:{github_stl_path}/imix.py[stl/imix.py]

[source,python]
----
    def __init__ (self):
        # default IP range
        self.ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"},
                         'dst': {'start': "8.0.0.1",  'end': "8.0.0.254"}}

        # default IMIX properties
        self.imix_table = [ {'size': 60,   'pps': 28,  'isg':0 },
                            {'size': 590,  'pps': 20,  'isg':0.1 },
                            {'size': 1514, 'pps': 4,   'isg':0.2 } ]


    def create_stream (self, size, pps, isg, vm ):
        # create a base packet and pad it to size
        base_pkt = Ether()/IP()/UDP()
        pad = max(0, size - len(base_pkt)) * 'x'

        pkt = STLPktBuilder(pkt = base_pkt/pad,
                            vm = vm)

        return STLStream(isg = isg,
                         packet = pkt,
                         mode = STLTXCont(pps = pps))


    def get_streams (self, direction = 0):                                      <1>

        if direction == 0:                                                      <2>
            src = self.ip_range['src']
            dst = self.ip_range['dst']
        else:
            src = self.ip_range['dst']
            dst = self.ip_range['src']

        # construct the base packet for the profile

        vm =[                                                                   <3>
            # src
            STLVmFlowVar(name="src",
                         min_value=src['start'],
                         max_value=src['end'],
                         size=4,op="inc"),
            STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src"),

            # dst
            STLVmFlowVar(name="dst",
                         min_value=dst['start'],
                         max_value=dst['end'],
                         size=4,
                         op="inc"),
            STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst"),

            # checksum
            STLVmFixIpv4(offset = "IP")

            ]

        # create imix streams
        return [self.create_stream(x['size'], x['pps'],x['isg'] , vm) for x in self.imix_table]
----
<1> Base on the direction, we will construct a diffrent stream (replace src and dest)
<2> Even port id has direction==0 and odd has direction==1
<3> We didn't explain this yet. but this is a Field Engine program to change fields inside the packets

==== Tutorial: Field Engine, Syn attack  

The following example demonstrates changing packet fields. 
The Field Engine (FE) has limited number of instructions/operation for supporting most use cases. There is a plan to add LuaJIT to be more flexiable in the cost of performance.
The FE can allocate stream variable in Stream context. Write a stream variable to a packet offset, change packet size etc.

*Some examples for what can be done:*

* Change ipv4.tos 1-10
* Change packet size to be random in range 64-9K
* Create range of flows (change src_ip,dest_ip,src_port,dest_port) 
* Update IPv4 checksum 

for more info see link:trex_rpc_server_spec.html#_object_type_em_vm_em_a_id_vm_obj_a[here]

The following example demonstrates creating SYN attack from many src to one server.

*file*:: link:{github_stl_path}/syn_attack.py[stl/syn_attack.py]

[source,python]
----
    def create_stream (self):

        # TCP SYN
        base_pkt  = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")      <1>


        # vm
        vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src", 
                                              min_value="16.0.0.0", 
                                              max_value="18.0.0.254", 
                                              size=4, op="random"),         <2>

                            STLVmFlowVar(name="src_port", 
                                              min_value=1025, 
                                              max_value=65000, 
                                              size=2, op="random"),         <3>

                           STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ), <4>

                           STLVmFixIpv4(offset = "IP"), # fix checksum              <5>

                           STLVmWrFlowVar(fv_name="src_port",                       <6>
                                                pkt_offset= "TCP.sport") # fix udp len  

                          ]
                       )

        pkt = STLPktBuilder(pkt = base_pkt,
                            vm = vm)

        return STLStream(packet = pkt,
                         random_seed = 0x1234,# can be remove. will give the same random value any run
                         mode = STLTXCont())
----
<1> Create SYN packet using Scapy 
<2> Define stream variable name=ip_src, 4 bytes size for IPv4. 
<3> Define stream variable name=src_port, 2 bytes size for port. 
<4> Write ip_src stream var into `IP.src` packet offset. Scapy calculate the offset. We could gave `IP:1.src" for second IP header in the packet
<5> Fix IPv4 checksum. here we provide the header name `IP` we could gave `IP:1` for second IP
<6> Update TCP src port- TCP checksum is not updated here

WARNING: Original Scapy does not have the capability to calculate offset for a header/field by name. This offset capability won't work for all the cases because there could be complex cases that Scapy rebuild the header. In such cases put offset as a number

The output pcap file field can be seen here 

.Pcap file output 
[format="csv",cols="1^,2<,2<", options="header",width="40%"]
|=================
pkt,Client IPv4,Client Port
 1  , 17.152.71.218  , 5814
 2  , 17.7.6.30      , 26810
 3  , 17.3.32.200    , 1810 
 4  , 17.135.236.168 , 55810 
 5  , 17.46.240.12   , 1078  
 6  , 16.133.91.247  , 2323
|=================


==== Tutorial: Field Engine, Tuple Generator 

The following example demonstrates creating multiply flows from the same packet template.
The TupleGenerator instructions are used to create two stream variables with IP, port see link:trex_rpc_server_spec.html#_object_type_em_vm_em_a_id_vm_obj_a[here]

*file*:: link:{github_stl_path}/udp_1pkt_tuple_gen.py[stl/udp_1pkt_tuple_gen.py]

[source,python]
----
        base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)        

        pad = max(0, size - len(base_pkt)) * 'x'
                             
        vm = CTRexScRaw( [   STLVmTupleGen ( ip_min="16.0.0.1",                              <1>
                                             ip_max="16.0.0.2", 
                                             port_min=1025, 
                                             port_max=65535,
                                             name="tuple"), # define tuple gen 

                             STLVmWrFlowVar (fv_name="tuple.ip", pkt_offset= "IP.src" ),     <2>
                             STLVmFixIpv4(offset = "IP"),                                
                             STLVmWrFlowVar (fv_name="tuple.port", pkt_offset= "UDP.sport" ) <3>
                                  ]
                              );

        pkt = STLPktBuilder(pkt = base_pkt/pad,
                            vm = vm)
----
<1> Define struct with two dependent varibles tuple.ip tuple.port 
<2> Write tuple.ip to IPv4 src field offset
<3> Write tuple.port to UDP header. You should set UDP.checksum to zero  


.Pcap file output 
[format="csv",cols="1^,2^,1^", options="header",width="40%"]
|=================
pkt,Client IPv4,Client Port
 1  , 16.0.0.1 , 1025
 2  , 16.0.0.2 , 1025
 3  , 16.0.0.1 , 1026
 4  , 16.0.0.2 , 1026
 5  , 16.0.0.1 , 1027
 6  , 16.0.0.2,  1027
|=================

* Number of clients are two. 16.0.0.1 and 16.0.0.2
* Number of flows is limited to 129020 (2*65535-1025)
* The stream variable size should match the size of the FlowVarWr instruction

==== Tutorial: Field Engine, write to a bit-field packet  

The following example demonstrates a way to write a stream variable to a bit field packet variables.
In this example MPLS label field will be changed.

.MPLS header 
[cols="32", halign="center",width="50%"] 
|==== 
20+<|Label 3+<|TC 1+<|S 8+<|TTL| 
0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|
|==== 

*file*:: link:{github_stl_path}/udp_1pkt_mpls_vm.py[stl/udp_1pkt_mpls_vm.py]

[source,python]
----

    def create_stream (self):
        # 2 MPLS label the internal with  s=1 (last one)
        pkt =  Ether()/
               MPLS(label=17,cos=1,s=0,ttl=255)/
               MPLS(label=0,cos=1,s=1,ttl=12)/
               IP(src="16.0.0.1",dst="48.0.0.1")/
               UDP(dport=12,sport=1025)/('x'*20)

        vm = CTRexScRaw( [ STLVmFlowVar(name="mlabel",                                 <1>
                                        min_value=1, 
                                        max_value=2000, 
                                        size=2, op="inc"), # 2 bytes var               <2>
                           STLVmWrMaskFlowVar(fv_name="mlabel",                      
                                              pkt_offset= "MPLS:1.label",              <3>
                                              pkt_cast_size=4, 
                                              mask=0xFFFFF000,shift=12) # write to 20bit MSB
                          ]
                       )

        # burst of 100 packets
        return STLStream(packet = STLPktBuilder(pkt = pkt ,vm = vm),
                         mode = STLTXSingleBurst( pps = 1, total_pkts = 100) )

----
<1> Define varible size of 2 bytes
<2> Write the stream variable label with a shift of 12 bits and with 20bit MSB mask. Cast the stream variables of 2 bytes to 4 bytes
<3> Second MPLS header should be changed 


==== Tutorial: Field Engine, Random packet size 

The following example demonstrates a way to to change packet size to be a random size.
The way to do it is:
1. Define template packet with maximum size 
2. Trim the packet to the size you want 
3. Update the packet fields to the new size 

*file*:: link:{github_stl_path}/udp_rand_len_9k.py[stl/udp_rand_len_9k.py]

[source,python]
----

    def create_stream (self):
        # pkt 
        p_l2  = Ether();
        p_l3  = IP(src="16.0.0.1",dst="48.0.0.1")
        p_l4  = UDP(dport=12,sport=1025)
        pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
        base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))

        l3_len_fix =-(len(p_l2));
        l4_len_fix =-(len(p_l2/p_l3));


        # vm
        vm = CTRexScRaw( [ STLVmFlowVar(name="fv_rand",                            <1>
                                        min_value=64, 
                                        max_value=len(base_pkt), 
                                        size=2, 
                                        op="random"),

                           STLVmTrimPktSize("fv_rand"), # total packet size        <2>

                           STLVmWrFlowVar(fv_name="fv_rand",                       <3>
                                          pkt_offset= "IP.len", 
                                          add_val=l3_len_fix), # fix ip len 

                           STLVmFixIpv4(offset = "IP"),                               

                           STLVmWrFlowVar(fv_name="fv_rand",                       <4>
                                          pkt_offset= "UDP.len", 
                                          add_val=l4_len_fix) # fix udp len  
                          ]
                       )
----
<1> Define a random stream variable with maximum size of the packet
<2> Trim the packet size to the fv_rand value 
<3> fix ip.len 
<4> fix udp.len 


==== Tutorial: New Scapy header  

The following example demonstrates a way to use a header that is not supported by Scapy in default. 
In this example we will show VXLAN support.

*file*:: link:{github_stl_path}/udp_1pkt_vxlan.py[stl/udp_1pkt_vxlan.py]

[source,python]
----

# Adding header that does not exists yet in Scapy
# This was taken from pull request of Scapy 
# 


# RFC 7348 - Virtual eXtensible Local Area Network (VXLAN):                                     <1>
# A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks
# http://tools.ietf.org/html/rfc7348
_VXLAN_FLAGS = ['R' for i in range(0, 24)] + ['R', 'R', 'R', 'I', 'R', 'R', 'R', 'R', 'R'] 

class VXLAN(Packet):
    name = "VXLAN"
    fields_desc = [FlagsField("flags", 0x08000000, 32, _VXLAN_FLAGS),
                   ThreeBytesField("vni", 0),
                   XByteField("reserved", 0x00)]

    def mysummary(self):
        return self.sprintf("VXLAN (vni=%VXLAN.vni%)")

bind_layers(UDP, VXLAN, dport=4789)
bind_layers(VXLAN, Ether)


class STLS1(object):

    def __init__ (self):
        pass;

    def create_stream (self):
        pkt =  Ether()/IP()/UDP(sport=1337,dport=4789)/VXLAN(vni=42)/Ether()/IP()/('x'*20)    <2>
        #pkt.show2()
        #hexdump(pkt)

        # burst of 17 packets
        return STLStream(packet = STLPktBuilder(pkt = pkt ,vm = []),
                         mode = STLTXSingleBurst( pps = 1, total_pkts = 17) )


----
<1> Download and and add the scapy header or write it
<2> Use it 

For more information how to define headers see Scapy link:http://www.secdev.org/projects/scapy/doc/build_dissect.html[here]


==== Tutorial: Field Engine, Many clients 

The following example demonstrates a way to generate traffic from many clients with different IP/MAC to one server.
The following figure shows it. 

image::images/stl_tut_12.png[title="client->server",align="left",width={p_width}, link="images/stl_tut_12.png"]

1. Send gratuitous ARP from B->D with server IP/MAC (58.55.1.1)
2. DUT learn the ARP of Server IP/MAC (58.55.1.1)
3. Send traffic from A->C with many Clients IP's/MAC's

Let's take an example:

Base source IPv4 : 55.55.1.1
Destination IPv4:  58.55.1.1

Increment src ipt portion starting at 55.55.1.1 for 'n' number of clients (55.55.1.1, 55.55.1.2)
Src MAC: start with 0000.dddd.0001, increment mac in steps of 1
Dst MAC: Fixed  - 58.55.1.1 

To send gratuitous ARP from TRex server side for this server (58.0.0.1)

[source,python]
----
    def create_stream (self):
        # create a base packet and pad it to size
        base_pkt =  Ether(src="00:00:dd:dd:01:01",
                          dst="ff:ff:ff:ff:ff:ff")/
                    ARP(psrc="58.55.1.1",
                        hwsrc="00:00:dd:dd:01:01", 
                        hwdst="00:00:dd:dd:01:01", 
                        pdst="58.55.1.1")
----

Then traffic can be sent from client side  A->C 

*file*:: link:{github_stl_path}/udp_1pkt_range_clients_split.py[stl/udp_1pkt_range_clients_split.py]

[source,python]
----
class STLS1(object):

    def __init__ (self):
        self.num_clients  =30000; # max is 16bit
        self.fsize        =64

    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether(src="00:00:dd:dd:00:01")/
                          IP(src="55.55.1.1",dst="58.55.1.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'

        vm = CTRexScRaw( [ STLVmFlowVar(name="mac_src", 
                                        min_value=1, 
                                        max_value=self.num_clients, 
                                        size=2, op="inc"), # 1 byte varible, range 1-10
                                        
                           STLVmWrFlowVar(fv_name="mac_src", pkt_offset= 10),        <1>                 
                           STLVmWrFlowVar(fv_name="mac_src" ,
                                          pkt_offset="IP.src",
                                          offset_fixup=2),                           <2>
                           STLVmFixIpv4(offset = "IP")
                          ]
                         ,split_by_field = "mac_src"  # split 
                       )

        return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm),
                         mode = STLTXCont( pps=10 ))
----
<1> Write the stream variable mac_src with offset of 10 (last 2 bytes of src_mac field)
<2> Write the stream variable mac_src with `offset_fixup` of 2. beacuse we write it with offset


==== Tutorial: Field Engine, Split to core 

The following example demonstrates a way to split generated traffic to a number of threads.
Using this feature, there is a way to specify by which field to split the traffic to threads.
Without this feature the traffic is duplicated and all the threads transmits the same traffic.

*Without Split*::

Let's assume we have two transmitters DP threads

[source,python]
----
    def create_stream (self):

        # TCP SYN
        base_pkt  = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")     


        # vm
        vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src", 
                                              min_value="16.0.0.0", 
                                              max_value="16.0.0.254", 
                                              size=4, op="inc"),                     <1>


                           STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ),  <2>

                           STLVmFixIpv4(offset = "IP"), # fix checksum              
                          ]

                       )

----
<1> Stream variable 
<2> write it to IPv4.src


.Variable per thread 
[format="csv",cols="1^,3^,3^", options="header",width="40%"]
|=================
pkt, thread-0 ip_src,thread-1 ip_src
 1  , 16.0.0.1 , 16.0.0.1
 2  , 16.0.0.2 , 16.0.0.2
 3  , 16.0.0.3 , 16.0.0.3
 4  , 16.0.0.4 , 16.0.0.4
 5  , 16.0.0.5 , 16.0.0.5
 6  , 16.0.0.6,  16.0.0.6
|=================

* In this case all the threads transmit the same packets


*With Split feature enabled*::

Let's assume we have two transmitters DP threads

[source,python]
----
    def create_stream (self):

        # TCP SYN
        base_pkt  = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")     


        # vm
        vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src", 
                                              min_value="16.0.0.0", 
                                              max_value="16.0.0.254", 
                                              size=4, op="inc"),                     


                           STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ),  

                           STLVmFixIpv4(offset = "IP"), # fix checksum              
                          ]
                         ,split_by_field = "ip_src"                                 <1>  
                       )

----
<1> The same example but now we with split by `ip_src` stream variable

.Variable per thread 
[format="csv",cols="1^,3^,3^", options="header",width="40%"]
|=================
pkt, thread-0 ip_src ,thread-1 ip_src
 1  , 16.0.0.1 , 16.0.0.128
 2  , 16.0.0.2 , 16.0.0.129
 3  , 16.0.0.3 , 16.0.0.130
 4  , 16.0.0.4 , 16.0.0.131
 5  , 16.0.0.5 , 16.0.0.132
 6  , 16.0.0.6,  16.0.0.133
|=================

* In this case the stream variable is split 

To simulate it you can run the following command, let's take the file `stl/udp_1pkt_range_clients_split.py` and simulate it

[source,bash]
----
$./stl-sim -f stl/udp_1pkt_range_clients_split.py -o a.pcap -c 2 -l 10   #<1> 
----
<1> simulate 2 threads -c 2


.Variable per thread 
[format="csv",cols="1^,3^,3^", options="header",width="40%"]
|=================
pkt, thread-0 ip_src,thread-1 ip_src
 1  , 55.55.0.1 , 55.55.58.153
 2  , 55.55.0.2 , 55.55.58.154
 3  , 55.55.0.3 , 55.55.58.155
 4  , 55.55.0.4 , 55.55.58.156
 5  , 55.55.0.5 , 55.55.58.157
 6  , 55.55.0.6 , 55.55.58.158
|=================

*Some rules about Split stream varibles and burst/multi-burst*::

* In case of burst/multi-burst the number of packets are split to number of threads in *default* there is no need an explict split it.
* When the number of packets in a burst is smaller than the number of threads only one thread will do the work. 
* In case there is a stream with burst of *1* packet, only the first DP thread will do the work. 

==== Tutorial: Field Engine, Split to core with Burst 

The following example demonstrates a way to split generated traffic to a number of threads in the case that we are using Burst stream.
In both cases the number of packets would be split into threads. 
Using this feature, The Field engine will be split too.

*Without Split*::

In this example:

* Number of threads are two 
* Split is not configured 

[source,python]
----              
# no split
class STLS1(object):
    """ attack 48.0.0.1 at port 80
    """

    def __init__ (self):
        self.max_pkt_size_l3  =9*1024;

    def create_stream (self):

        base_pkt  = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")

        vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src",                              <1>
                                        min_value="16.0.0.0", 
                                        max_value="18.0.0.254", 
                                        size=4, op="inc"),

                           STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ), <2>

                           STLVmFixIpv4(offset = "IP"), # fix checksum
                          ]
                       )

        pkt = STLPktBuilder(pkt = base_pkt,
                            vm = vm)

        return STLStream(packet = pkt,
                         mode = STLTXSingleBurst(total_pkts = 20))                  <3>

----
<1> Stream variable 
<2> write it to IPv4.src
<3> burst of 20 packets

.Variable per thread 
[format="csv",cols="1^,3^,3^", options="header",width="40%"]
|=================
pkt, thread-0 ip_src,thread-1 ip_src
 1  , 16.0.0.1 , 16.0.0.1
 2  , 16.0.0.2 , 16.0.0.2
 3  , 16.0.0.3 , 16.0.0.3
 4  , 16.0.0.4 , 16.0.0.4
 5  , 16.0.0.5 , 16.0.0.5
 6  , 16.0.0.6,  16.0.0.6
 7  , 16.0.0.7,  16.0.0.7
 8  , 16.0.0.8,  16.0.0.8
 9  , 16.0.0.9,  16.0.0.9
 10 , 16.0.0.10, 16.0.0.10
|=================

*The results*::

* Total packets are 20 as expected, 10 generated by each thread 
* Field engine is the same for both threads 


*With Split feature enabled*::

[source,python]
----              
# no split
class STLS1(object):
    """ attack 48.0.0.1 at port 80
    """

    def __init__ (self):
        self.max_pkt_size_l3  =9*1024;

    def create_stream (self):

        base_pkt  = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")

        vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src",                              
                                        min_value="16.0.0.0", 
                                        max_value="18.0.0.254", 
                                        size=4, op="inc"),

                           STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ), 

                           STLVmFixIpv4(offset = "IP"), # fix checksum
                          ]
                         ,split_by_field = "ip_src"                                 <1>
                          
                       )

        pkt = STLPktBuilder(pkt = base_pkt,
                            vm = vm)

        return STLStream(packet = pkt,
                         mode = STLTXSingleBurst(total_pkts = 20))                  <2>

----
<1> Split is added by `ip_src` stream variable
<2> burst of 20 packets 


.Variable per thread 
[format="csv",cols="1^,3^,3^", options="header",width="40%"]
|=================
pkt, thread-0 ip_src,thread-1 ip_src
 1  , 16.0.0.1 , 17.0.0.128
 2  , 16.0.0.2 , 17.0.0.129
 3  , 16.0.0.3 , 17.0.0.130
 4  , 16.0.0.4 , 17.0.0.131
 5  , 16.0.0.5 , 17.0.0.132
 6  , 16.0.0.6,  17.0.0.133
 7  , 16.0.0.7,  17.0.0.134
 8  , 16.0.0.8,  17.0.0.135
 9  , 16.0.0.9,  17.0.0.136
 10 , 16.0.0.10, 17.0.0.137
|=================

*The results*::

* Total packets are 20 as expected, 10 generated by each thread 
* Field engine is *not* the same for both threads. 

==== Tutorial: Field Engine, Null stream 

The following example demonstrates a way create a Stream with no packets. The use cases is to use the Null stream inter stream gap (ISG) and then go to a new stream. 
using this you can create loops like this:

image::images/stl_null_stream.png[title="Null Stream",align="left",width={p_width}, link="images/stl_null_stream.png"]
 
1. S1 - send_burst of packets, go to stream NULL
2. NULL - wait ISG time - go to S1 

Null stream is with configured with 

1. mode: burst 
2. number of packets: 0


==== Tutorial: Field Engine, Barrier stream (Split) - [TODO]

image::images/stl_barrier.png[title="Barrier Stream",align="left",width={p_width}, link="images/stl_barrier.png"]

In some cases there is a need to split the streams to thread in a way that specific stream will continue only after all the threads pass the same path. 
In the above figure we would like to that stream S3 will start on all the thread after S2 was finished by all the threads 


==== Tutorial: Pcap file to one stream 

*Goal*:: Load stream template packet from pcap file instaed of scapy.

There is an assumption that this pcap has one packet. In case it has more only the first packet is loaded.

*file*:: link:{github_stl_path}/udp_1pkt_pcap.py[stl/udp_1pkt_pcap.py]

[source,python]
----

    def get_streams (self, direction = 0):
        return [STLStream(packet = 
                          STLPktBuilder(pkt ="stl/yaml/udp_64B_no_crc.pcap"), # path relative to pwd   <1>
                           mode = STLTXCont(pps=10)) ] 

----
<1> packet is taken from pcap file relative to pwd of the script you run


*file*:: link:{github_stl_path}/udp_1pkt_pcap_relative_path.py[udp_1pkt_pcap_relative_path.py]


[source,python]
----

    def get_streams (self, direction = 0):
        return [STLStream(packet = STLPktBuilder(pkt ="yaml/udp_64B_no_crc.pcap",
                                                 path_relative_to_profile = True), <1>
                         mode = STLTXCont(pps=10)) ] 

----
<1> packet is taken from pcap file relative to *profile* file location

==== Tutorial: Pcap file conversion to many streams

*Goal*:: Demonstrates a way to load pcap with *number* of packets and for each packet create a stream with burst of 1. the ISG for each stream is the inter packet gap (IPG) 

*file*:: link:{github_stl_path}/pcap.py[pcap.py]

[source,python]
----
    def get_streams (self,
                     ipg_usec = 10.0,                           <1>
                     loop_count = 1):                           <2>

        profile = STLProfile.load_pcap(self.pcap_file,          <3>
                                       ipg_usec = ipg_usec, 
                                       loop_count = loop_count)
----
<1> The inter stream gap in usec
<2> How many times to loop
<3> The input pcap file 

image::images/stl_tut_pcap_file1.png[title="pcap file",align="left",width={p_width}, link="images/stl_tut_pcap_file1.png"]

This figure illustrates how the streams look like for pcap file with 3 packets.
* Each stream is configured to burst with one packet 
* Each stream point to the next stream. 
* The last stream point to the first with action_loop=loop_count in case it was asked (>1)
The profile will run on one DP thread because it has burst with one packet (Split can work in this case)

Running this example

[source,bash]
----
./stl-sim -f stl/pcap.py --yaml
----

will give this

[source,python]
----
$./stl-sim -f stl/pcap.py --yaml
- name: 1
  next: 2                      <1> 
  stream:
    action_count: 0
    enabled: true
    flags: 0
    isg: 10.0
    mode:
      percentage: 100
      total_pkts: 1
      type: single_burst
    packet:
      meta: ''
    rx_stats:
      enabled: false
    self_start: true
    vm:
      instructions: []
      split_by_var: ''
- name: 2
  next: 3
  stream:
    action_count: 0
    enabled: true
    flags: 0
    isg: 10.0
    mode:
      percentage: 100
      total_pkts: 1
      type: single_burst
    packet:
      meta: ''
    rx_stats:
      enabled: false
    self_start: false
    vm:
      instructions: []
      split_by_var: ''
- name: 3
  next: 4
  stream:
    action_count: 0
    enabled: true
    flags: 0
    isg: 10.0
    mode:
      percentage: 100
      total_pkts: 1
      type: single_burst
    packet:
      meta: ''
    rx_stats:
      enabled: false
    self_start: false
    vm:
      instructions: []
      split_by_var: ''
- name: 4
  next: 5
  stream:
    action_count: 0
    enabled: true
    flags: 0
    isg: 10.0
    mode:
      percentage: 100
      total_pkts: 1
      type: single_burst
    packet:
      meta: ''
    rx_stats:
      enabled: false
    self_start: false
    vm:
      instructions: []
      split_by_var: ''
- name: 5
  next: 1                   <2>
  stream:
    action_count: 1         <3>
    enabled: true
    flags: 0
    isg: 10.0
    mode:
      percentage: 100
      total_pkts: 1
      type: single_burst
    packet:
      meta: ''
    rx_stats:
      enabled: false
    self_start: false       <4>    
    vm:
      instructions: []
      split_by_var: ''
----
<1> Each stream point to the next stream
<2> Last point to the first 
<3> The number of loop is given in `action_count: 1`
<4> Self_start is disabled for all the streams except the first one

==== Tutorial: Pcap file to many streams and Field Engine 

The following example demonstrates a way to load pcap file to many stream and attach to each stream a Field Engine program.
For example change the IP.src of all the streams to a random number
 
*file*:: link:{github_stl_path}/pcap_with_vm.py[stl/pcap_with_vm.py]

[source,python]
----

    def create_vm (self, ip_src_range, ip_dst_range):
        if not ip_src_range and not ip_dst_range:
            return None

        # until the feature of offsets will be fixed for PCAP use hard coded offsets

        vm = []

        if ip_src_range:
            vm += [STLVmFlowVar(name="src", 
                                min_value = ip_src_range['start'], 
                                max_value = ip_src_range['end'], 
                                size = 4, op = "inc"),
                   #STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src")
                   STLVmWrFlowVar(fv_name="src",pkt_offset = 26)
                  ]

        if ip_dst_range:
            vm += [STLVmFlowVar(name="dst", 
                                min_value = ip_dst_range['start'], 
                                max_value = ip_dst_range['end'], 
                                size = 4, op = "inc"),
                   
                   #STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst")
                   STLVmWrFlowVar(fv_name="dst",pkt_offset = 30)
                   ]

        vm += [#STLVmFixIpv4(offset = "IP")
              STLVmFixIpv4(offset = 14)
              ]

        return vm


    def get_streams (self,
                     ipg_usec = 10.0,
                     loop_count = 5,
                     ip_src_range = None,
                     ip_dst_range = {'start' : '10.0.0.1', 
                                        'end': '10.0.0.254'}):

        vm = self.create_vm(ip_src_range, ip_dst_range)                 <1> 
        profile = STLProfile.load_pcap(self.pcap_file, 
                                      ipg_usec = ipg_usec, 
                                      loop_count = loop_count, 
                                      vm = vm)                          <2> 

        return profile.get_streams()
----
<1> Create Field Engine program, 
<2> Apply to all the packets -> convert to streams 

.Output 
[format="csv",cols="1^,2^,1^", options="header",width="40%"]
|=================
pkt, IPv4 , flow 
 1  , 10.0.0.1, 1 
 2  , 10.0.0.1, 1 
 3  , 10.0.0.1, 1 
 4  , 10.0.0.1, 1 
 5  , 10.0.0.1, 1 
 6  , 10.0.0.1, 1
 7  , 10.0.0.2, 2
 8  , 10.0.0.2, 2 
 9  , 10.0.0.2, 2 
 10  , 10.0.0.2,2  
 11  , 10.0.0.2,2  
 12  , 10.0.0.2,2 
|=================


==== Tutorial: Teredo tunnel (IPv6 over IPv4)

The following example demonstrates creating IPv6 packet inside IPv4 packet and create a range of IPs

*file*:: link:{github_stl_path}/udp_1pkt_ipv6_in_ipv4.py[stl/udp_1pkt_ipv6_in_ipv4.py]

[source,python]
----
    def create_stream (self):
        # Teredo Ipv6 over Ipv4 
        pkt =  Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/
              UDP(dport=3797,sport=3544)/
              IPv6(dst="2001:0:4137:9350:8000:f12a:b9c8:2815",
                   src="2001:4860:0:2001::68")/
              UDP(dport=12,sport=1025)/ICMPv6Unknown()

        vm = CTRexScRaw( [ 
                            # tuple gen for inner Ipv6 
                            STLVmTupleGen ( ip_min="16.0.0.1", ip_max="16.0.0.2", 
                                            port_min=1025, port_max=65535,
                                            name="tuple"),                      <1>

                             STLVmWrFlowVar (fv_name="tuple.ip", 
                                             pkt_offset= "IPv6.src",
                                             offset_fixup=12 ),                 <2>
                             STLVmWrFlowVar (fv_name="tuple.port", 
                                             pkt_offset= "UDP:1.sport" )        <3>
                          ]
                       )
----
<1> Define stream struct name tuple. it has tuple.ip, tuple.port variables
<2> Write stream tuple.ip variable into IPv6.src offset and fixup with 12 bytes (only 4 LSB)
<3> Write stream tuple.port variable into the second UDP header 


==== Tutorial: Mask instruction 

The STLVmWrMaskFlowVar is a handy instruction. The pseudocode is as follows:

.Pseudocode 
[source,bash]
----
        uint32_t val=(cast_to_size)rd_from_varible("name"); # read flow-var
        val+=m_add_value;                                   # add value

        if (m_shift>0) {                                    # shift 
            val=val<<m_shift;
        }else{
            if (m_shift<0) {
                val=val>>(-m_shift);
            }
        }

        pkt_val=rd_from_pkt(pkt_offset)                     # RMW
        pkt_val = (pkt_val & ~m_mask) | (val & m_mask)
        wr_to_pkt(pkt_offset,pkt_val)
----


*Example 1*::

[source,python]
----
        vm = CTRexScRaw( [ STLVmFlowVar(name="mac_src", 
                                        min_value=1, 
                                        max_value=30, 
                                        size=2, op="dec",step=1), 
                           STLVmWrMaskFlowVar(fv_name="mac_src", 
                                              pkt_offset= 11,
                                              pkt_cast_size=1, 
                                              mask=0xff) # mask command ->write it as one byte
                          ]
                       )

----

This will cast stream variable with 2 byte to be 1 byte 

*Example 2*::

[source,python]
----

        vm = CTRexScRaw( [ STLVmFlowVar(name="mac_src", 
                                        min_value=1, 
                                        max_value=30, 
                                        size=2, op="dec",step=1), 
                           STLVmWrMaskFlowVar(fv_name="mac_src", 
                                              pkt_offset= 10,
                                              pkt_cast_size=2, 
                                              mask=0xff00,
                                              shift=8) # take the var shift it 8 (x256) write only to LSB
                          ]
                       )
----

The output will be shift by 8 

.Output 
[format="csv",cols="1^", options="header",width="20%"]
|=================
 value
 0x0100 
 0x0200 
 0x0300 
|=================

*Example 3*::

[source,python]
----
        vm = CTRexScRaw( [ STLVmFlowVar(name="mac_src", 
                                        min_value=1, 
                                        max_value=30, 
                                        size=2, 
                                        op="dec",step=1), 
                           STLVmWrMaskFlowVar(fv_name="mac_src", 
                                              pkt_offset= 10,
                                              pkt_cast_size=1, 
                                              mask=0x1,
                                              shift=-1)         <1>
                          ]
                       )

----
<1> take var mac_src>>1 and write the LSB every two packet there should be a change

.Output 
[format="csv",cols="1^", options="header",width="20%"]
|=================
value
 0x00 
 0x00 
 0x01 
 0x01 
 0x00 
 0x00 
 0x01 
 0x01 
|=================

==== Tutorial: Advance traffic profile - platform  [TODO]

*Direction*::

To make the traffic profile more usable, the traffic profile support per direction/interface. 

[source,python]
----
def create_streams (self, direction = 0,**args):
    if direction = 0:
        rate =100                                       <1>
    else:    
        rate =200
    return [STLHltStream(tcp_src_port_mode = 'decrement',
                         tcp_src_port_count = 10,
                         tcp_src_port = 1234,
                         tcp_dst_port_mode = 'increment',
                         tcp_dst_port_count = 10,
                         tcp_dst_port = 1234,
                         name = 'test_tcp_ranges',
                         direction = direction,
                         rate_pps = rate,
                         ),
           ]
----
<1> Different rate base on direction 

[source,bash]
----
$start -f ex1.py -a 
----
 
If you have 4 interfaces
 
interfaces 0/2 is direction 0 
interfaces 1/3 is direction 1
 
So rate will be changed accordingly. 

*Per Interface*::
 
In this case there is a different  profile base on interface ID 
 
[source,python]
----
 
def create_streams (self, direction = 0, **args):

    port_id = args.get('port_id')
    if port_id==None:
        port_id=0;

    if port_id == 0:
     return [STLHltStream(tcp_src_port_mode = 'decrement',
                         tcp_src_port_count = 10,
                         tcp_src_port = 1234,
                         tcp_dst_port_mode = 'increment',
                         tcp_dst_port_count = 10,
                         tcp_dst_port = 1234,
                         name = 'test_tcp_ranges',
                         direction = direction,
                         rate_pps = rate,
                         ),
           ]

   if port_id == 1:
        return STLHltStream(
                #enable_auto_detect_instrumentation = '1', # not supported yet
                ip_dst_addr = '192.168.1.3',
                ip_dst_count = '1',
                ip_dst_mode = 'increment',
                ip_dst_step = '0.0.0.1',
                ip_src_addr = '192.168.0.3',
                ip_src_count = '1',
                ip_src_mode = 'increment',
                ip_src_step = '0.0.0.1',
                l3_imix1_ratio = 7,
                l3_imix1_size = 70,
                l3_imix2_ratio = 4,
                l3_imix2_size = 570,
                l3_imix3_ratio = 1,
                l3_imix3_size = 1518,
                l3_protocol = 'ipv4',
                length_mode = 'imix',
                #mac_dst_mode = 'discovery', # not supported yet
                mac_src = '00.00.c0.a8.00.03',
                mac_src2 = '00.00.c0.a8.01.03',
                pkts_per_burst = '200000',
                rate_percent = '0.4',
                transmit_mode = 'continuous',
                vlan_id = '1',
                direction = direction,
                )
   
   if port_id = 3:
         ..
----
 
The Console will give the port/direction and will get the right stream in each interface
 

*Tunable*::
 
[source,python]
----
 
class STLS1(object):

    def __init__ (self):
        self.num_clients  =30000; # max is 16bit  <1>
        self.fsize        =64                     

    def create_stream (self):

        # create a base packet and pad it to size
        size = self.fsize - 4; # no FCS
        base_pkt =  Ether(src="00:00:dd:dd:00:01")/IP(src="55.55.1.1",dst="58.0.0.1")/UDP(dport=12,sport=1025)
        pad = max(0, size - len(base_pkt)) * 'x'
 
----
<1> Define object args 

 
[source,bash]
----
$start -f ex1.py -t "fsize=1500,num_clients=10000" #<1>
----
<1> Change the Tunable using -t option

Once a profile was defined, it is possible to give a tunable from Console and change the default value.
In this example, change the fsize to 1500 bytes 


==== Tutorial: Per stream statistics 

* Per stream statistics is implemented using hardware assist when possible (X710/XL710 Intel NICs flow director rules for example).
* With other NICs (Intel I350, 82599) it is implemented in software.
* Implementation works as follows:
** User chooses 32 bit packet group id (pg_id).
** IPv4 Identification field of the stream is changed to a value with in a reserved range (0xff00 to 0xffff). Notice that if a stream for which no statistics is needed has IPv4 Identification in the reserved range, it is changed (left bit becomes 0).

* In the software implementation, hardware rules are used to direct packets from relevant streams to rx thread, where they are counted. In the hardware implementation, HW rules are inserted to count packets from relevant streams.
* Summed up statistics (per stream, per port) are sent using ZMQ async channel to clients.

*Limitations*::

* Currently, the feature supports only two packet types:
** IPv4 over ethernet
** IPv4 with one vlan tag
* Number of concurrent streams you can get statistics for is 128.

[source,python]
----

class STLS1(object):

    def get_streams (self, direction = 0):
        return [STLStream(packet = 
                          STLPktBuilder(
                          pkt ="stl/yaml/udp_64B_no_crc.pcap"), 
                          mode = STLTXCont(pps=10),
                          rx_stats = STLRxStats(pg_id = 7))   <1> 
               ]

----
<1> Configure this stream to be counted on all RX ports as packet group id 7

* TUI should show Tx/Rx stats [TODO]
* Python API to get the info  [TODO]


==== Tutorial: Per stream latency/Jitter [TODO]



==== Tutorial: HLT traffic profile 

traffic_config API has set of arguments for specifying stream. In particular the packet template and which field and how to send it.
It is possible to define a  traffic profile using HTTAPI arguments .
Under the hood there is a compiler that converts it to native scapy/field engine instruction
The support is limited, see  xref:altapi-support[here].

*file*:: link:{github_stl_path}/hlt/hlt_udp_inc_dec_len_9k.py[stl/hlt/hlt_udp_inc_dec_len_9k.py]

[source,python]
----

class STLS1(object):
    '''
    Create 2 Eth/IP/UDP steams with different packet size:
    First stream will start from 64 bytes (default) and will increase until max_size (9,216)
    Seconds stream will decrease the packet size in reverse way
    '''

    def create_streams (self):
        max_size = 9*1024
        return [STLHltStream(length_mode = 'increment',
                             frame_size_max = max_size,
                             l3_protocol = 'ipv4',
                             ip_src_addr = '16.0.0.1',
                             ip_dst_addr = '48.0.0.1',
                             l4_protocol = 'udp',
                             udp_src_port = 1025,
                             udp_dst_port = 12,
                             rate_pps = 1,
                             ),
                STLHltStream(length_mode = 'decrement',
                             frame_size_max = max_size,
                             l3_protocol = 'ipv4',
                             ip_src_addr = '16.0.0.1',
                             ip_dst_addr = '48.0.0.1',
                             l4_protocol = 'udp',
                             udp_src_port = 1025,
                             udp_dst_port = 12,
                             rate_pps = 1,
                             )
               ]

    def get_streams (self, direction = 0):
        return self.create_streams()
----

This profile can be run with the simulator to generate pcap file

[source,bash]
----
$ ./stl-sim -f stl/hlt/hlt_udp_inc_dec_len_9k.py -o b.pcap -l 10 
----

It can be converted to native json or YAML

[source,bash]
----
$ ./stl-sim -f stl/hlt/hlt_udp_inc_dec_len_9k.py --josn
----

or converted to native Python profile you can use this command 

[source,bash]
----
$ ./stl-sim -f stl/hlt/hlt_udp_inc_dec_len_9k.py --native 
----

.Auto generated code
[source,python]
----
# !!! Auto-generated code !!!
from trex_stl_lib.api import *

class STLS1(object):
    def get_streams(self):
        streams = []
        
        packet = (Ether(src='00:00:01:00:00:01', dst='00:00:00:00:00:00', type=2048) / 
                  IP(proto=17, chksum=5882, len=9202, ihl=5L, id=0) / 
                  UDP(dport=12, sport=1025, len=9182, chksum=55174) / 
                  Raw(load='!' * 9174))
        vm = CTRexScRaw([CTRexVmDescFlowVar(name='pkt_len', size=2, op='inc', 
                          init_value=64, min_value=64, max_value=9216, step=1),
                         CTRexVmDescTrimPktSize(fv_name='pkt_len'),
                         CTRexVmDescWrFlowVar(fv_name='pkt_len', 
                         pkt_offset=16, add_val=-14, is_big=True),
                         CTRexVmDescWrFlowVar(fv_name='pkt_len', 
                         pkt_offset=38, add_val=-34, is_big=True),
                         CTRexVmDescFixIpv4(offset=14)], split_by_field = 'pkt_len')
        stream = STLStream(packet = CScapyTRexPktBuilder(pkt = packet, vm = vm),
                           mode = STLTXCont(pps = 1.0))
        streams.append(stream)
        
        packet = (Ether(src='00:00:01:00:00:01', dst='00:00:00:00:00:00', type=2048) / 
                  IP(proto=17, chksum=5882, len=9202, ihl=5L, id=0) / 
                  UDP(dport=12, sport=1025, len=9182, chksum=55174) / 
                  Raw(load='!' * 9174))
        vm = CTRexScRaw([CTRexVmDescFlowVar(name='pkt_len', size=2, op='dec', 
                         init_value=9216, min_value=64, 
                         max_value=9216, step=1),
                         CTRexVmDescTrimPktSize(fv_name='pkt_len'),
                         CTRexVmDescWrFlowVar(fv_name='pkt_len', pkt_offset=16, 
                         add_val=-14, is_big=True),
                         CTRexVmDescWrFlowVar(fv_name='pkt_len', 
                         pkt_offset=38, add_val=-34, is_big=True),
                         CTRexVmDescFixIpv4(offset=14)], split_by_field = 'pkt_len')
        stream = STLStream(packet = CScapyTRexPktBuilder(pkt = packet, vm = vm),
                           mode = STLTXCont(pps = 1.0))
        streams.append(stream)

        return streams

def register():
    return STLS1()
----    


to run it using using the TRex Console 

[source,bash]
----
TRex>start -f stl/hlt/hlt_udp_inc_dec_len_9k.py -m 10mbps -a     
----

more profiles and example can be found in `stl/hlt` folder 


=== Reference

==== Stream 

==== Packet 

==== Field Engine commands

==== Modes

=== Console commands 

==== Overview 

The console will use TRex Client API for controling TRex 
Some guidelines:

* Console should not save it own state, it should only cache server state. It assumed there is only one console that has R/W capability so once connected as R/W console (per user/interface)  it could read the server state and then cache all the operations. 
* There could be many read-only clients for the same user same interface. 
* Console should sync with server to get the state in connection stage and cache the server information locally 
* In case of crash/exit  of the Console it should sync again at startup 
* Commands will be like bash shell - no order args, many flags  
* Ability to show stats in real time.  Gives the option to open two Console one for statistics and one for commands ( many read-only clients)

==== Ports State 

[options="header",cols="^1,3a"]
|=================
| state   |    meaning
| IDLE    | no streams, does not work 
| STREAMS | with streams, does not work 
| WORK    | with streams, works 
| PAUSE   | with streams, pause 
|=================


[source,bash]
----

  IDLE -> (add streams) -> STREAMS (start) -> WORK (stop) -> STREAMS (start) 
                                           |   WORK (pause) -> PAUSE (resume )---
                                           |                                     | 
                                           |                                     |
                                           --------------------------------------                                    

-----

==== Common Arguments 

This section includes arguments that are common to many commands 
In the command they will be marked like this (arg name)

==== Port mask 

this gives the ability to choose batch of ports 

[source,bash]
----
$command   [-a] [-port 1 2 3]  [-port 0xff]  [-port clients/servers] 

  port mask : 
    [-a]           : all ports 
    [-port 1 2 3]  : port 1,2 3
    [-port 0xff]   : port by mask 0x1 for port 0 0x3 for port 0 and 1 
    [-port clients/servers] : -port clients will choose all the client side ports
----

==== Duration 

duration in second or in min or hours 

[source,bash]
----
$command[-d 100] [-d 10m] [-d 1h] 
  
  duration:
   -d 100 : in sec 
   -d 10m : in min 
   -d 1h  : in hours
----


==== Multiplier 

[source,bash]
----
$command [-m 100] [-m 10gb] [-m 10kpps] [-m 40%]
  
  multiplier :
  
  -m 100    : multiply stream file by this factor
  -m 10gb   : from graph calculate the maximum rate as this bandwidth for all streams( for each port )
  -m 10kpps : from graph calculate the maximum rate as this pps for all streams      ( for each port )
  -m 40%    : from graph calculate the maximum rate as this precent from total port  ( for each port )
----


==== Commands 

===== Connect 

[source,bash]
----

$trex-con   [--ip  $IP] [--server  $IP] [--rpc-port $PORT] [--async_port port]

   --rpc-port   : change the default server - default 5505 for RPC

   --async_port : for sub/pub ZMQ - default 4505 

   --ip or --server  :default 127.0.0.1 the TRex server ip
----

This command 
* try to connect to server 
* send ping  command 
* sync with  all the ports info / streams info state 
* read all counters stats for reference

===== reset 

Reset the server and client to a known state - should not be used in a normal scenario 

[source,bash]
----
$reset  
----

- force acuire all the ports
- Stop all traffic on all the ports
- Remove all the streams from all the ports


===== port 

Configure port state, autoneg, rate etc 

[source,bash]
----
$port (port mask) --cfg "auto/10/" 

 --cfg string with the configuration name

----


===== clear 

Clear all port stats counters 

[source,bash]
----
$clear (port mask) 
----


===== stats 

Shows global and port statistic 

[source,bash]
----
$stats  (port mask) [-g] [-p] [-ps]   

  -g show only global stats
  -p only ports stats
  -ps only port status (type/driver/link-up/down/negotion type etc)
  
----


===== streams

Shows the configured streams on each port/ports  
Should show from client cache 

[source,bash]
----
$streams (port mask)  [--streams mask] [-f]  [--full]  [--graph]

   --port mask,  e.g --port 1 2 3 4
   --streams mask e.g. --streams 1 2 
   -f /--full  print stream info in a JSON format with all the information 
   --graph : add the graph in time of each port stream  
----
    

example
  
[source,bash]
----
$streams 

port 0 : imix/a.yaml 

  stream id  , packet type        , length , mode       , rate    , next    
 + 0     , ip/tcp                 , 64    , continues  , 100KPPS ,   none
 + 1     , ip/udp                 , 128   , burst , 200KPPS  , none
 + 2     , ip/udp                 , 1500  , multi-burst , 100KPPS  , none
  
 

port 1 : imix/a.yaml

 + 0     , ip/tcp                 , 64    , continues  , 100KPPS ,   none
 + 1     , ip/udp                 , 128   , burst , 200KPPS  , none
 + 2     , ip/udp                 , 1500  , multi-burst , 100KPPS  , none

----


show only port 1 and 2 

[source,bash]
----
$streams --port 1 2 

 ..
 ..
----

[source,bash]
----
$streams --port 0 --streams 0 -f  


 show the full info on stream 0 and port 0, print in JSON format

----
        

===== start 

* work on a set of ports 
* remove all streams
* load new streams
* start traffic with specific multiplier 
* limit the traffic to a specific duration  
* port state should be stopped, in case of --force stop the port 
* in case one of the port is not stop don't start any port
* all ports should be in state IDLE or STREAMS

[source,bash]
----
$start [--force] (port mask) [-f stl/imix.yaml] [-db ab] (duration) (multiplier)  

    
  stream to load:
  -f stl/imix.yaml : load from local disk the streams file 
  --db stream that was loaded to db 
   
  force:
    --force stop ports if they are active 

----

examples


[source,bash]
----
$start -a -f stl/imix.yaml  -m 10gb
----
start this profile on all all ports maximum bandwidth is 10gb

   
[source,bash]
----
$start -port 1 2 -f stl/imix.yaml  -m 100
----
start this profile on port 1,2  multiply by 100 


[NOTE]
=====================================
 in case of start command without args, try to remember the last args given and reprint them 
=====================================

===== stop

* work on a set of ports 
* change the mode of the port to stopped
* do not remove the streams
* in case port state is already stopped don't do anything 
* all ports should be in state WORK


[source,bash]
----
$stop (port mask)

 See ports command explanation from the start 

----


===== pause 

* work on a set of ports 
* move a wokring set of ports to a state of pause
* all ports should be in state WORK



[source,bash]
----
$pause  (port mask)

 see ports command explanation from start 

----


===== resume 

* work on a set of ports 
* move a wokring set of port to a state of resume 
* all ports should be in state PAUSE



[source,bash]
----
$resume  (port mask) 

 see ports command explanation from start 

----


===== restart 

* restart the work on the loaded streams
* same as start without the -f /--db switch 
* all ports should be in state STREAMS

[source,bash]
----
$restart  (port mask) (duration) (multiplier)  

 see ports command explanation from start 

----

===== update 

* all ports should be in state WORK


[source,bash]
----
>update   (port mask) (multiplier)  
----
Update the bandwidth multiplier for a mask of ports


[NOTE]
=====================================
 Here we could add the ability to disable/enable specific stream, load new stream dynamically etc. 
=====================================


===== tui

shows the stats in a textual window (like top)
	
[source,bash]
----
$tui
----

enter to a mode of Stats and present 3 type of windows
* global/port stats/version/connected etc 
* per port
* per port streams info


get keyboard 
 q - quit the gui window
 c - clear all counters


=== Appendix

==== Scapy packet examples

[source,python]
----

# udp header 
Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)

# UDP over one valn
Ether()/Dot1Q(vlan=12)/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)

# UDP QinQ
Ether()/Dot1Q(vlan=12)/Dot1Q(vlan=12)/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)

#TCP over IP ove VALN
Ether()/Dot1Q(vlan=12)/IP(src="16.0.0.1",dst="48.0.0.1")/TCP(dport=12,sport=1025)

# IPv6 over valn 
Ether()/Dot1Q(vlan=12)/IPv6(src="::5")/TCP(dport=12,sport=1025)

#Ipv6 over UDP over IP 
Ether()/IP()/UDP()/IPv6(src="::5")/TCP(dport=12,sport=1025)

#DNS packet
Ether()/IP()/UDP()/DNS()

#HTTP packet 
Ether()/IP()/TCP()/"GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"
----


==== HLT supported Arguments anchor:altapi-support[]


[source,python]
----

traffic_config_kwargs = {
    'mode': None,                           # ( create | modify | remove | reset )
    'split_by_cores': 'split',              # ( split | duplicate | single ) TRex extention: split = split traffic by cores, duplicate = duplicate traffic for all cores, single = run only with sinle core (not implemented yet)
    'consistent_random': False,             # TRex extention: False (default): random sequence will be different every run, True: random sequence will be same every run
    'port_handle': None,
    'port_handle2': None,
    # stream builder parameters
    'transmit_mode': 'continuous',          # ( continuous | multi_burst | single_burst )
    'rate_pps': None,
    'rate_bps': None,
    'rate_percent': 10,
    'stream_id': None,
    'name': None,
    'bidirectional': 0,
    'direction': 0,                         # ( 0 | 1 ) TRex extention: 1 = exchange sources and destinations
    'pkts_per_burst': 1,
    'burst_loop_count': 1,
    'inter_burst_gap': 12,
    'length_mode': 'fixed',                 # ( auto | fixed | increment | decrement | random | imix )
    'l3_imix1_size': 60,
    'l3_imix1_ratio': 28,
    'l3_imix2_size': 590,
    'l3_imix2_ratio': 20,
    'l3_imix3_size': 1514,
    'l3_imix3_ratio': 4,
    'l3_imix4_size': 9226,
    'l3_imix4_ratio': 0,
    #L2
    'frame_size': 64,
    'frame_size_min': 64,
    'frame_size_max': 64,
    'frame_size_step': 1,
    'l2_encap': 'ethernet_ii',              # ( ethernet_ii | ethernet_ii_vlan )
    'mac_src': '00:00:01:00:00:01',
    'mac_dst': '00:00:00:00:00:00',
    'mac_src2': '00:00:01:00:00:01',
    'mac_dst2': '00:00:00:00:00:00',
    'mac_src_mode': 'fixed',                # ( fixed | increment | decrement | random )
    'mac_src_step': 1,
    'mac_src_count': 1,
    'mac_dst_mode': 'fixed',                # ( fixed | increment | decrement | random )
    'mac_dst_step': 1,
    'mac_dst_count': 1,
    'mac_src2_mode': 'fixed',                # ( fixed | increment | decrement | random )
    'mac_src2_step': 1,
    'mac_src2_count': 1,
    'mac_dst2_mode': 'fixed',                # ( fixed | increment | decrement | random )
    'mac_dst2_step': 1,
    'mac_dst2_count': 1,
    # vlan options below can have multiple values for nested Dot1Q headers
    'vlan_user_priority': 1,
    'vlan_priority_mode': 'fixed',          # ( fixed | increment | decrement | random )
    'vlan_priority_count': 1,
    'vlan_priority_step': 1,
    'vlan_id': 0,
    'vlan_id_mode': 'fixed',                # ( fixed | increment | decrement | random )
    'vlan_id_count': 1,
    'vlan_id_step': 1,
    'vlan_cfi': 1,
    'vlan_protocol_tag_id': None,
    #L3, general
    'l3_protocol': None,                  # ( ipv4 | ipv6 )
    'l3_length_min': 110,
    'l3_length_max': 238,
    'l3_length_step': 1,
    #L3, IPv4
    'ip_precedence': 0,
    'ip_tos_field': 0,
    'ip_mbz': 0,
    'ip_delay': 0,
    'ip_throughput': 0,
    'ip_reliability': 0,
    'ip_cost': 0,
    'ip_reserved': 0,
    'ip_dscp': 0,
    'ip_cu': 0,
    'l3_length': None,
    'ip_id': 0,
    'ip_fragment_offset': 0,
    'ip_ttl': 64,
    'ip_checksum': None,
    'ip_src_addr': '0.0.0.0',
    'ip_dst_addr': '192.0.0.1',
    'ip_src_mode': 'fixed',                 # ( fixed | increment | decrement | random )
    'ip_src_step': 1,                       # ip or number
    'ip_src_count': 1,
    'ip_dst_mode': 'fixed',                 # ( fixed | increment | decrement | random )
    'ip_dst_step': 1,                       # ip or number
    'ip_dst_count': 1,
    #L3, IPv6
    'ipv6_traffic_class': 0,
    'ipv6_flow_label': 0,
    'ipv6_length': None,
    'ipv6_next_header': None,
    'ipv6_hop_limit': 64,
    'ipv6_src_addr': 'fe80:0:0:0:0:0:0:12',
    'ipv6_dst_addr': 'fe80:0:0:0:0:0:0:22',
    'ipv6_src_mode': 'fixed',               # ( fixed | increment | decrement | random )
    'ipv6_src_step': 1,                     # we are changing only 32 lowest bits; can be ipv6 or number
    'ipv6_src_count': 1,
    'ipv6_dst_mode': 'fixed',               # ( fixed | increment | decrement | random )
    'ipv6_dst_step': 1,                     # we are changing only 32 lowest bits; can be ipv6 or number
    'ipv6_dst_count': 1,
    #L4, TCP
    'l4_protocol': None,                   # ( tcp | udp )
    'tcp_src_port': 1024,
    'tcp_dst_port': 80,
    'tcp_seq_num': 1,
    'tcp_ack_num': 1,
    'tcp_data_offset': 5,
    'tcp_fin_flag': 0,
    'tcp_syn_flag': 0,
    'tcp_rst_flag': 0,
    'tcp_psh_flag': 0,
    'tcp_ack_flag': 0,
    'tcp_urg_flag': 0,
    'tcp_window': 4069,
    'tcp_checksum': None,
    'tcp_urgent_ptr': 0,
    'tcp_src_port_mode': 'increment',       # ( increment | decrement | random )
    'tcp_src_port_step': 1,
    'tcp_src_port_count': 1,
    'tcp_dst_port_mode': 'increment',       # ( increment | decrement | random )
    'tcp_dst_port_step': 1,
    'tcp_dst_port_count': 1,
    # L4, UDP
    'udp_src_port': 1024,
    'udp_dst_port': 80,
    'udp_length': None,
    'udp_dst_port_mode': 'increment',       # ( increment | decrement | random )
    'udp_src_port_step': 1,
    'udp_src_port_count': 1,
    'udp_src_port_mode': 'increment',       # ( increment | decrement | random )
    'udp_dst_port_step': 1,
    'udp_dst_port_count': 1,
}

----









