The PacketParser facility

Modules

 Collection parsers
 
 Integer parsers
 
 Helper macros for defining new packet parsers
 

Classes

class  senf::PacketParserBase
 Parser Base class. More...
 
struct  senf::init_bytes< Parser >
 Return number of bytes to allocate to new object of given type. More...
 
struct  senf::is_fixed< Parser >
 Test, whether a parser is a fixed-size parser. More...
 
class  senf::SafePacketParserWrapper< Parser >
 Iterator re-validating Parser wrapper. More...
 

Functions

template<class Parser >
PacketParserBase::size_type senf::bytes (Parser const &p)
 Return raw size parsed by the given parser object. More...
 
template<class Parser >
Parser senf::operator<< (Parser const &target, Parser const &source)
 Generic parser copying. More...
 
template<class Parser , class Value >
Parser senf::operator<< (Parser target, Value const &value)
 Generic parser value assignment. More...
 
template<class Parser , class Value >
Parser senf::operator<< (Parser target, boost::optional< Value > const &value)
 Generic parser value assignment. More...
 

Detailed Description

The PacketParser facility provides a framework to implement very lightweight classes which parse the raw content of a packet into meaningful values. PacketParsers are always passed around by value, they can be understood as pointers into the packet data with added type information providing parsing functions.

Packet parsers are only used within the packet framework. You should never allocate a new parser instance directly, you should the Packet library let that do for you (either by having the parser as a packet parser in a packet type or by having a member in the packet parser which allocates the parser as a sub-parser).

Parsers are built hierarchically. A high-level parser will return other parsers when accessing an element (Example: Asking an EthernetParser for the ethertype field by calling the parsers type() member will return an UInt16 parser). The lowest level building blocks then return the values. This hierarchical structure greatly simplifies building complex parsers.

Since parsers are very lightweight and are passed by value, packet fields are accessed using the corresponding accessor method:

SomePacket p (...)
SomePacket q (...)
// Assign new value to an integer parser
p->someField() = 10;
// Write out above value
std::cerr << p->someField() << "\n";
// Use the generic parser-assignment operator '<<' to copy field values
p->someVector()[1].someOtherField() << q->someField();
p->someVector() << q->someVector()
Here \c someField(), \c someOtherField() and \c someVector() are accessor methods named after
the field name. Each returns a parser object. Simple parsers can be used like their
corresponding basic type (e.g. a UInt16Parser field can be used like an unsigned integer), more
complex parsers provide type specific access members. Assigning a value to a parser will change
the underlying representation (the packet data).

Parsers can be grouped into several categories. These categories are not all defined rigorously
but are nevertheless helpful when working with the parsers:
\li <em>\ref parserimpl_value</em> provide the lowest level parsers (e.g. senf::UInt16Parser which
    returns an integer value).
\li <em>\ref parserimpl_collection</em> are parsers which model a collection of sub-elements like
    senf::ListParser or senf::VectorParser.
\li <em>\ref parserimpl_composite</em> collect several fields of arbitrary type into a new
    parser. Parsers defined using the \ref packetparsermacros fall under this category.
\li <em>\ref parserimpl_packet</em> are used to define a packet type.

\warning Parsers are like iterators: They are invalidated <em>whenever the size of the packet's
data is changed</em>. You should not store a parser anywhere. If you want to keep a parser
reference, use the senf::SafePacketParserWrapper wrapper. You still will need to take extra care to
ensure the parser is not invalidated.

Packet parser categories

Every parser is derived from senf::PacketParserBase. This class provides the necessary
housekeeping information and provides the parsers with access to the data. You may in principle
define arbitrary methods as parser members (e.g. methods to calculate a checksum, methods
processing fields in some way and so on). You should however be very wary to access data outside
the range assigned to the packet (the range starting at \c i() and with a size of senf::bytes()
bytes).

Each parser type has specific features

Value parsers

For a parser \a SomeParser to be a value parser, the following expressions must be valid:
// SomeParser must have a 'value_type', The 'value_type' must be default constructible, copy
// constructible and assignable
SomeParser::value_type v;
// An instance of 'SomeParser' must have a 'value' member which returns a value which may be
// assigned to a variable of type 'value_type'
v = p.someParserField().value()
// It must be possible to assign a new value using the 'value' member
p.someParserField().value(v)
If at all possible, the 'value_type' should not reference the packet data using iterators or
pointers, it should hold a copy of the value (it's Ok for \c value() to return such a reference
as long as assigning it to a \c value_type variable will copy the value).

\see parseint

Collection parsers

A collection parser \a SomeParser should model STL containers. The parsers themselves will
probably only // provide a reduced interface, but the collection parser should have a \c
collection member which is a wrapper providing the full interface.
SomeParser::container_type c (p.someParserField());
You will probably only very seldom need to implement a completely new collection
parser. Instead, you can rely on senf::VectorParser or senf::ListParser and implement new
policies.

\see parsecollection

Composite parsers

If possible, composite parsers should be implemented using the \ref packetparsermacros. In
addition to the normal parser requirements, these macros ensure, that for each field,
<em>fieldname</em><tt>_t</tt> is a typedef for the fields parser and
<em>fieldname</em><tt>_offset</tt> is the offset of the field in bytes from the beginning of the
parser (either a constant for fixed size parsers or a member function for dynamically sized
parsers). When defining composite parsers without the help of the \ref packetparsermacros, you
should provide those same members.

Protocol parsers

Protocol parsers are composite parsers with relaxed requirements. Since a Protocol parser will
never be used as a sub-parser (it will not be used within another composite parser or as value
type in a collection parser), the value returned by senf::bytes for this parser must not
necessarily cover the complete packet (e.g. if the packet has a trailer, the trailer will live
outside the range given by senf::bytes). You may define any member you want to have in your
packets field interface. These members may access the packet data in any way. You just need to
ensure, that the integration into the packet-type is correct (the senf::PacketTypeMixin will by
default use senf::bytes() to find the end of the header).

<hr>

Function Documentation

◆ bytes()

template<class Parser >
PacketParserBase::size_type senf::bytes ( Parser const &  p)

Return raw size parsed by the given parser object.

This function will either call p.bytes() or return Parser::fixed_bytes depending on the type of parser.

The value returned does not take into account the amount of data actually available. So you always need to validate this value against the packet size if you directly access the data. The standard low-level parses all do this check automatically to guard against malformed packets.

Parameters
[in]pParser object to check
Returns
number of bytes this parser expects to parser

◆ operator<<() [1/3]

template<class Parser >
Parser senf::operator<< ( Parser const &  target,
Parser const &  source 
)

Generic parser copying.

This operator allows to copy the values of identical parsers. This operation does not depend on the parsers detailed implementation, it will just replace the data bytes of the target parser with those from the source parser. This allows to easily copy around complex packet substructures.

This operation is different from the ordinary assignment operator: It does not change the target parser, it changes the data referenced by the target parser.

◆ operator<<() [2/3]

template<class Parser , class Value >
Parser senf::operator<< ( Parser  target,
Value const &  value 
)

Generic parser value assignment.

This operator allows to assign a value to parsers which implement a value(value) member. This operator allows to use a common syntax for assigning values or parsers to a parser.

◆ operator<<() [3/3]

template<class Parser , class Value >
Parser senf::operator<< ( Parser  target,
boost::optional< Value > const &  value 
)

Generic parser value assignment.

This operator allows to assign a value to parsers which implement a value(value) member. This special version allows to assign optional values: IF the optional value is not set, the assignment will be skipped.

This operator allows to use a common syntax for assigning values or parsers to a parser.