p4-traffictool helps in packet generation and parsing (dissection) for custom packet formats specified in P4. Specifically, it generates "plugin code" for Wireshark (Tshark), Scapy, MoonGen, and PcapPlusPlus such that these target tools can now support custom packet formats.
Currently, p4-traffictool expects the input P4 source code to be written for the bmv2
simple_switch target. For P4 source code written for other targets, you need to extract your headers and parser definition and modify it to fit in the v1model template available at /usr/share/p4-traffictool/templates/template.p4
(after installation).
Ongoing: We are currently working on enabling p4-traffictool to directly accept P4 source code written for any target.
The target tools for which p4-traffictool generates plugin code do not support all possible P4 packet specifications e.g. Wireshark does not support parser transitions based on multiple header field values. p4-traffictool is therefore inherently limited by the kind of packet formats supported by the target tools.
p4-traffictool was presented as a poster at ACM SOSR 2019: Poster Extended Abstract (BibTeX entry)
p4-traffictool requires the open-source p4c compiler installed and accessible by running the p4c
command on bash shell. Other required dependencies are python3
and the python3-tabulate
library.
After installing the dependencies, clone this repository and run ./install.sh
.
p4-traffictool PPAs are available for Ubuntu 16.04 and 18.04:
sudo add-apt-repository ppa:pro-panda/p4-traffictool
sudo apt-get update
sudo apt install p4-traffictool
# With P4 program as the input.
p4-traffictool -p4 <path to p4 source> [OPTIONS] <target tool(s)>
# With bmv2 json as the input.
p4-traffictool -json <path to json HLIR description> [OPTIONS] <target tool(s)>
<target tool(s)>
--scapy --wireshark --moongen --pcpp --all
[OPTIONS]
--std {p4-14|p4-16} : The P4 standard to use. Default is p4-16.
-o <output dir> : Output directory path. Default is the same as the P4/json input file.
--debug : Shows debugging information.
# For help
p4-traffictool --help
The plugin code is generated in a target tool specific sub-directory within the output directory. For steps on using the plugin code, please refer to the tool-specific usage below: Wireshark (Tshark), Scapy, MoonGen, PcapPlusPlus.
If standard headers for Ethernet, IPv4, IPv6, TCP, and UDP are detected, the user will be prompted to choose between using the built-in header implementations provided by the tools (except Wireshark) or generating new implementations for them.
There is limited support for variable length fields with Scapy, PcapPlusPlus and Wireshark. p4-traffictool prompts the user to enter the maximum length of a variable length field when it detects one. This length should be a multiple of 8 to ensure that the header is byte aligned. A fixed length field would be produced for the current run of p4-traffictool for Scapy, PcapPlusPlus and Wireshark Lua dissector. In order to modify this length, the user needs to rerun p4-traffictool.
To open Wireshark with the generated plugin code imported into it:
wireshark -X lua_script:init.lua
Note: init.lua
is generated by p4-traffictool in the sub-directory "wireshark" within the output directory.
To extract field values from the packets captured in a pcap file using tshark with the custom plugin code enabled:
tshark -X lua_script:init.lua -r captured_packets.pcap -Tfields -e <field_name>
- p4-traffictool expects that the user will be using Ethernet as the base layer and any modifications to the network stack shall be present on top of it.
- As of now, p4-traffictool does not support using Wireshark's built-in implementation of standard headers.
Scapy is a powerful Python-based interactive packet manipulation program and library.
-
In your Python code that uses Scapy, import the generated Python file using
from <filename> import *
-
Then, you can generate packets using standard Scapy syntax. For example, if you want to generate a packet with custom P4-defined layers foo and bar with payload "foobar" , then:
a = Foo()/Bar()/"foobar"
will generate the required packet.
- Post build fields like length and checksums need to be defined by the user himself/herself in the post build method.
- Fields are automatically mapped to XBit, XByte, XShort, XInt, etc. User can modify them if desired.
MoonGen is a Lua-based high-speed packet generator/sniffer that runs on top of DPDK.
-
Copy the file
templates/bitfields_def.lua
to the directoryMoonGen/libmoon/lua/
. This is just a one-time requirement. This file contains struct definitions for 24, 40 and 48 bits fields. -
Copy the newly generated protocol files (Lua files) to MoonGen/libmoon/lua/proto/
-
If you used any default (built-in) headers then add the corresponding next layer headers to the
resolveNextHeader
function of the default header.- Let's say you have added the protocol foo on top of the standard UDP layer. Then you need to modify MoonGen/libmoon/lua/proto/udp.lua so that foo gets recognized while parsing the UDP packet. Search for the function
resolveNextHeader
in the file udp.lua. Over there you will find a mapmapNamePort
. Add the pairfoo = udp.PORT_FOO
to the map. Here,udp.PORT_FOO
is a constant defined in the same file (udp.lua).
- Let's say you have added the protocol foo on top of the standard UDP layer. Then you need to modify MoonGen/libmoon/lua/proto/udp.lua so that foo gets recognized while parsing the UDP packet. Search for the function
-
In the file MoonGen/libmoon/lua/packet.lua, register the packet header combinations using the
createStack
function.-
Say you have defined the layer foo over UDP. Then inside the file packet.lua you need to define a
getFooPacket
function to generate a packet of the foo protocol:pkt.getFooPacket = createStack("eth","ip4","udp","FOO")
-
-
Additional changes in MoonGen/libmoon/lua/packet.lua may also be required:
- if the header has a length field that depends on the next layer's length, then adapt the function
packetSetLength
- if the packet has a checksum, adapt
createStack
(the loop at end of functioncreateStack
) andpacketCalculateChecksums
- if the header has a length field that depends on the next layer's length, then adapt the function
-
Add your protocol to MoonGen/libmoon/lua/proto/proto.lua so that it gets loaded :
proto.<protocol name> = require "proto.<file containing protocol without the .lua extension>"
-
For example, the foo protocol could be added as following:
proto.foo = require "proto.foo"
-
-
Now you can run any of the examples (or otherwise scripts) in MoonGen by using the function
get<ProtoName>Packet()
instead of the usualgetUdpPacket()
.- For example, to get a packet of the protocol foo, use
getFooPacket()
which was defined in step #3 above.
- For example, to get a packet of the protocol foo, use
- Post build fields like length and checksums need to be calculated in the setter function by the user.
- Field lengths which are not amongst {8, 16, 24, 32, 40, 48, 64} shall be promoted to the next higher power of 2. If the user needs a field to be strictly of a particular size other than these then a proper struct needs to be defined and corresponding ntoh and hton functions need to be defined.
PcapPlusPlus is a multi-platform C++ network sniffing and packet parsing/crafting framework for popular packet processing engines such as libpcap, WinPcap, Npcap, DPDK and PF_RING.
-
Copy the files
uint24_t.h
,uint40_t.h
anduint48_t.h
from/usr/share/p4-traffictool/templates/
toPacket++/header
inside your PcapPlusPlus source tree. This is just a one-time requirement. These files contain definitions for 24, 40 and 48 bit data types. -
Copy the header (.h) files of custom P4-defined protocol(s) to
Packet++/header
directory and the C++ (.cpp) files toPacket++/source
directory inside your PcapPlusPlus source tree. -
If you have used any standard PcapPlusPlus headers (e.g. Ethernet, IPv4, etc.) and if any of the new custom headers are the "next" layers, then add the new custom headers to the
parseNextLayer
function of the standard header.- For example, if a new custom header foo appears after the Ethernet layer and is identified by etherType
0x123
, then add a new switch case inside the functionEthLayer::parseNextLayer()
inPacket++/src/EthLayer.cpp
.
- For example, if a new custom header foo appears after the Ethernet layer and is identified by etherType
-
Add the newly generated protocol(s) to the
enum ProtocolType
insidePacket++/header/ProtocolType.h
. Theenum ProtocolType
specifies a separate bit for each protocol. So if the already defined last protocol in theenum ProtocolType
has value 0x20000000, then to add a newly generated protocol foo, addP4_FOO = 0x40000000
to the enum. -
Now recompile PcapPlusPlus and also install it (if you are accessing it from a central location):
make clean
make all [-j4]
sudo make install
- For using the new P4-defined layers in your PcapPlusPlus application, simply include the header (.h) files of the required layer(s) in your C++ program and call the constructor, getters, setters, etc. in the usual way of using PcapPlusPlus.
- Post build fields like length and checksums need to be calculated in the setter function by the user.
- Field lengths which are not amongst {8, 16, 32, 64} shall be promoted to the next higher power of 2. If the user needs a field to be strictly of a particular size other than these, then a proper struct needs to be defined and corresponding ntoh and hton functions need to be provided.
p4pktgen is closely related to p4-traffictool. However, p4pktgen is focused on testing all possible packet header combinations, whereas p4-traffictool provides auto-generated plugin code for popular traffic generation and parsing tools.
P4 Wireshark Dissector also generates a Wireshark (Tshark) Lua dissector plugin for a given P4 program. However, a custom P4-defined layer can only be the last layer in the protocol stack. For example, if "foo" and "bar" are custom layers, then using P4 Wireshark Dissector, you would be able to parse a packet of format Ethernet/IP/UDP/foo
or Ethernet/IP/bar
, but not of the format Ethernet/IP/UDP/foo/bar
or Ethernet/bar/foo
.