X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=HowTos%2FNewPacket%2FMainpage.dox;h=e05198b2da278ee79c7bfc75f85d40dde7d09dd7;hb=9b31d6dfefc620a0a78030302b95088a6e311f15;hp=674453542fff05224e0c4bd67f923917061d286c;hpb=e9db10951ae84696e2c287c22c86add276213281;p=senf.git diff --git a/HowTos/NewPacket/Mainpage.dox b/HowTos/NewPacket/Mainpage.dox index 6744535..e05198b 100644 --- a/HowTos/NewPacket/Mainpage.dox +++ b/HowTos/NewPacket/Mainpage.dox @@ -28,10 +28,49 @@ \autotoc - \section howto_newpacket_start Getting started - - Before starting with the implementation, we look at the specification of the GRE packet. This is - found in RFC 2784 in Section 2.1: + \section howto_newpacket_overview Overview + + How is a new packet type defined? Or more basically: How are packets defined in the packet + library at all? + + In the packet library, there are two basic packet representations: There is the generic packet + senf::Packet. This representation does not know anything about the type of packet or any fields + or properties. It really only is a bunch of bytes. Possibly there is a preceding packet (header) + or a following one, but that is all, a senf::Packet knows. + + However, this is not all. There is a second representation: senf::ConcretePacket. This + representation derives from senf::Packet and adds information about the packet type: Which + fields it has, maybe some invariants or packet specific operations and so on. This howto is + concerned with this representation. + + A concrete packet type in senf provides a lot of detailed information about a specific type of + packet: + \li It provides access to the packets fields + \li It may provide additional packet specific functions (e.g. calculating or validating a + checksum) + \li It provides information on the nesting of packets + \li It implements packet invariants + + To define a new packet type, we need to implement to classes which together provide all this + information: + \li a \e parser (a class derived from senf::PacketParserBase). This class defines the data + fields of the packet header and may provide also additional packet specific functionality. + \li a \e packet \e type (a class derived from senf::PacketTypeBase). This class defines, how + packets are nested and how to initialize and maintain invariants. + + This howto will introduce how to define these classes using GRE as an example. + + \section howto_newpacket_gre GRE Introduction + + Before we start with the implementation, we begin with introducing the GRE packet. We will need + this information later in the implementation. Some decisions can also be taken now by just + looking at the packet specification: + \li What kind of parser is needed for this packet type (fixed size or variable sized). + \li Whether the packet has another packet as payload (a nested packet) and how the type of this + payload is found (whether a registry is used and if yes, which). + + The GRE packet is defined in RFC 2784. In + Section 2.1 we find the header layout:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -42,6 +81,8 @@ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++ This header is followed by the payload data. + Using this protocol definition, we can decide the first important question: Whether the packet header is fixed size or dynamically sized. As we see above, the header incorporates optional fields. Therefore it must be dynamically sized. The RFC further details, that if the \a Checksum @@ -61,15 +102,14 @@ \li The GRE packet header utilizes the senf::EtherTypes registry for next-header selection - \section howto_newpacket_parser Implementing the GRE Parser + \section howto_newpacket_parser Implementing the packet parser The next step in creating a new packet type is to implement the parser. The parser is - responsible for turning a bunch of bytes into an interpreted header with specific fields. The - parser will later be constructed with an iterator (pointer) to the first byte to be interpreted - as a GRE header and will provide member functions to access the header fields. You can implement - these members manually but the SENF library provides a large set of helper macros which simplify - this task considerably. - + responsible for turning a bunch of bytes into an interpreted header with specific fields. A + parser instance is constructed with an iterator (pointer) to the first byte to be interpreted + (the first byte of the packet data) and provides member functions to access the header + fields. You can implement 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 @@ -91,6 +131,8 @@ whether we define a fixed size or a dynamically sized parser. As \c GREPacketParser is dynamically sized, we include \ref SENF_PARSER(). + The following section will define all the fields. We will come back to this later. + After the fields are defined, we need to call the \ref SENF_PARSER_FINALIZE() macro to close of the parser definition. This call takes the name of the parser being defined as it's sole argument. @@ -102,6 +144,10 @@ \subsection howto_newpacket_parser_simple Simple field definitions + Packet parser fields are defined utilizing special \ref packetpasermacros. We take the fields + directly from the definition of the packet type we want to implement (here the GRE RFC). In the + case of GRE we might come up with: + \code SENF_PARSER_BITFIELD ( checksumPresent, 1, bool ); SENF_PARSER_SKIP_BITS ( 12 ); @@ -109,10 +155,6 @@ SENF_PARSER_BITFIELD ( protocolType, 16, unsigned ); \endcode - This is a direct transcript of the field definition above. There are quite a number of macros - which may be used to define fields. All these macros are documented in '\ref - packetparsermacros'. - This is a correct \c GREPacket header definition but we can optimize a little bit: Since the \a protocolType field is aligned on a byte boundary, instead of defining it as a bitfield, we can define it as a UInt16 field: @@ -121,7 +163,6 @@ SENF_PARSER_BITFIELD ( checksumPresent, 1, bool ); SENF_PARSER_SKIP_BITS ( 12 ); SENF_PARSER_BITFIELD ( version, 3, unsigned ); - SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser ); \endcode @@ -146,10 +187,14 @@ \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. + Until now, the parser is very simple, it could have been defined as a fixed size parser. Here we + will now explain, how to define optional fields. The same facilities are used to define + either/or fields or field collections. + + In the special case of GRE< there are two fields which need to be disabled/enabled together. We + first 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 GREPacketParser_OptFields : public senf::PacketParserBase @@ -164,8 +209,8 @@ \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 GREPacketParser - implementation: + parser this time is a fixed size parser. We can now use this parser to continue the \c + GREPacketParser implementation: \code SENF_PARSER_BITFIELD ( checksumPresent, 1, bool ); @@ -188,8 +233,8 @@ 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 GREPacketParser - instance, than we access the fields via: + This parser will work, it is however not very safe and not very usable. If \a p is a + GREPacketParser instance, than we would access the fields via: \code p.checksumPresent() = true; p.version() = 4u; @@ -197,7 +242,7 @@ p.optionalFields().get<1>().checksum() = 12345u; \endcode - There are two problems here: + This code has two problems: \li accessing the checksum field is quite unwieldy \li changing the checksumPresent() value will break the parser @@ -210,54 +255,49 @@ \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: + The problems found above will happen whenever we use variant parsers and will often occur with + other complex parsers too (most \ref parsercollection reference some field external to + themselves and don't like it at all, if that value is changed without them knowing about + it). There may often also be other reasons to restrict access to a field: The field may be set + automatically or may be calculated from other values (how to do this, we'll see later). + + In all these cases we will want to disallow the user of the packet to change the value while + still allowing him to read the value. To do this, we can mark \e value fields as read-only: \code SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool ); \endcode - To change the \a checksumPresent() value, the variant parser provides special members to change - the currently selected variant: + Value fields are fields, which return a simple value and not a complex sub-parser (this are + bit-fields, the integer parsers and also some additional parsers like those parsing network + addresses). + + In this case however, we still want to allow the user to change the field value, albeit not + directly. We will need to go through the collection parser, in this case the variant. Since the + variant syntax to change the currently active variant is not very favorable, we provide nice + helper members for this: \code - p.optionalFields().init<0>(); - p.optionalFields().init<1>(); + void enableChecksum() const { optionalFields_().init<1>(); } + void disableChecksum() const { optionalFields_().init<0>(); } \endcode - - 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). + By changing the collection we automatically change the field, this collection references. Now we + only need to provide an additional \a checksum member to hide the complex variant access syntax + from the user. And since now all variant access is via specialized members, we can hide the + variant field completely from the user: + \code SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent, (senf::VoidPacketParser) (GREPacketParser_OptFields) ); - typedef GREPacketParser_OptFields::checksum_t checksum_t; - checksum_t checksum() const + GREPacketParser_OptFields::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 field
_t
, 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.
+ As we can see here, we have used the name_t
typedef which is available for
+ all fields to provide the return value for our accessor member.
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
@@ -266,10 +306,10 @@
\subsection howto_newpacket_parser_add Providing additional functionality
- 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.
+ We have now implemented parsing all the header fields. However, often packets would benefit from
+ additional functionality. In the case of GRE, this could be a function to calculate the checksum
+ value if it is enabled. Defining this member will also show, how to safely access the raw packet
+ data from a parser member.
\code
#include