// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-/** \mainpage libPackets: How to define and use a new Packet Type
+/** \mainpage Defining and using a new 'libPacket' Packet Type
This howto will introduce the facilities needed to define a new packet type. As example, the
\c GREPacket type is defined.
+ \autotoc
+
+
\section howto_newpacket_start Getting started
Before starting with the implementation, we look at the specification of the GRE packet. This is
\li The GRE packet header is a dynamically sized header.
\li The GRE packet header utilizes the senf::EtherTypes registry for next-header selection
+
\section howto_newpacket_parser Implementing the GRE Parser
The next step in creating a new packet type is to implement the parser. The parser is
these members manually but the SENF library provides a large set of helper macros which simplify
this task considerably.
+
+ \subsection howto_newpacket_parser_skeleton The PacketParser skeleton
+
\code
- struct GREParser : public senf::PacketParser
+ #include <senf/Packets.hh>
+
+ struct GREPacketParser : public senf::PacketParser
{
# include SENF_PARSER()
// Define fields
- SENF_PARSER_FINALIZE(GREParser);
+ SENF_PARSER_FINALIZE(GREPacketParser);
};
\endcode
This is the standard skeleton of any parser class: We need to inherit senf::PacketParser and
start out by including either \ref SENF_PARSER() or \ref SENF_FIXED_PARSER(). Which, depends on
- whether we define a fixed size or a dynamically sized parser. As \c GREParser is dynamically
+ whether we define a fixed size or a dynamically sized parser. As \c GREPacketParser is dynamically
sized, we include \ref SENF_PARSER().
After the fields are defined, we need to call the \ref SENF_PARSER_FINALIZE() macro to close of
go back to define the parser fields and begin with the simple part: Those fields which are
always present.
+
+ \subsection howto_newpacket_parser_simple Simple field definitions
+
\code
SENF_PARSER_BITFIELD ( checksumPresent, 1, bool );
SENF_PARSER_SKIP_BITS ( 12 );
is defined as a dynamically sized parser, this offset is not a constant, it is an expression
which calculates the offset of a field depending on the preceding data).
+
+ \subsection howto_newpacket_parser_variant Defining optional fields: The 'variant' parser
+
We now come to the optional fields. Since there are two fields which need to be disabled/enabled
together, we first need to define an additional sub-parser which combines those two
fields. After this parser is defined, we can use \ref SENF_PARSER_VARIANT() to add this parser
as an optional parser to the GRE header.
\code
- struct GREParser_OptFields : public senf::PacketParser
+ struct GREPacketParser_OptFields : public senf::PacketParser
{
# include SENF_FIXED_PARSER()
SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
SENF_PARSER_SKIP ( 2 );
- SENF_PARSER_FINALIZE(GREParser_OptFields);
+ SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
};
\endcode
This parser only parses the two optional fields of which the reserved field is just skipped. The
- parser this time is a fixed size parser. We can now use this parser to continue the \c GREParser
+ parser this time is a fixed size parser. We can now use this parser to continue the \c GREPacketParser
implementation:
\code
SENF_PARSER_VARIANT ( optionalFields, checksumPresent,
(senf::VoidPacketParser)
- (GREParser_OptFields) );
+ (GREPacketParser_OptFields) );
\endcode
For a variant parser, two things need to be specified: A selector and a list of variant
parser which returns a value which is implicitly convertible to \c unsigned). This value is used
as index into the list of variant types. So in our case, 0 is associated with
senf::VoidPacketParser whereas 1 is associated with \c
- GREParser_OptFields. (senf::VoidPacketParser is a special empty parser which is used in a
+ GREPacketParser_OptFields. (senf::VoidPacketParser is a special empty parser which is used in a
Variant to denote cases in which the variant parser should not parse anything)
- This parser will work, it is however not very safe and not very usable. If \a p is a GREParser
+ This parser will work, it is however not very safe and not very usable. If \a p is a GREPacketParser
instance, than we access the fields via:
\code
p.checksumPresent() = true;
enabled, the variant parser needs to insert additional 4 bytes of data and remove those bytes,
when the checksumPresent field is disabled.
- To fix this, we make the checksumPresent field read-only:
+
+ \subsection howto_newpacket_parser_fixvariant Fixing access by providing custom accessor members
+
+ Since we don't want to allow the \a checksumPresent() field to be changed directly, we mark this
+ field as read-only:
\code
SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
\endcode
- To change the checksumPresent value, we now need to use the variant parsers \a init member:
+ To change the \a checksumPresent() value, the variant parser provides special members to change
+ the currently selected variant:
\code
p.optionalFields().init<0>();
p.optionalFields().init<1>();
\endcode
- The first statements switches to the first variant and therefore in this case disables the
- checksum field. The second statement will switch to the second variant and enable the checksum
- field.
+ These statements also change the selector field (in this case \a checksumPresent()) to the
+ correct value: The first statements switches to the first variant and therefore in this case
+ disables the checksum field. The second statement will switch to the second variant and enable
+ the checksum field.
+
+ Again, these statements are relatively complex. So we complete the parser by providing simple
+ additional members which wrap these complicated calls. While doing this, we also mark the
+ variant as a private field so it is not directly accessible any more (since we now have the
+ additional helpers which are used to access the variant, we don't want anyone to mess around
+ with it directly).
+
+ \code
+ SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
+ (senf::VoidPacketParser)
+ (GREPacketParser_OptFields) );
+
+ typedef GREPacketParser_OptFields::checksum_t checksum_t;
+ checksum_t checksum() const
+ { return optionalFields_().get<1>().checksum(); }
+
+ void enableChecksum() const { optionalFields_().init<1>(); }
+ void disableChecksum() const { optionalFields_().init<0>(); }
+ \endcode
+
+ Above code has one other twist we need to discuss: the \a checksum_t typedef. This is added as a
+ convenience to the user of this parser. The \c SENF_PARSER_* macros which define a field all
+ define some additional symbols providing further information about the field. Of these
+ additional symbols, the most important is <em>field</em><code>_t</code>, which is the (parser)
+ type returned by the field. This helps to work with a parser in more complex situations
+ (e.g. when using \ref parsecollection) since it allows to access the parser type without exact
+ knowledge of this type (which may become quite complex if templates are involved) as long as the
+ field name is known. Since we provide an accessor for the \a checksum field, we also provide the
+ \a checksum_t typedef for this accessor.
+
+ The \c GREPacketParser is now simple and safe to use. The only responsibility of the user now is to
+ only access \a checksum() if the \a checksumPresent() field is set. Otherwise, the behavior is
+ undefined (in debug builds, the parser will terminate the application with an assert).
+
+
+ \subsection howto_newpacket_parser_add Providing additional functionality
- This again is not very usable. So we complete the parser by providing simple additional members
- which wrap these complicated calls. While doing this, we also mark the variant as a private
- field so it is not directly accessible any more (since we now have the additional helpers which
- are used to access the variant, we don't want anyone to mess around with it directly). Here the
- final \c GREParser
+ The \c GREPacketParser is now complete. But we can do better: A packet parser is not restricted
+ to simply parsing data. Depending on the packet type, additional members can be arbitrarily
+ defined. In the case of \c GREPacket, we provide one additional member, \a calculateChecksum()
+ which does just that: It calculates the checksum of the GRE packet.
\code
- struct GREParser_OptFields : public senf::PacketParser
+ #include <senf/Utils/IpChecksum.hh>
+
+ checksum_t::value_type calculateChecksum() const
+ {
+ if (!checksumEnabled())
+ return 0;
+
+ senf::IpChecksum cs;
+ cs.feed( i(), i(4) );
+ // Skip even number of 0 bytes (the 2 bytes checksum field)
+ // cs.feed(0); cs.feed(0);
+ cs.feed( i(6), data().end() );
+
+ return cs.sum()
+ }
+ \endcode
+
+ This code just implements what is defined in the RFC: The checksum covers the complete GRE
+ packet including it's header with the checksum field temporarily set to 0. Instead of really
+ changing the checksum field we manually pass the correct data to \a cs.
+
+ We use the special <tt>i(</tt><i>offset</i><tt>)</tt> helper to get iterators \a offset number
+ of bytes into the data. This helper has the additional benefit of range-checking the returned
+ iterator and thereby safe os from errors due to truncated packets: If the offset is out of
+ range, a TruncatedPacketException will be thrown.
+
+ In this code we utilize some additional information provided by senf::PacketParserBase. The \a
+ i() member returns an iterator to the first byte the parser is interpreting whereas \a data()
+ returns a reference to the packet data container for the packet being parsed. Access to \a
+ data() should be restricted as much as possible. It is safe when defining new packet parsers
+ (like GREPacketParser). It's usage from sub parsers (like GREPacketParser_OptFields or even
+ senf::UInt16Parser) would be much more arcane and should be avoided.
+
+
+ \subsection howto_newpacket_parser_final The complete GREPacketParser implementation
+
+ So this is now the complete implementation of the \c GREPacketParser:
+
+ \code
+ #include <senf/Packets.hh>
+
+ struct GREPacketParser_OptFields : public senf::PacketParser
{
# include SENF_FIXED_PARSER()
- SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
- SENF_PARSER_SKIP ( 2 );
+ SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
+ SENF_PARSER_SKIP ( 2 );
- SENF_PARSER_FINALIZE(GREParser_OptFields);
+ SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
};
- struct GREParser : public senf::PacketParser
+ struct GREPacketParser : public senf::PacketParser
{
# include SENF_PARSER()
- SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
- SENF_PARSER_SKIP_BITS ( 12 );
- SENF_PARSER_BITFIELD ( version, 3, unsigned );
+ SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
+ SENF_PARSER_SKIP_BITS ( 12 );
+ SENF_PARSER_BITFIELD ( version, 3, unsigned );
- SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
+ SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
(senf::VoidPacketParser)
- (GREParser_OptFields) );
+ (GREPacketParser_OptFields) );
- typedef GREParser_OptFields::checksum_t checksum_t;
+ typedef GREPacketParser_OptFields::checksum_t checksum_t;
checksum_t checksum() const
{ return optionalFields_().get<1>().checksum(); }
void enableChecksum() const { optionalFields_().init<1>(); }
void disableChecksum() const { optionalFields_().init<0>(); }
- SENF_PARSER_FINALIZE(GREParser);
+ SENF_PARSER_FINALIZE(GREPacketParser);
+
+ checksum_t::value_type calculateChecksum() const;
};
+
+ // In the implementation (.cc) file:
+
+ #include <senf/Utils/IpChecksum.hh>
+
+ GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const
+ {
+ if (!checksumEnabled())
+ return 0;
+
+ validate(6);
+ senf::IpChecksum cs;
+ cs.feed( i(), i()+4 );
+ // Skip even number of 0 bytes (the 2 bytes checksum field)
+ // cs.feed(0); cs.feed(0);
+ cs.feed( i()+6, data().end() );
+
+ return cs.sum()
+ }
\endcode
- Above code has one other twist we need to discuss: the \a checksum_t typedef. This is added as a
- convenience to the user of this parser. The \c SENF_PARSER_* macros which define a field all
- define some additional symbols providing further information about the field. Of these
- additional symbols, the most important is <em>field</em><code>_t</code>, which is the (parser)
- type returned by the field. This helps to work with a parser in more complex situations
- (e.g. when using collection parsers) since it allows to access the parser type without exact
- knowledge of this type (which may become quite complex if templates are involved) as long as the
- field name is known. Since we provide an accessor for the \a checksum field, we also provide the
- \a checksum_t typedef for this accessor.
- The \c GREParser is now simple and safe to use. The only responsibility of the user now is to
- only access \a checksum() if the \a checksumPresent() field is set. Otherwise, the behavior is
- undefined (in debug builds, the parser will terminate the application with an assert).
+ \section howto_newpacket_type Defining the packet type
+
+ After we have implemented the \c GREPacketParser we now need to build the packet type. This is
+ done by providing a special policy class called the 'packet type'. This class encapsulates all
+ the information the packet library needs to know about a packet:
+
+
+ \subsection howto_newpacket_type_skeleton The packet type skeleton
+
+ For every type of packet, the 'packet type' class will look roughly the same. If the packet
+ utilizes a registry and is not hopelessly complex, the packet type will almost always look like
+ this:
+ \code
+ #include <senf/Packets.hh>
+
+ struct GREPacketType
+ : public senf::PacketTypeBase,
+ public senf::PacketTypeMixin<GREPacketType, EtherTypes>
+ {
+ typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
+ typedef senf::ConcretePacket<GREPacketType> packet;
+ typedef senf::GREPacketParser parser;
+
+ using mixin::nextPacketRange;
+ using mixin::nextPacketType;
+ using mixin::init;
+ using mixin::initSize;
+
+ // Define members here
+ };
+ \endcode
+ We note, that \c GREPacketType derives from two classes: senf::PacketTypeBase and
+ senf::PacketTypeMixin. senf::PacketTypeBase must be inherited by every packet type class. the
+ senf::PacketTypeMixin provides default implementations for some members which are useful for
+ most kinds of packets. If a packet type is very complex and these defaults don't work, the mixin
+ class can and should be left out.
+
+ Of the typedefs, only \a parser is mandatory. It defines the packet parser to use to interpret
+ this type of packet. \a mixin and \a packet are defined to simplify the following
+ definitions (More on \a packet and senf::ConcretePacket later).
+
+ The next block of statements imports all the default implementations provided by the mixin
+ class:
+
+ \li \a nextPacketRange provides information about where the next packet lives within the GRE
+ packet.
+ \li \a nextPacketType provides the type of the next packet from information in the GRE packet.
+ \li \a init is called to initialize a new GRE packet. This call is forwarded to \c
+ GREPacketParser::init.
+ \li \a initSize is called to find the size of an empty (newly create) GRE packet. This is also
+ provided by GREPacketParser.
+
+ With these default implementations provided by the mixin, only a few additional members are
+ needed to complete the \c GREPacketType: \a nextPacketKey, \a finalize, and \a dump
+
+
+ \subsection howto_newpacket_type_registry Utilizing the packet registry
+
+ We want the GRE packet to utilize the senf::EtherTypes registry to find the type of packet
+ contained in the GRE payload. A registry maps an arbitrary key value to a packet type
+ represented by a packet factory instance. The details have already been taken care of by the
+ senf::PacketTypeMixin (it provides the \a nextPacketType member). However, to lookup the packet
+ in the registry, the mixin needs to know the key value. To this end, we implement \a
+ nextPacketKey(), which is very simple:
+
+ \code
+ static key_t nextPacketKey(packet p) { return p->protocolType(); }
+ \endcode
+
+ All \c GREPacketType members are static. They are passed the packet in question as an
+ argument. \a nextPacketKey() just needs to return the value of the correct packet field. And
+ since the \c parser type (as defined as a typedef) allows direct access to the packet parser
+ using the <tt>-></tt> operator, we can simply access that value.
+
+ The \c key_t return type is a typedef provided by the mixin class it is taken from the type of
+ registry, in this case it is senf::EtherTypes::key_t (which is defined as a 16 bit unsigned
+ integer value).
+
+ With this information, the packet library can now find out the type of packet needed to parse
+ the GRE payload -- as long as the protocolType() is registered with the senf::EtherTypes
+ registry. If this is not the case, the packet library will not try to interpret the payload, it
+ will return a senf::DataPacket.
+
+ One special case of GRE encapsulation occurs when layer 2 frames and especially ethernet frames
+ are carried in the GRE payload. The ETHERTYPE registry normally only contains layer 3 protocols
+ (like IP or IPX) however for this special case, the value 0x6558 has been added to the ETHERTYPE
+ registry. So we need to add this value to inform the packet library to parse the payload as an
+ ethernet packet if the \a protocolType() is 0x6558. This happens in the implementation file (the
+ \c .cc file):
+
+ \code
+ #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
+
+ SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
+ \endcode
+
+ This macro registers the value 0x6558 in the senf::EtherTypes registry and associates it with
+ the packet type senf::EthernetPacket. This macro declares an anonymous static variable, it
+ therefore must always be placed in the implementation file and \e never in an include file.
+
+ Additionally, we want the GRE packet to be parsed when present as an IP payload. Therefore we
+ additionally need to register GRE in the senf::IpTypes registry. Looking at the <a
+ href="http://www.iana.org/assignments/protocol-numbers">IP protocol numbers</a>, we find that
+ GRE has been assigned the value 47:
+
+ \code
+ #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
+
+ SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
+ \endcode
+
+ But wait -- what is \c GREPacket ? This question is answered a few section further down.
+
+
+ \subsection howto_newpacket_type_invariants Providing packet invariants
+
+ Many packets have some invariants that must hold: The payload size must be equal to some field,
+ a checksum must match and so on. When packets are newly created or changed, these invariants
+ have to be updated to be correct. This is the responsibility of the \a finalize() member. This
+ is also the place, where the \a protocolType() field is assigned.
+
+ \code
+ static void finalize(packet p)
+ {
+ p->protocolType() << key(p.next(senf::nothrow));
+ if (p->checksumPresent())
+ p->checksum() << p->calculateChecksum();
+ }
+ \endcode
+
+ \a finalize() firs sets the \a protocolType() field depending on the \e next packet. The \c
+ key() function is provided by the mixin class: It will lookup the \e type of a packet in the
+ registry and return that packets key in the registry.
+
+ It then updates the \a checksum() field if present (this always needs to be done last since the
+ checksum depends on the other field values).
+
+ Here we are using the more generic parser assignment expressed using the \c << operator. This
+ operator in the most cases works like an ordinary assignment, however it can also be used to
+ assign parsers to each other efficiently and it supports 'optional values' (as provided by <a
+ href="http://www.boost.org/libs/optional/doc/optional.html">Boost.Optional</a> and as returned
+ by \c key()).
+
+
+ \subsection howto_newpacket_type_dump Writing out a complete packet: The 'dump()' member
+
+ For diagnostic purposes, every packet should provide a meaningful \a dump() member which writes
+ out the complete packet. This member is simple to implement and is often very helpful when
+ tracking down problems.
+
+ \code
+ #include <boost/io/ios_state.hpp>
+
+ static void dump(packet p, std::ostream & os)
+ {
+ boost::io::ios_all_saver ias(os);
+ os << "General Routing Encapsulation:\n"
+ << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
+ << " version : " << p->version() << "\n"
+ << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
+ << p->protocolType() << "\n";
+ if (p->checksumPresent())
+ os << " checksum : 0x" << std::hex << std::setw(4)
+ << std::setfill('0') << p->checksum() << "\n";
+ }
+ \endcode
+
+ This member is quite straight forward. We should try to adhere to the formating standard shown
+ above: The first line should be the type of packet/header being dumped followed by one line for
+ each protocol field. The colon's should be aligned at column 33 with the field name indented by
+ 2 spaces.
+
+ The \c boost::ios_all_saver is just used to ensure, that the stream formatting state is restored
+ correctly at the end of the method. An instance of this type will save the stream state when
+ constructed and will restore that state when destructed.
+
+ \subsection howto_newpacket_type_final Final touches
+
+ The \c GREPacket implementation is now almost complete. The only thing missing is the \c
+ GREPacket itself. \c GREPacket is just a typedef for a specific senf::ConcretePacket template
+ instantiation. Here the complete GREPacket definition:
+
+ \code
+ #include <senf/Packets.hh>
+
+ struct GREPacketType
+ : public senf::PacketTypeBase,
+ public senf::PacketTypeMixin<GREPacketType, EtherTypes>
+ {
+ typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
+ typedef senf::ConcretePacket<GREPacketType> packet;
+ typedef senf::GREPacketParser parser;
+
+ using mixin::nextPacketRange;
+ using mixin::nextPacketType;
+ using mixin::init;
+ using mixin::initSize;
+
+ static key_t nextPacketKey(packet p) { return p->protocolType(); }
+
+ static void finalize(packet p) {
+ p->protocolType() << key(p.next(senf::nothrow));
+ if (p->checksumPresent()) p->checksum() << p->calculateChecksum();
+ }
+
+ static void dump(packet p, std::ostream & os);
+ };
+
+ typedef GREPacketType::packet GREPacket;
+
+ // In the implementation (.cc) file:
+
+ #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
+ #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
+
+ SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
+ SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
+
+ void GREPacketType::dump(packet p, std::ostream & os)
+ {
+ boost::io::ios_all_saver ias(os);
+ os << "General Routing Encapsulation:\n"
+ << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
+ << " version : " << p->version() << "\n"
+ << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
+ << p->protocolType() << "\n";
+ if (p->checksumPresent())
+ os << " checksum : 0x" << std::hex << std::setw(4)
+ << std::setfill('0') << p->checksum() << "\n";
+ }
+ \endcode
+
+
+ \section howto_newpacket_advanced Going further
+
+ \subsection howto_newpacket_advanced_valid Checking the GRE packet for validity
+
+ We have implemented the \c GREPacket completely by now. There is however still room for
+ improvement. Reading the RFC, there are some restrictions which a packet needs to obey to be
+ considered valid. If the packet is not valid it cannot be parsed and should be dropped. There
+ are two conditions which lead to this: If one of the \a reserved0 fields first 5 bits is set or
+ if the version is not 0. We will add a \a valid() check to the parser and utilize this check in
+ the packet type.
+
+ So first lets update the parser. We will need to change the fields a little bit so we have
+ access to the first 5 bits of \a reserved0. We therefore replace the first three field
+ statements with
+
+ \code
+ SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
+ SENF_PARSER_PRIVATE_BITFIELD ( reserved0_5bits_, 5, unsigned );
+ SENF_PARSER_SKIP_BITS ( 7 );
+ SENF_PARSER_PRIVATE_BITFIELD ( version_, 3, unsigned );
+ \endcode
+
+ We have added an additional private bitfield \a reserved0_5bits_() and we made the \a version_()
+ field private since we do not want the user to change the value. We could have used a read-only
+ field in this special case (since the valid value is 0) but generally in a case like this since
+ the field will be initialized by the parser (see next section on how). the field should be
+ private with an additional public read-only accessor.
+
+ We will now add two additional simple members to the parser
+
+ \code
+ typedef version__t version_t;
+ version_t::value_type version() const { return version_(); }
+
+ bool valid() const { return version() == 0 && reserved0_5bits_() == 0; }
+ \endcode
+
+ I think, both are quite straight forward: \a version() will allow the user to read out the value
+ of the version field. However, since it does \e not return a parser but a numeric value, the
+ access is read-only. The \a valid() member will just check the restrictions as defined in the RFC.
+
+ Now to the packet type. We want to refrain from parsing the payload if the packet is
+ invalid. This is important: If the packet is not valid, we have no idea, whether the payload is
+ what we surmise it to be (if any of the \a reserved0_5bits_() are set, the packet is from an
+ older GRE RFC and the header is quite a bit longer so the payload will be incorrect).
+
+ So we need to change the logic which is used by the packet library to find the type of the next
+ packet. We have two ways to do this: We keep using the default \c nextPacketType()
+ implementation as provided by the senf::PacketTypeMixin and have our \a nextPacketKey()
+ implementation return a key value which is guaranteed never to be registered in the registry.
+
+ The more flexible possibility is implementing \c nextPacketType() ourselves. In this case, the
+ first method would suffice, but we will choose to go the second route to show how to write the
+ \c nextPacketType() member. We therefore remove the \c using declaration of \c nextPacketType()
+ and also remove the \a nextPacketKey() implementation. Instead we add the following to the
+ packet type
+
+ \code
+ // disabled: using Min::nextPacketType;
+
+ factory_t nextPacketType(packet p) { return p->valid() ? lookup(p->protocolType()) : no_factory(); }
+ \endcode
+
+ As we see, this is still quite simple. \c factory_t is provided by senf::PacketTypeBase. For our
+ purpose it is an opaque type which somehow enables the packet library to create a new packet of
+ a specified packet type. The \c factory_t has a special value, \c no_factory() which stands for
+ the absence of any concrete factory. In a boolean context this (and only this) \c factory_t
+ value tests \c false.
+
+ The \c lookup() member is provided by the senf::PacketTypeMixin. It looks up the key passed as
+ argument in the registry and returns the factory or \c no_factory(), if the key was not found in
+ the registry.
+
+ In this case this is all. We can however return the factory for some specific type easily as in
+ the following example:
+
+ \code
+ factory_t nextPacketType(packet p) {
+ if (p->valid()) {
+ if (p->protocolType() == 0x6558) return senf::EthernetPacket::factory();
+ else return lookup(p->protocolType());
+ }
+ else return no_factory();
+ }
+ \endcode
+
+ Of course, this example is academic since senf::EthernetPacket is correctly registered in the
+ senf::EtherTypes registry but you get the picture.
+
+
+ \subsection howto_newpacket_advanced_init Non-trivial packet initialization
+
+ When we create a new GRE packet using the packet library, the library will initialize the packet
+ with all 0 bytes. It just happens, that this is correct for our GRE packet. Lets just for the
+ sake of experiment assume, the GRE packet would have to set \a version() to 1 not 0. In this
+ case, the default initialization would not suffice. It is however very simple to explicitly
+ initialize the packet. The initialization happens within the parser. We just add
+
+ \code
+ SENF_PARSER_INIT() { version_() << 1u; }
+ \endcode
+
+ to \c GREPacketParser. Here we see, why we have defined \a version_() as a private and not a
+ read-only field.
+
+
+ \section howto_newpacket_final The ultimate GRE packet implementation completed
+
+ So here we now have \c GREPacket finally complete in all it's glory. First the header file \c
+ GREPacket.hh:
+
+ \code
+ #ifndef HH_GREPacket_
+ #define HH_GREPacket_
+
+ #include <senf/Packets.hh>
+
+ struct GREPacketParser_OptFields : public senf::PacketParser
+ {
+ # include SENF_FIXED_PARSER()
+
+ SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
+ SENF_PARSER_SKIP ( 2 );
+
+ SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
+ };
+
+ struct GREPacketParser : public senf::PacketParser
+ {
+ # include SENF_PARSER()
+
+ SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
+ SENF_PARSER_PRIVATE_BITFIELD ( reserved0_5bits_, 5, unsigned );
+ SENF_PARSER_SKIP_BITS ( 7 );
+ SENF_PARSER_PRIVATE_BITFIELD ( version_, 3, unsigned );
+
+ SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
+
+ SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
+ (senf::VoidPacketParser)
+ (GREPacketParser_OptFields) );
+
+ typedef version__t version_t;
+ version_t::value_type version() const { return version_(); }
+
+ bool valid() const { return version() == 0 && reserved0_5bits_() == 0; }
+
+ typedef GREPacketParser_OptFields::checksum_t checksum_t;
+ checksum_t checksum() const
+ { return optionalFields_().get<1>().checksum(); }
+
+ void enableChecksum() const { optionalFields_().init<1>(); }
+ void disableChecksum() const { optionalFields_().init<0>(); }
+
+ SENF_PARSER_FINALIZE(GREPacketParser);
+
+ checksum_t::value_type calculateChecksum() const;
+ };
+
+ struct GREPacketType
+ : public senf::PacketTypeBase,
+ public senf::PacketTypeMixin<GREPacketType, EtherTypes>
+ {
+ typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
+ typedef senf::ConcretePacket<GREPacketType> packet;
+ typedef senf::GREPacketParser parser;
+
+ using mixin::nextPacketRange;
+ using mixin::init;
+ using mixin::initSize;
+
+ factory_t nextPacketType(packet p)
+ { return p->valid() ? lookup(p->protocolType()) : no_factory(); }
+
+ static void finalize(packet p) {
+ p->protocolType() << key(p.next(senf::nothrow));
+ if (p->checksumPresent()) p->checksum() << p->calculateChecksum();
+ }
+
+ static void dump(packet p, std::ostream & os);
+ };
+
+ typedef GREPacketType::packet GREPacket;
+
+ #endif
+ \endcode
+
+ And the implementation file \c GREPacket.cc:
+
+ \code
+ #include "GREPacket.hh"
+ #include <senf/Utils/IpChecksum.hh>
+ #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
+ #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
+
+ SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
+ SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
+
+ GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const
+ {
+ if (!checksumEnabled())
+ return 0;
+
+ validate(6);
+ senf::IpChecksum cs;
+ cs.feed( i(), i()+4 );
+ // Skip even number of 0 bytes (the 2 bytes checksum field)
+ // cs.feed(0); cs.feed(0);
+ cs.feed( i()+6, data().end() );
+
+ return cs.sum()
+ }
+
+ void GREPacketType::dump(packet p, std::ostream & os)
+ {
+ boost::io::ios_all_saver ias(os);
+ os << "General Routing Encapsulation:\n"
+ << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
+ << " version : " << p->version() << "\n"
+ << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
+ << p->protocolType() << "\n";
+ if (p->checksumPresent())
+ os << " checksum : 0x" << std::hex << std::setw(4)
+ << std::setfill('0') << p->checksum() << "\n";
+ }
+ \endcode
+
+
+ \section howto_newpacket_using Using the newly created GRE packet type
+
+ The GRE packet is now fully integrated into the packet library framework. For example, we can
+ read GRE packets from a raw INet socket and forward decapsulated Ethernet frames to a packet
+ socket.
+
+ \code
+ #include <iostream>
+ #include <senf/Packets.hh>
+ #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
+ #include <senf/Socket/Protocols/INet/RawINetProtocol.hh>
+ #include <senf/Socket/Protocols/Raw/PacketSocketHandle.hh>
+ #include "GREPacket.hh"
+
+ int main(int, char const **)
+ {
+ senf::RawV6ClientSocketHandle isock (47u); // 47 = Read GRE packets
+ senf::PacketSocketHandle osock;
+
+ while (true) {
+ try {
+ GREPacket gre (GREPacket::create(senf::noinit));
+ isock.read(gre.data(),0u);
+ if (gre->checksumPresent() && gre->checksum() != gre->calculateChecksum())
+ throw InvalidPacketChainException();
+ osock.write(gre.next<EthernetPacket>().data())
+ }
+ catch (senf::TruncatedPacketException & ex) {
+ std::cerr << "Malformed packet dropped\n";
+ }
+ catch (senf::InvalidPacketChainException & ex) {
+ std::cerr << "Invalid GRE packet dropped\n";
+ }
+ }
+ }
+ \endcode
+
+ Or we can do the opposite: Read ethernet packets from a \c tap device and send them out GRE
+ encapsulated.
+
+ \code
+ #include <iostream>
+ #include <senf/Packets.hh>
+ #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
+ #include <senf/Socket/Protocols/INet/RawINetProtocol.hh>
+ #include <senf/Socket/Protocols/Raw/TunTapSocketHandle.hh>
+ #include "GREPacket.hh"
+
+ int main(int argc, char const ** argv)
+ {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " <tunnel endpoint>\n";
+ return 1;
+ }
+
+ senf::TapSocketHandle tap ("tap0");
+ senf::ConnectedRawV6ClientSocketHandle osock (47u, senf::INet6SocketAddress(argv[1]));
+
+ while (true) {
+ senf::EthernetPacket eth (senf::EthernetPacket::create(senf::noinit));
+ isock.read(eth.data(),0u);
+ GREPacket gre (senf::GREPacket::createBefore(eth));
+ gre.finalize();
+ osock.write(gre.data());
+ }
+ }
+ \endcode
+
+
+ \section howto_newpacket_further Further reading
+
+ Lets start with references to the important API's (Use the <i>List of all members</i> link to
+ get the complete API of one of the classes and templates):
+
+ <table class="senf fixedcolumn">
+
+ <tr><td>senf::ConcretePacket</td> <td>this is the API provided by the packet handles.</td></tr>
+
+ <tr><td>senf::PacketData</td> <td>this API provides raw data access accessible via the handles
+ 'data' member.</td></tr>
+
+ <tr><td>senf::PacketParserBase</td> <td>this is the generic parser API. This API is accessible
+ via the packets \c -> operator or via the sub-parsers returned by the field accessors.</td></tr>
+
+ </table>
+
+ When implementing new packet's, the following information will be helpful:
+
+ <table class="senf fixedcolumn">
+
+ <tr><td>senf::PacketTypeBase</td> <td>here you find a description of the members which need to
+ be implemented to provide a 'packet type'. Most of these members will normally be provided by
+ the mixin helper.</td></tr>
+
+ <tr><td>senf::PacketTypeMixin</td> <td>here you find all about the packet type mixin and how to
+ use it.</td></tr>
+
+ <tr><td>\ref packetparser</td> <td>This section describes the packet parser facility.</td></tr>
+
+ <tr><td>\link packetparsermacros Packet parser macros\endlink</td> <td>A complete list and
+ documentation of all the packet parser macros.</td></tr>
+
+ <tr><td>\ref parseint, \n \ref parsecollection</td> <td>There are several lists of available
+ reusable packet parsers. However, these lists are not complete as there are other protocol
+ specific reusable parsers (without claiming to be exhaustive: senf::INet4AddressParser,
+ senf::INet6AddressParser, senf::MACAddressParser)</td></tr>
+
+ </table>
+
*/
\f
// indent-tabs-mode: nil
// ispell-local-dictionary: "american"
// compile-command: "scons -u doc"
-// mode: flyspell
// mode: auto-fill
// End: