PktGen
PktGen is a SysUnit API for generating and validating packets. PktGen is based around packet templates -- immutable structures that describe every field in a header. Templates are arbitrarily composable, subject to certain restrictions (e.g. an IPv4 header may only be encapsulated in a header that has an Ethertype field).
New templates may be defined by applying mutators to an existing template. This generates a template with all of the same header field values as the original template, but with the fields affected by the mutators changed to the new values specified by the mutator.
Packets (referenced via an mbuf chain) can be generated from a packet template via the Generate() method. As templates are immutable, it is safe to call this multiple times. Each call will generate a new mbuf with identical contents. The mbuf returned from Generate() is wrapped in a std::unique_ptr, typedefed as PktGen::MbufUniquePtr. The unique_ptr will automatically free the mbuf when the object goes out of scope. If this behaviour is not wanted, GenerateRawMbuf() will return a struct mbuf * that must be freed manually by calling m_freem().
A PacketMatcher can be used for validating that an mbuf's contents exactly matches the fields specified in a template.
Examples
1 using namespace PktGen;
2
3 // Create a packet template that describes a packet with a single IPv4 header
4 // with a source IP of 192.168.1.1 and a dest IP of 192.168.1.2. All other
5 // fields in the template have their default values.
6 auto pktTemplate = PacketTemplate(
7 Ipv4Header()
8 .With(
9 src("192.168.1.1"),
10 dst("192.168.1.2")
11 )
12 );
13
14
15 // Create a packet template that describes a packet with an Ethernet header,
16 // an IPv6 header, a TCP header and a payload. The Ethertype, IP Protocol,
17 // IP length and checksum (IP and TCP) fields will be automatically set
18 // appropriately. Other fields that are not explicitly specified get default
19 // values.
20 auto pktTemplate2 = PacketTemplate(
21 EthernetHeader()
22 .With(
23 src("02:01:02:03:04:05"),
24 dst("56:ed:59:33:73:67")
25 ),
26 Ipv6Header()
27 .With(
28 src("4898:5298::0001"),
29 dst("2659::2659:0864"),
30 ),
31 TcpHeader()
32 .With(
33 src(2985),
34 dst(80),
35 seq(155658),
36 ack(229289)
37 ),
38 PacketPayload()
39 .With(
40 payload("samplePayload")
41 )
42 );
43
44 // Define a packet template that is the next packet in the TCP stream after
45 // pktTemplate2 (i.e. th_seq has been incremented). This template will have
46 // the value "data" as its payload (4 bytes -- the null terminator is excluded)
47 // and the th_ack field has been incremented by 1448 bytes from pktTemplate2.
48 auto pktTemplate3 = pktTemplate2.Next()
49 .WithHeader(Layer::L4).Fields(incrAck(+1448))
50 .WithHeader(Layer::PAYLOAD).Fields(payload("data"));
Header Templates
Note: Any mutators that accept integer parameters accept the integer in host byte order (not network byte order).
EthernetHeader
auto PktGen::EthernetHeader();
Layer: PktGen::Layer::L2
Supported Mutators
Mutator |
Meaning |
Default |
Value Type |
PktGen::dst() |
Specify the destination MAC. |
00:00:00:00:00:00 |
Colon-separated MAC address. |
PktGen::ethertype() |
Specify the Ethertype value |
Based on encapsulated header type |
integer |
PktGen::mtu() |
Specify the maximum frame size |
ULONG_MAX |
integer |
PktGen::mbufVlan() |
Specify the value of the mbuf ether_vtag field |
0 |
integer |
PktGen::src() |
Specify the source MAC. |
00:00:00:00:00:00 |
Colon-separated MAC address. |
Ipv4Header
auto PktGen::Ipv4Header();
Layer: PktGen::Layer::L3
This header can only be encapsulated in headers that support the ethertype mutator.
Supported Mutators
Mutator |
Meaning |
Default |
Value Type |
PktGen::checksum() |
Specify the IP checksum (ip_sum) field value |
Checksum is calculated |
integer |
PktGen::checksumPassed() |
Specify whether the mbuf CSUM_L3_VALID flag should be set |
false |
bool |
PktGen::checksumVerified() |
Specify whether the mbuf CSUM_L3_CALC flag should be set |
false |
bool |
PktGen::dst() |
Specify the destination IP |
0.0.0.0 |
String (IP address in dotted decimal notation) |
PktGen::fragOffset() |
Specify the IP fragment offset field value |
0 |
integer |
PktGen::headerLength() |
Specify the ip_hl field value |
5 |
integer |
PktGen::id() |
Specify the ip_id field value |
0 |
integer |
PktGen::incrId(x) |
Increase the ip_id field value by x |
n/a |
integer |
PktGen::ipVersion() |
Specify the ip_v field value |
4 |
integer |
PktGen::mtu() |
Specify the MTU |
ULONG_MAX |
integer |
PktGen::proto() |
Specify the ip_p field value |
Based on encapsulated header type |
integer |
PktGen::src() |
Specify the source IP |
0.0.0.0 |
String (IP address in dotted decimal notation) |
PktGen::tos() |
Specify the type-of-service (ip_tos) field value |
0 |
integer |
PktGen::ttl() |
Specify the time-to-live (ip_ttl) field value |
255 |
integer |
Ipv6Header
auto PktGen::Ipv6Header();
Layer: PktGen::Layer::L3
This header can only be encapsulated in headers that support the ethertype mutator.
Supported Mutators
Mutator |
Meaning |
Default |
Value Type |
PktGen::dst() |
Specify the destination IP |
:: |
String (IPv6 address in RFC 4291 format) |
PktGen::ipVersion() |
Specify the ip_v field value |
6 |
integer |
PktGen::hopLimit() |
Specify the ip_hlim field value |
255 |
integer |
PktGen::mtu() |
Specify the MTU |
ULONG_MAX |
integer |
PktGen::proto() |
Specify the ip6_nxt field value |
Based on encapsulated header type |
integer |
PktGen::src() |
Specify the source IP |
:: |
String (IPv6 address in RFC 4291 format) |
PktGen::ttl() |
Alias for hopLimit() |
n/a |
integer |
PacketPayload
auto PktGen::PacketPayload();
Layer: PktGen::Layer::PAYLOAD
No header type (including another PacketPayload) can be encapsulated within a PacketPayload.
Supported Mutators
Mutator |
Meaning |
Default |
PktGen::payload() |
Specify an zero-length payload |
Yes |
PktGen::payload(const std::vector<uint8_t> &) |
Specify the contents of the payload directly |
n/a |
PktGen::payload(uint8_t byte, size_t count = 1) |
Specify a payload that consists of count repetitions of byte |
n/a |
PktGen::payload(const std::string & str, size_t count) |
Specify a payload of length count. The string value is repeated until count bytes are filled |
n/a |
PktGen::payload(const char *) |
Specify a payload containing the string. The null terminator is not included |
n/a |
PktGen::appendPayload(const std::vector<uint8_t> &) |
Append the series of bytes to the current payload |
n/a |
PktGen::appendPayload(uint8_t byte, size_t count = 1) |
Append count repetitions of byte to the payload |
n/a |
PktGen::appendPayload(const std::string & str, size_t count) |
Append a payload of length count. The string value is repeated until count bytes are filled |
n/a |
PktGen::appendPayload(const char *) |
Append the string to the payload. The null terminator is not included |
n/a |
TcpHeader
auto PktGen::TcpHeader();
Layer: PktGen::Layer::L4
This header type can only be encapsulated in headers that support the proto() mutator.
Supported Mutators
Mutator |
Meaning |
Default |
Value Type |
PktGen::ack() |
Specify the TCP ack (th_ack) field value |
0 |
integer |
PktGen::checksum() |
Specify the TCP checksum (th_sum) field value |
Checksum is calculated |
integer |
PktGen::checksumPassed() |
Specify whether the mbuf CSUM_L4_VALID flag should be set |
false |
bool |
PktGen::checksumVerified() |
Specify whether the mbuf CSUM_L4_CALC flag should be set |
false |
bool |
PktGen::dst() |
Specify the destination port |
0 |
integer |
PktGen::flags() |
Specify the TCP flags (th_flags) field value |
TH_ACK |
integer |
PktGen::incrAck(x) |
Increase the th_ack field by x |
n/a |
integer |
PktGen::incrSeq(x) |
Increase the th_seq field by x |
n/a |
integer |
PktGen::incrWindow(x) |
Increase the th_win field by x |
n/a |
integer |
PktGen::seq() |
Specify the TCP sequence number (th_seq) field value |
0 |
integer |
PktGen::src() |
Specify the source port |
0 |
integer |
PktGen::window() |
Specify the TCP window (th_win) field value |
0 |
integer |
PktGen::urp() |
Set the th_urp field value |
0 |
integer |
Packet Template APIs
PacketTemplate
template<typename... Headers> auto PktGen::PacketTemplate(Headers... auto);
Encapsulate one or more packet templates in each other. The first template passed to PacketTemplate() will be the outermost header, the second will be directly encapsulated in the outermost, so on, until the innermost header is specified as the last argument.
PacketMatcher
template <typename PacketTemplate> auto PktGen::PacketMatcher(const PacketTemplate & packetTemplate);
Return a Google Mock matcher that matches a struct mbuf * against the specified template. All fields specified in every header in the template must exactly match against the headers in the mbuf's payload or the match will fail. The mbuf's length must also match the length of the specified payload.
Packet Template Methods
Each packet template supports the following methods:
Generate()
PktGen::MbufUniquePtr Generate() const;
Allocate an mbuf, and fill its payload with the headers that are described by *this. This method returns a std::unique_ptr smart point that will automatically free the mbuf when the smart pointer goes out of scope.
Because packet templates are immutable, this method may be safely called any number of times. It will produce an identical packet (with a unique address) every time.
GenerateRawMbuf()
struct mbuf * GenerateRawMbuf() const;
This is a convenience method that returns an unmanaged mbuf pointer. It is equivalent to calling Generate().release(). The returned mbuf must be freed via a call to m_freem(). A test case will fail if any mbufs are leaked.
This method is more convenient to use in the case where the mbuf pointer will be immediately passed to a code unit that will consume it (and call m_freem() on it once it is done with the mbuf).
Next()
auto Next() const;
Return a new packet template that describes the packet that is next in the packet stream. In the case of a TCP template, the TCP sequence number will be incremented to reflect the size of the payload of the packet described by *this. In the case of a UDP template, the IP fragment offset will increase if *this described a fragmented packet.
If no MTU was configured on the template or the length of the headers plus the payload was less than the MTU, then the new template will describe a packet with a 0-length payload. Otherwise, the payload will reflect the next part of the packet stream.
Retransmit()
auto Retransmit() const;
Return a new packet template that describes a packet that is a TCP retransmission of the packet described by *this. For the most part, a retransmission will be identical to *this, but in the case of IPv4 the ip_id field will have incremented.
With()
template <typename... Mutators> auto With(Mutators... mutators) const;
Returns a new packet template. The new packet template is a copy of *this, but with the given mutators applied to modify their respective fields. The mutators are applied to the innermost header. A compile error will result if a mutator does not apply to the innermost header type.
WithHeader().Fields()
auto WithHeader(Layer layer).Fields(Mutators... mutators) const;
Returns a new packet template. The new template is a copy of *this, but with the given mutators applied to modify their respective fields in the header at the given layer. Supported values for layer are:
- Ordinary Usage
PktGen::Layer::L2 -- mutate the outermost layer 2 header
PktGen::Layer::L3 -- mutate the outermost layer 3 header
PktGen::Layer::L4 -- mutate the outermost layer 4 header
PktGen::Layer::PAYLOAD -- mutate the outermost payload header
- Tunneled (e.g. IP-in-IP) Templates
PktGen::Layer::INNER_L2 -- mutate the innermost layer 2 header
PktGen::Layer::INNER_L3 -- mutate the innermost layer 3 header
PktGen::Layer::INNER_L4 -- mutate the innermost layer 4 header
PktGen::Layer::OUTER_L2 -- alias for Layer::L2
PktGen::Layer::OUTER_L3 -- alias for Layer::L3
PktGen::Layer::OUTER_L4 -- alias for Layer::L4
- Arbitrary Nesting
PktGen::NestedLayer<n>::L2 -- mutate the nth layer 2 header, counting from the outermost header
PktGen::NestedLayer<-n>::L2 -- mutate the nth layer 2 header, counting from the innermost header
PktGen::NestedLayer<n>::L3 -- mutate the nth layer 3 header, counting from the outermost header
PktGen::NestedLayer<-n>::L3 -- mutate the nth layer 3 header, counting from the innermost header
PktGen::NestedLayer<n>::L4 -- mutate the nth layer 4 header, counting from the outermost header
PktGen::NestedLayer<-n>::L4 -- mutate the nth layer 4 header, counting from the innermost header