Creating custom packet profiles and scapy

Crafting packets and test profiles is becoming much easier with tools such as scapy and Trex, the following tutorial will walk through a test case for creating ‘EtherIP’ or RFC 3378 type packets and a test stream

Using Scapy

Scapy is a well known packet creating, manipulation and testing tool written in python. Details can be found within the Github repo: https://github.com/secdev/scapy

You can use Scapy in interactive mode to create and define packets, however we have an easy method which will:

  • Create a new custom packet / packet header (EtherIP - tunneling ethernet frames in IP) Commonly used for Wifi tunnels
  • Generate a .pcap file from this packet
  • Add this to a profile and generate a defined packet-per-second rate
  • Create streams within the profile
  • Simulate a test stream within Trex using ‘stl-sim’ and confirm our packets and profile are ready to be used for testing

A recent requirement to test some firewall infrastructure under load when processing fragmented EtherIP traffic from Wifi tunnels spawned this test.

Trex stateless profiles and setup

This type of test will work in stateless mode only, as we are just sending UDP datagrams between each interface and through a DUT (device-under-test)

For more information on setting up Trex traffic generator for stateless mode, please refer to the documentation page Stateless support

For the purpose of packet creating, profiles, API interaction and simulation the standard build and default setup are fine.

The traffic generator comes with a ‘stl-sim’ function which operates as a python script and calls an executable and can be found in the default directory for the app:

user@trex:~/v2.14$ ls
automation       doc_process.py       find_python.sh        scapy_daemon_server  t-rex-64-debug-gdb        trex-console
avl              dpdk_nic_bind.py     ko                    stl                  _t-rex-64-debug-o         trex_daemon_server
bp-sim-64        dpdk_nic_bind.pyc    libzmq.so.3           stl-sim              t-rex-64-debug-o
bp-sim-64-debug  dpdk_setup_ports.py  master_daemon.py      _t-rex-64            _t-rex-64-o
cap2             dumy_libs            python-lib            t-rex-64             t-rex-64-o
cfg              exp                  run_functional_tests  _t-rex-64-debug      trex-cfg
daemon_server    external_libs        run_regression        t-rex-64-debug       trex_client_v2.14.tar.gz

Creating the packet

As we are creating a packet header which does not exist yet in Scapy we need to import the scapy modules along with the trex_stl_lib.api

from trex_stl_lib.api import *
from scapy.fields import BitField
from scapy.packet import Packet, bind_layers
from scapy.layers.inet import IP
from scapy.layers.l2 import Ether

Create a class and add the relevant EtherIP fields. The relevant modules from scapy used are ‘bind_layers’, ‘BitField’, ‘IP’ and ‘Ether’

We just bind the layers (headers) together and register the relevant protocol type and fragment offset.

class EtherIP(Packet):
    name = "EtherIP / RFC 3378"
    fields_desc = [ BitField("version", 3, 4),
                    BitField("reserved", 0, 12)]

bind_layers( IP,            EtherIP,       frag=0, proto=0x61)
bind_layers( EtherIP,       Ether)

Once the scapy definitions are done we create the trex ‘stl’ class to use and take the headers we just created above and add the relevant layer 4 header / port numbers, in this case UDP = 16666. Note the order of header creation for the packets, Ethernet > IP > EtherIP > IP > UDP

class STLS1(object):

    def __init__ (self):
        pass;

    def create_stream (self):
        pkt =  Ether()/IP()/EtherIP()/Ether()/IP()/UDP(sport=16666,dport=16666)
        #pkt.show2()
        #hexdump(pkt)

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


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

def register():
    return STLS1()

Testing and converting to a pcap file

The following runs the traffic profile through the TRex simulator, limiting the number of packets to 10, and storing the output in a pcap file. This must be run from the location which contains ‘stl-sim’ or refer to this location when running.

user@trex:~/v2.14$ sudo ./stl-sim -f stl/ether_ip.py -o ether_ip_pkt.pcap -l 10
 
executing command: '/home/user/v2.14/bp-sim-64-debug --pcap --sl --cores 1 --limit 10 -f /tmp/tmpEfHVXs -o ether_ip_pkt.pcap'

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

image type:               debug
I/O output:               ether_ip_pkt.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 :              816.00  bps
max BPS L2 :              656.00  bps
line util. :              0.00  %


Starting simulation...


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

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

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

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

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

written 10 packets to 'ether_ip_pkt.pcap'

Our pcap file will be written to the current directory:

user@trex:~/v2.14$ ls -l
total 207088
drwxr-xr-x  6 user user     4096 Jan 10 12:14 automation
drwxr-xr-x  2 user user     4096 Jan 10 12:14 avl
-rwxr-xr-x  1 user user 33413037 Jan 10 12:14 bp-sim-64
-rwxr-xr-x  1 user user 19916626 Jan 10 12:14 bp-sim-64-debug
drwxr-xr-x  2 user user     4096 Jan 10 12:14 cap2
drwxr-xr-x  2 user user     4096 Jan 10 12:14 cfg
-rwxr-xr-x  1 user user     5481 Jan 10 12:14 daemon_server
-rwxr-xr-x  1 user user     2207 Jan 10 12:14 doc_process.py
-rwxr-xr-x  1 user user    27996 Jan 10 12:14 dpdk_nic_bind.py
-rw-r--r--  1 root root    21540 Jan 30 16:16 dpdk_nic_bind.pyc
-rwxr-xr-x  1 user user    46648 Jan 10 12:14 dpdk_setup_ports.py
drwxr-xr-x  2 user user     4096 Jan 10 12:14 dumy_libs
*-rw-r--r--  1 root root      964 Feb  2 14:58 ether_ip_pkt.pcap*

The end result should be a standard .pcap file and look like the below output

Ether_IP packet

Looking at the detail of each packet, we can see the relevant and correct Ethernet headers and versions. Two ethernet headers, two IP headers, etc…

Packet detail RFC 3378

This profile and packet can now be used to create a test stream either via API or directly on the traffic generator. We will post this in a new entry and detail the steps…

comments powered by Disqus