Skip to content

Latest commit

 

History

History
executable file
·
1517 lines (1196 loc) · 39.5 KB

trex_rpc_server_spec.asciidoc

File metadata and controls

executable file
·
1517 lines (1196 loc) · 39.5 KB

The TRex RPC Server

1. Change log

Version name meaning

1.00

Itay Marom (imarom)

  • first version

1.01

Dan Klein (danklei)

  • added usage examples using Python code as Higher-level usage

  • added logic and explanation behind VM commands

1.1

Dan Klein (danklei)

  • Fixed some consistency issues

  • added RPC interaction examples appendix

2. RPC Support On TRex

TRex implements a RPC protocol in order to config, view and in general execute remote calls on TRex

In this document we will provide information on how a client can implement the protocol used to communicate with TRex

In general, we will describe the following:

  • Transport Layer - The transport layer used to communicate with TRex server

  • RPC Reprensentation Protocol - The format in which remote procedures are carried

2.1. Transport Layer

TRex server transport layer is implemented using ZMQ.

The default configuration is TCP on port 5555, however this is configurable.


The communication model is based on the request-reply ZMQ model:


for more on ZMQ and implementation please refer to: ​
http://zeromq.org/intro:read-the-manual

2.2. RPC Reprensentation Protocol

The RPC reprensentation protocol is JSON RPC v2.0. Every request and response will be encoded in a JSON RPC v2.0 format.


For more info on JSON RPC v2.0 spec please refer to: ​


Later on in the document we will describe all the supported commands.

2.3. TRex RPC Mock Server

Before we get into the commands, it’s worth mentioning that TRex has a mock RPC server designed to allow playing around with the server in order to understand the response and perform adjustments to the request.

TRex also provides a Python based console that can connect to the server (mock or real) and send various commands to the server.

2.3.1. Building The Mock Server

Building the mock server is performed like this:

trex-core/linux>  ./b configure
trex-core/linux>  ./b --target=mock-rpc-server-64-debug

2.3.2. Running The Mock Server

Launching the mock server is performed like this:

trex-core/scripts> ./mock-rpc-server-64-debug

-= Starting RPC Server Mock =-

Listening on tcp://localhost:5050 [ZMQ]

Setting Server To Full Verbose

Server Started

2.3.3. Using The TRex Console To Interact

When the mock server is up, you can already send commands to the server. ​

Let’s demonstrate the operation with the Python based TRex console:


trex-core/scripts> ./trex-console

Connecting To RPC Server On tcp://localhost:5050
[SUCCESS]


-=TRex Console V1.0=-

Type 'help' or '?' for supported actions

TRex >

As we will see later on, a basic RPC command supported by the server is ping. ​
Let’s issue a ping command to the server and see what happens on both sides:



On the client side:

TRex > verbose on

verbose set to on

TRex > ping

-> Pinging RPC server
[verbose] Sending Request To Server:

{
    "id": "l0tog11a",
    "jsonrpc": "2.0",
    "method": "ping",
    "params": null
}

[verbose] Server Response:

{
    "id": "l0tog11a",
    "jsonrpc": "2.0",
    "result": "ACK"
}

[SUCCESS]

On the server side:

trex-core/scripts> ./mock-rpc-server-64-debug

-= Starting RPC Server Mock =-

Listening on tcp://localhost:5050 [ZMQ]

Setting Server To Full Verbose

Server Started


[verbose][req resp] Server Received:

{
   "id" : "maa5a3g1",
   "jsonrpc" : "2.0",
   "method" : "ping",
   "params" : null
}

[verbose][req resp] Server Replied:

{
   "id" : "maa5a3g1",
   "jsonrpc" : "2.0",
   "result" : "ACK"
}

3. RPC Server Component Position Illustration

The following diagram illustres the RPC server component’s place:

rpc server big picture
Figure 1. RPC Server Position

4. RPC Server Port State Machine

Any port on the server can be in numbered of states, each state provides other subset of the commands that are allowed to be executed.

We define the following possible states:

  • unowned - The specific port is either unowned or another user is owning the port

  • owned - The specific port has been acquired by the client

  • active - The specific port is in the middle of injecting traffic - currently active

Each port command will specify on which states it is possible to execute it.

For port related commands valid only on owned or active, a field called 'handler' MUST be passed along with the rest of the parameters.

This will identify the connection:

rpc states
Figure 2. Port States

5. RPC Commands

The following RPC commands are supported

5.1. Ping

  • Name - ping

  • Valid States - not relevant

  • Description - Pings the TRex server

  • Paramters - None

  • Result [string] - "ACK" On Sucess

Example:

'Request':

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "ping",
    "params": null
}

'Response':

{
   "jsonrpc" : "2.0",
   "id" : 1,
   "result" : "ACK"
}

5.2. Get Server Supported Commands

  • Name - get_supported_cmds

  • Valid States - not relevant

  • Description - Queries the server for all the supported commands

  • Paramters - None

  • Result [array] - A list of all the supported commands by the server

Example:

'Request':

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "get_supported_cmds",
    "params": null
}


'Response':

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": [
        "remove_all_streams",
        "remove_stream",
        "add_stream",
        "get_reg_cmds",
        "ping",
        "test_sub",
        "get_version",
        "test_add"
    ]
}

5.3. Get Version

  • Name - get_version

  • Valid States - not relevant

  • Description - Queries the server for version information

  • Paramters - None

  • Result [object] - See table below

Table 1. Object type return values for get_version
Field Type Description

version

string

TRex version

build_date

string

build date

build_time

string

build time

built_by

string

who built this version

'Request':

{
    "id": "wapkk8m6",
    "jsonrpc": "2.0",
    "method": "get_version",
    "params": null
}


'Response':

{
    "id": "wapkk8m6",
    "jsonrpc": "2.0",
    "result": {
        "build_date": "Sep 16 2015",
        "build_time": "12:33:01",
        "built_by": "imarom",
        "version": "v0.0"
    }
}

5.4. Get System Info

  • Name - get_system_info

  • Description - Queries the server for system properties

  • Paramters - None

  • Result [object] - See table below

Table 2. return value: get_system_info
Field Type Description

dp_core_count

int

DP core count

core_type

string

DP core type

hostname

string

machine host name

uptime

string

uptime of the server

port_count

int

number of ports on the machine

ports

array

arary of object 'port' - see below

Table 3. return value: get_system_info.port
Field Type Description

driver

string

driver type

speed

string

speed of the port (1g, 10g, 40g, 100g)

status

string

down, idle or transmitting

'Request':

{
    "id": "zweuldlh",
    "jsonrpc": "2.0",
    "method": "get_system_info",
    "params": null
}

'Response':

{
    "id": "zweuldlh",
    "jsonrpc": "2.0",
    "result": {
        "core_type": "Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz",
        "dp_core_count": 1,
        "hostname": "csi-kiwi-03.cisco.com",
        "port_count": 4,
        "ports": [
            {
                "driver": "E1000",
                "index": 0,
                "speed": "1g",
                "status": "down"
            },
            {
                "driver": "E1000",
                "index": 1,
                "speed": "1g",
                "status": "down"
            },
            {
                "driver": "E1000",
                "index": 2,
                "speed": "1g",
                "status": "down"
            },
            {
                "driver": "E1000",
                "index": 3,
                "speed": "1g",
                "status": "down"
            }
        ]
    }
}

5.5. Get Owner

  • Name - get_owner

  • Valid States - all

  • Description - Queries the server for a specific port current owner

  • Paramters -

    • port_id [int] - port id to query for owner

  • Result [string] - owner name if exists, otherwise none

'Request':

{
    "id": "hxjkuwj9",
    "jsonrpc": "2.0",
    "method": "get_owner",
    "params": {
	"port_id": 1
    }
}

'Response':

{
    "id": "hxjkuwj9",
    "jsonrpc": "2.0",
    "result": {
        "owner": "itay"
    }
}

5.6. Acquire

  • Name - Acquire

  • Valid States - all

  • Description - Takes ownership over the port

  • Paramters -

    • port_id [int] - port id to take ownership

    • user [string] - User name aquiring the system

    • force [boolean] - force action even if another user is holding the port

  • Result [string] - unique connection handler for future requests for that port

'Request':

{
    "id": "b1tr56yz",
    "jsonrpc": "2.0",
    "method": "Acquire",
    "params": {
	"user": "itay"
	"port_id": 1
        "force": false,
    }
}


'Response':

{
    "id": "b1tr56yz",
    "jsonrpc": "2.0",
    "result": "4cBWDxS2"
}

5.7. Release

  • Name - release

  • Valid States - owned

  • Description - Release owernship over the device

  • Paramters -

    • handler [string] - unique connection handler

    • port_id [int] - port id to release

  • Result [string] - "ACK" on success

'Request':

{
    "id": "m785dxwd",
    "jsonrpc": "2.0",
    "method": "release",
    "params": {
        "handler": "37JncCHr"
	"port_id": 1
    }
}


'Response':

{
    "id": "m785dxwd",
    "jsonrpc": "2.0",
    "result": "ACK"
}

5.8. Add Stream

  • Name - add_stream

  • Valid States - owned

  • Description - Adds a stream to a port

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port id associated with this stream

    • stream_id [int] - stream id associated with the stream object

    • stream - object of type stream

  • Result [string] - "ACK" in case of success

5.8.1. Object type stream

Add_stream gets a single parameter of type object.

The format of that object is as follows:

Table 4. Object type stream
Field Type Description

enabled

boolean

is this stream enabled

self_start

boolean

is this stream triggered by starting injection or triggered by another stream

isg

double

[usec] inter stream gap - delay time in usec until the stream is started

next_stream_id

int

next stream to start after this stream. -1 means stop after this stream

packet

object

object of type packet

mode

object

object of type mode

vm

array

array of objects of type vm

rx_stats

object

object of type rx_stats

Object type packet

packet contains binary and meta data

Table 5. Object type packet
Field Type Description

binary

byte array

binary dump of the packet to be used in the stream as array of bytes

meta

string

meta data object. opaque to the RPC server. will be passed on queries

Object type mode

mode object can be one of the following objects:

Table 6. Object type mode - continuous
Field Type Description

type

string

'continuous'

pps

double

rate in packets per second

Table 7. Object type mode - single_burst
Field Type Description

type

string

'single_burst'

pps

double

rate in packets per second

total pkts

int

total packets in the burst

Table 8. Object type mode - multi_burst
Field Type Description

type

string

'multi_burst'

pps

int

rate in packets per second

pkts_per_burst

int

packets in a single burst

ibg

double

[usec] inter burst gap. delay between bursts in usec

count

int

number of bursts. '0' means loop forever, '1' will fall back to single burst

Object type vm

Array of VM instruction objects to be used with this stream

Any element in the array can be one of the following object types:

Table 9. Object type vm - fix_checksum_ipv4
Field Type Description

type

string

'fix_checksum_ipv4'

pkt_offset

uint16

offset of the field to fix

Table 10. Object type vm - flow_var
Field Type Description

type

string

'flow_var''

name

string

flow var name - this should be a unique identifier

size

[1,2,4,8]

size of the flow var in bytes

op

[inc, dec, random]

operation type to perform on the field

init value

uint64_t as string

init value for the field

min value

uint64_t as string

minimum value for the field

max value

uint64_t as string

maximum value for the field

Table 11. Object type vm - write_flow_var
Field Type Description

type

string

'write_flow_var'

name

string

flow var name to write

pkt_offset

uint16

offset at the packet to perform the write

add_value

int

delta to add to the field prior to writing - can be negative

is_big_endian

boolean

should write as big endian or little

Tip
For more information and examples on VM objects please refer to: VM examples
Object type rx_stats

Describes rx stats for the stream


Important
In case rx_stats is enabled, meta data will be written in the end of the packet. please also consider the following constraints:

5.8.2. Constrains

  • performance - this will have performance impact as rx packets will be examined

  • override - up to 10 bytes at the end of the packet will be overidden by the meta data required

5.8.3. The bytes needed for activating rx_stats:

  • stream_id consumes 2 bytes

  • seq_enabled consumes 4 bytes

  • latency_enabled consumes 4 bytes

so if no seq or latency are enabled 2 bytes will be used.

if seq or latency alone are enabled, 6 bytes will be used.

if both are enabled then 10 bytes will be used.

Table 12. Object type rx_stats
Field Type Description

enabled

boolean

is rx_stats enabled for this stream

stream_id

int

stream_id for which to collect rx_stats.
This could be stream_id different from the stream object which contains the rx_stats object.

seq_enabled

boolean

should write 32 bit sequence

latency_enabled

boolean

should write 32 bit latency

'Request':

{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "add_stream",
    "params": {
        "handler": "37JncCHr",
        "port_id": 1,
	"stream_id": 502
        "stream": {
            "enabled": true,
            "isg": 4.3,
            "mode": {
                "pps": 3,
                "total_pkts": 5000,
                "type": "single_burst"
            },
            "next_stream_id": -1,
            "packet": {
                "binary": [
                    4,
                    1,
                    255
                ],
                "meta": ""
            },
            "rx_stats": {
                "enabled": false
            },
            "self_start": true,
        }
    }
}

'Response':

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": "ACK"
}

This request-reply sequence demonstrate a method in which rx_stats are diabled. In case rx_stats feature is enabled, rx_object must include all rx_stats object fields as described above.

5.9. Remove Stream

  • Name - remove_stream

  • Valid States - owned

  • Description - Removes a stream from a port

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port assosicated with the stream.

    • stream_id [int] - stream to remove

  • Result [string] - "ACK" in case of success

'Request':

{
    "id": 1
    "jsonrpc": "2.0",
    "method": "remove_stream",
    "params": {
        "handler": "37JncCHr",
        "port_id": 1,
        "stream_id": 502
    }
}


'Response':

{
    "id": 1
    "jsonrpc": "2.0",
    "result": "ACK"
}

5.10. Get Stream ID List

  • Name - get_stream_list

  • Valid States - owned, active

  • Description - fetch all the assoicated streams for a port

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port to query for registered streams

  • Result [array] - array of stream_id

'Request':

{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "get_stream_list",
    "params": {
        "handler": "37JncCHr",
        "port_id": 1
    }
}

'Response':

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": [
        502,
        18
    ]
}

5.11. Get Stream

  • Name - get_stream

  • Valid States - owned, active

  • Description - get a specific stream object

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port for the associated stream

    • stream_id [int] - the requested stream id

  • Result [object] - object stream

'Request':

{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "get_stream",
    "params": {
        "handler": "37JncCHr",
        "port_id": 1,
        "stream_id": 7
    }
}


'Response':

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
        "stream": {
            "enabled": true,
            "isg": 4.3,
            "mode": {
                "pps": 3,
                "type": "continuous"
            },
            "next_stream_id": -1,
            "packet": {
                "binary": [
                    4,
                    1,
                    255
                ],
                "meta": ""
            },
            "self_start": true
        }
    }
}

5.12. Remove All Streams

  • Name - remove_all_streams

  • Valid States - owned

  • Description - remove all streams from a port

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port for the associated stream

  • Result [string] - "ACK" on success

'Request':

{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "remove_all_streams",
    "params": {
        "handler": "37JncCHr",
        "port_id": 2
    }
}

'Response':

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": "ACK"
}

5.13. Start Traffic

  • Name - start_traffic

  • Valid States - owned

  • Description - Starts the traffic on a specific port. if traffic has already started an error will be returned

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port id on which to start traffic

  • Result [string] - "ACK" on success

'Request':

{
    "id": "b3llt8hs",
    "jsonrpc": "2.0",
    "method": "start_traffic",
    "params": {
        "handler": "37JncCHr",
        "port_id": 3
    }

'Response':

{
    "id": "b3llt8hs",
    "jsonrpc": "2.0",
    "result": "ACK"
}

5.14. Stop Traffic

  • Name - stop_traffic

  • Valid States - active

  • Description - Stops the traffic on a specific port. if the port has already started nothing will happen

  • Paramters

    • handler [string] - unique connection handler

    • port_id [int] - port id on which to stop traffic

  • Result [string] - "ACK" on success

'Request':

{
    "id": "h2fyhni7",
    "jsonrpc": "2.0",
    "method": "stop_traffic",
    "params": {
        "handler": "37JncCHr",
        "port_id": 3
    }
}

'Response':

{
    "id": "h2fyhni7",
    "jsonrpc": "2.0",
    "result": "ACK"
}

5.15. Get Global Stats

  • Name - get_global_stats

  • Valid States - owned, active

  • Description - Get machine global stats

  • Paramters - None

  • Result [object] - See Below

Table 13. Return value of get_global_stats
Field Type Description

state

string

server state: can be unowned, owned or active

cpu_util

double

DP CPU util. in %

tx_bps

double

total TX bits per second

rx_bps

double

total RX bits per second

tx_pps

double

total TX packets per second

rx_pps

double

total RX packets per second

total_tx_pkts

int

total TX packets

total_rx_pkts

int

total RX packets

total_tx_bytes

int

total TX bytes

total_rx_bytes

int

total RX bytes

tx_rx_error

int

total Tx/Rx errors

5.16. Get Port Stats

  • Name - get_port_stats

  • Valid States - owned, active

  • Description - Get port stats

  • Paramters

    • port_id [int] - The port id for query

  • Result [object] - See Below

Table 14. Return value of get_port_stats
Field Type Description

status

string

down, idle or transmitting

tx_bps

double

total TX bits per second

rx_bps

double

total RX bits per second

tx_pps

double

total TX packets per second

rx_pps

double

total RX packets per second

total_tx_pkts

int

total TX packets

total_rx_pkts

int

total RX packets

total_rx_bytes

int

total TX bytes

total_tx_bytes

int

total RX bytes

tx_rx_error

int

total Tx/Rx errors

5.17. Get Stream Stats

  • Name - get_steram_stats

  • Valid States - owned, active

  • Description - Get port stats

  • Paramters

    • port_id [int] - The port id for query

    • stream_id [int] - The stream id for query

  • Result [object] - See Below

Table 15. Return value of get_stream_stats
Field Type Description

tx_bps

double

total TX bits per second

tx_pps

double

total TX packets per second

total_tx_pkts

int

total TX packets

total_tx_bytes

int

total TX bytes

rx_bps

double

total RX bits per second (if rx_stats enabled)

rx_pps

double

total RX packets per second (if rx_stats enabled)

total_rx_pkts

int

total RX packets (if rx_stats enabled)

total_rx_bytes

int

total RX bytes (if rx_stats enabled)

latency

array

array of 2 ordered elements average, maximum (if rx_stats enabled)

6. Typical Transactions Examples

the following examples represents common scenarios. commands in […​] represents meta commands and not real RPC commands such as repeat, wait and etc.

6.1. Init/Boot

This sequence represents a client implementing the protocol taking ownership over the server and preparing to perform work

6.1.1. Commands Flow

  • ping - Ping the server to verify the server is up

  • get_owner - if owner is not me or none prompt to the user if he wants to force it

  • acquire - Ask or force for exclusive control over the server. save the handler given for future commands

  • get_version - Verify the server is compatible with the GUI

  • get_system_info - Get the installed ports and cores

  • get_stream_list - for every port, get the list and sync the GUI

  • get_stream - for every stream in a port list, get the stream info and sync the GUI

6.2. Simple Traffic With Adding / Editing Streams

describes a simple scenario where a user wants to add or edit one or more streams to one or more ports

6.2.1. Commands Flow

  • [init] - perform the init procedure from above

  • [GUI add/edit streams] - GUI provides the user a way to add or edit streams and sync them

  • remove_all_streams [optional] - remove all previous streams to start from scratch

  • add_stream - configure a specific port with a stream.

  • [repeat previous] - repeat the above for how many ports and streams desired

  • get_stream_list [optional] - sanity - verify the server is synced with the GUI

  • start_traffic - start traffic on the specific port / all the ports

  • get_global_stats [optional] - make sure the machine is transmiting traffic

  • [perfrom test] - perform the required test

  • stop_traffic - when done, stop the traffic on the specific port / all the ports

  • get_global_stats [optional] - make sure the machine has stopped

6.3. Logout

Describes the log off from the machine

6.3.1. Commands Flow

  • stop_traffic [optional] - if traffic has started - stop it

  • get_global_stats [optional] - make sure the machine has stopped

  • remove_all_streams [optional] - if you want to clear all the previous streams - use this

  • release - release the ownership over the device

7. Higher Level implementation examples

The following examples represents common scenarios implemented by a higher layer, which uses the API described above.

The examples are written in Python, however similar examples can be shown in any programming language.

7.1. CTRexPktBuilder class description

CTRexPktBuilder is a Python module designed to provide a progammer API for dynamic packet building. Since the packet is built to be used by TRex, a CTRexVM subclass has been created to describe how TRex should use the described packet in its transmission.

While the entire CTRexPktBuilder class (which is initialized by specifying the total length of the packet) responsible to both building the packet layer by layer, the CTRexVM class is responsible for controlling the ranging of the values as desribed in the VM objects section, and other attributes being used by TRex data-plane once the server receives its streams.

7.2. Creating an example packet

The following conde snippet describes how an ICMP Echo packet is built.

from packet_builder import CTRexPktBuilder
import dpkt

pkt_bld = CTRexPktBuilder()      # (1)
pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet())
# set Ethernet layer attributes
pkt_bld.set_eth_layer_addr("l2", "src", "00:15:17:a7:75:a3")
pkt_bld.set_eth_layer_addr("l2", "dst", "e0:5f:b9:69:e9:22")
pkt_bld.set_layer_attr("l2", "type", dpkt.ethernet.ETH_TYPE_IP)
# set IP layer attributes
pkt_bld.add_pkt_layer("l3_ip", dpkt.ip.IP())
pkt_bld.set_ip_layer_addr("l3_ip", "src", "21.0.0.2")
pkt_bld.set_ip_layer_addr("l3_ip", "dst", "22.0.0.12")
pkt_bld.set_layer_attr("l3_ip", "p", dpkt.ip.IP_PROTO_ICMP)
# set ICMP layer attributes
pkt_bld.add_pkt_layer("icmp", dpkt.icmp.ICMP())
pkt_bld.set_layer_attr("icmp", "type", dpkt.icmp.ICMP_ECHO)
# set Echo(ICMP) layer attributes
pkt_bld.add_pkt_layer("icmp_echo", dpkt.icmp.ICMP.Echo())
pkt_bld.set_layer_attr("icmp_echo", "id", 24528)
pkt_bld.set_layer_attr("icmp_echo", "seq", 11482)
pkt_bld.set_pkt_payload('hello world')
# finally, set IP header len with relation to payload data
pkt_bld.set_layer_attr("l3_ip", "len", len(pkt_bld.get_layer('l3_ip')))
  1. Initialize the packet builder instance.

This example created a packet without any ranging to it, so in this case TRex is expected to reply the same packet over and over without any changes to it.

When adding sending this packet as part of the Add Stream command, the packet content specified under packet would look for the created ICMP packet like this:

>>> print pkt_bld.dump_pkt()
 [224, 95, 185, 105, 233, 34, 0, 21, 23, 167, 117, 163, 8, 0, 69, 0, 0, 39,
  0, 0, 0, 0, 64, 1, 79, 201, 21, 0, 0, 2, 22, 0, 0, 12, 8, 0, 217, 134, 95,
  208, 44, 218, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Each of the array items representing a byte data-representation, hence ranging from 0 to 255.

7.3. Create a packet with single ranging instruction

The following example creates an HTTP GET packet, hence layering Ethernet/IP/TCP/HTTP.

from packet_builder import CTRexPktBuilder
import dpkt

pkt_bld = CTRexPktBuilder()
pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet())
# set Ethernet layer attributes
pkt_bld.set_eth_layer_addr("l2", "src", "00:15:17:a7:75:a3")
pkt_bld.set_eth_layer_addr("l2", "dst", "e0:5f:b9:69:e9:22")
pkt_bld.set_layer_attr("l2", "type", dpkt.ethernet.ETH_TYPE_IP)
# set IP layer attributes
pkt_bld.add_pkt_layer("l3_ip", dpkt.ip.IP())
pkt_bld.set_ip_layer_addr("l3_ip", "src", "21.0.0.2")
pkt_bld.set_ip_layer_addr("l3_ip", "dst", "22.0.0.12")
pkt_bld.set_layer_attr("l3_ip", "p", dpkt.ip.IP_PROTO_TCP)
# set TCP layer attributes
pkt_bld.add_pkt_layer("l4_tcp", dpkt.tcp.TCP())
pkt_bld.set_layer_attr("l4_tcp", "sport", 13311)
pkt_bld.set_layer_attr("l4_tcp", "dport", 80)
pkt_bld.set_layer_attr("l4_tcp", "flags", 0)
pkt_bld.set_layer_attr("l4_tcp", "win", 32768)
pkt_bld.set_layer_attr("l4_tcp", "seq", 0)
# set packet payload, for example HTTP GET request
pkt_bld.set_pkt_payload('GET /10k_60k HTTP/1.1\r\nHost: 22.0.0.3\r\nConnection: Keep-Alive\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\r\nAccept: */*\r\nAccept-Language: en-us\r\nAccept-Encoding: gzip, deflate, compress\r\n\r\n')

# finally, set IP header len with relation to payload data
pkt_bld.set_layer_attr("l3_ip", "len", len(pkt_bld.get_layer('l3_ip')))

Now, we extened the single packet created with three VM instructions, in order to range over the source IP of the packet.

pkt_bld.set_vm_ip_range(ip_layer_name="l3_ip", # (1)
                        ip_field="src",        # (2)
                        ip_init="10.0.0.1", ip_start="10.0.0.1", ip_end="10.0.0.255",
                        add_value=1,
                        operation="inc")
  1. l3_ip corresponds with the layer name given to the IP layer of the packet. This helps identifying and diffrenciate in packet containing more than one IP header.

  2. the name of the field on which we want to range.

Now, we added ranging for source IP starting from 10.0.0.1 to 10.0.0.255. This will generate the follwing VM instructions, which will be provided under vm field of the add_stream command:

>>> print pkt_bld.vm.dump(),
 [{'name': 'l3__src', 'ins_name': 'flow_var', 'max_value': '167772415', 'min_value': '167772161', 'init_value': '167772161', 'size': 4, 'op': 'inc'},
  {'is_big_endian': False, 'pkt_offset': 26, 'type': 'write_flow_var', 'name': 'l3__src', 'add_value': 1},
  {'pkt_offset': 14, 'type': 'fix_checksum_ipv4'}]

As we can see, three instructions has been generated for this ranging criteria:

  1. flow_var instruction - for defining the ranging parameters.

  2. write_flow_var instruction - for specifying where and how the modification should take place.

  3. fix_checksum_ipv4 instruction - for updated the checksum field

Warning
The order of the instruction does matter. In this example, if the fix_checksum_ipv4 instruction would have been places prior to the write_flow_var instruction, the generated packet would have satyed with the old checksum values.
Note
By default, with each change to the IP header, a fix_checksum_ipv4 instruction is added. This can be canceled by passing add_checksum_inst=False in functions which ranges over an IP field.

7.4. Create a packet with multiple ranging instructions

Now, we shall extend our ranging and add another field to range on, this time we’ll pick the TOS field of the IP header.

So, we’ll add the following code snippet ontop of the ranging method we already applied:

pkt_bld.set_vm_custom_range(layer_name="l3_ip",
                            hdr_field="tos",
                            init_val="10", start_val="10", end_val="200", add_val=2, val_size=1,
                            operation="inc")

So, in this case we chose to range the TOS field from 10 to 200 in steps of 2.

Finally, let’s see the expected JSON output of the VM instructions:

>>> print pkt_bld.vm.dump()
 [{ 'init_value': '167772161',  # (1)
    'ins_name': 'flow_var',
    'max_value': '167772415',
    'min_value': '167772161',
    'name': 'l3__src',
    'op': 'inc',
    'size': 4},
  { 'init_value': '10',         # (2)
    'ins_name': 'flow_var',
    'max_value': '200',
    'min_value': '10',
    'name': 'l3__tos',
    'op': 'inc',
    'size': 1},
  { 'add_value': 2,             # (3)
    'is_big_endian': False,
    'name': 'l3__tos',
    'pkt_offset': 15,
    'type': 'write_flow_var'},
  { 'add_value': 1,             # (4)
    'is_big_endian': False,
    'name': 'l3__src',
    'pkt_offset': 26,
    'type': 'write_flow_var'},
  { 'pkt_offset': 14, 'type': 'fix_checksum_ipv4'} # (5)
  ]
  1. flow_var instruction for source IP.

  2. flow_var instruction for TOS field

  3. write_flow_var instruction for TOS.

  4. write_flow_var instruction for source IP.

  5. fix_checksum_ipv4 instruction for both ranging options

Note
In this case only one checksum instruction has been generated, since both ranging options applies to the same IP header.

Appendix A: Interaction Examples

This appendix brings examples with data for the this RPC interaction.

add_stream method example

The following example represents an interaction between the RPC client and the server’s response.

Simple single packet client request

On the following example, there’s no VM instructions, rx_stats option is disabled and there’s only a single packet which isn’t connected to any other packet.

Client request

{
  "id" : "2bqgd2r4",
  "jsonrpc" : "2.0",
  "method" : "add_stream",
  "params" : {
     "handler" : "37JncCHr",
     "port_id" : 1,
     "stream" : {
        "enabled" : true,
        "isg" : 0,
        "mode" : {
           "pps" : 100,
           "type" : "continuous"
        },
        "next_stream_id" : -1,
        "packet" : {
           "binary" : [
              0,
              80,
              86,
              128,
              13,
              ...  # more packet data
              77,
              79,
              250,
              154,
              66
           ],
           "meta" : ""
        },
        "rx_stats" : {
           "enabled" : false
        },
        "self_start" : true,
        "vm" : []
     },
     "stream_id" : 0
  }
}

Server’s response

{
   "id" : "2bqgd2r4",
   "jsonrpc" : "2.0",
   "result" : "ACK"
}

Two linked packets with VM instructions client request

On the following example, a batch request is being issued to the server, containing two add_stream requests.

First request
The first client request is similar to the previous example.
However, in this case the rx_stats object is enbaled and set to monitor ancestor’s stream_id (which is 0 in this case).

Ontop, this stream points to the next stream as the one to follow, as described under next_stream_id of stream object.

Second request
In this stream the big difference is that it has VM instructions under the vm field of the stream object.

Ontop, this stream is the last stream of the sequence, so next_stream_id of stream object is set to -1.

Client request

[
   {
      "id" : "tq49f6uj",
      "jsonrpc" : "2.0",
      "method" : "add_stream",
      "params" : {
         "handler" : "2JjzhMai",
         "port_id" : 3,
         "stream" : {
            "enabled" : true,
            "isg" : 0,
            "mode" : {
               "pps" : 100,
               "type" : "continuous"
            },
            "next_stream_id" : 1,
            "packet" : {
               "binary" : [
                  0,
                  80,
                  86,
                  ...  # more packet data
                  250,
                  154,
                  66
               ],
               "meta" : ""
            },
            "rx_stats" : {
               "enabled" : true,
               "latency_enabled" : false,
               "seq_enabled" : false,
               "stream_id" : 0
            },
            "self_start" : true,
            "vm" : []
         },
         "stream_id" : 0
      }
   },
   {
      "id" : "2m7i5olx",
      "jsonrpc" : "2.0",
      "method" : "add_stream",
      "params" : {
         "handler" : "2JjzhMai",
         "port_id" : 3,
         "stream" : {
            "enabled" : true,
            "isg" : 0,
            "mode" : {
               "pps" : 200,
               "type" : "continuous"
            },
            "next_stream_id" : -1,
            "packet" : {
               "binary" : [
                  0,
                  80,
                  86,
                  128,
                  ...  # more packet data
                  216,
                  148,
                  25
               ],
               "meta" : ""
            },
            "rx_stats" : {
               "enabled" : false
            },
            "self_start" : false,
            "vm" : [
               {
                  "init_value" : "65537",
                  "max_value" : "65551",
                  "min_value" : "65537",
                  "name" : "l3__src",
                  "op" : "inc",
                  "size" : 4,
                  "type" : "flow_var"
               },
               {
                  "add_value" : 1,
                  "is_big_endian" : false,
                  "name" : "l3__src",
                  "pkt_offset" : 34,
                  "type" : "write_flow_var"
               }
            ]
         },
         "stream_id" : 1
      }
   }
]

Server’s response

[
   {
      "id" : "tq49f6uj",
      "jsonrpc" : "2.0",
      "result" : "ACK"
   },
   {
      "id" : "2m7i5olx",
      "jsonrpc" : "2.0",
      "result" : "ACK"
   }
]