X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Packets%2FPacketParser.hh;h=631bf10f915736700f0796cabcbba485d9f8b80b;hb=d084c02e4914da4fa1a4098a33d704f69e8bdedd;hp=e5eee7797d67cb39fc034d2a6a4f5260be9ba761;hpb=f13c1275e48e97dceb7de7925793a4c69a5aeb61;p=senf.git diff --git a/Packets/PacketParser.hh b/Packets/PacketParser.hh index e5eee77..631bf10 100644 --- a/Packets/PacketParser.hh +++ b/Packets/PacketParser.hh @@ -1,7 +1,7 @@ // Copyright (C) 2007 // Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) // Kompetenzzentrum fuer Satelitenkommunikation (SatCom) -// Stefan Bund +// Stefan Bund // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,20 +24,45 @@ /** \defgroup packetparser The PacketParser facility 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 data structure with added type information - providing parsing functions. + 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. - Parsers are built hirarchically. A high-level parser will return other parsers when returning - some element (Example: Asking an EthernetParser for the ethertype field by calling the parsers - \c type() member will return an UInt16 parser). The lowest level building blocks then return the + 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 \c + type() member will return an \c UInt16 parser). The lowest level building blocks then return the values. This hierarchical structure greatly simplifies building complex parsers. - Every parser is derived from senf::PacketParserBase. This parser provides the necessary - housekeeping information and provides the parsers with access to the data. + Since parsers are very lightweight and are passed by value, packet fields are accessed using the + corresponding accessor method: + \code + 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() + \endcode - The PacketParser facility predefines several parsers to be used as building blocks in defining - more complex parsers (integer parsers, several parsers for repetitive constructs) + 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 Parse_UInt16 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). + + More complex parsers (especially those representing a collection of values) provide an + additional wrapper class for mutating access (e.g. Parse_Vector provides a container wrapper + with am STL compatible random-access sequence interface). See the documentation of the specific + parser for the wrapper specification. + + Every parser is derived from senf::PacketParserBase. This class provides the necessary + housekeeping information and provides the parsers with access to the data. */ #ifndef HH_PacketParser_ @@ -49,25 +74,37 @@ #include #include "Utils/SafeBool.hh" #include "PacketTypes.hh" -#define HH_PacketData_DeclOnly #include "PacketData.hh" -#undef HH_PacketData_DeclOnly #include "PacketParser.mpp" ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { - /** \brief Parser Baseclass + /** \brief Parser Base class + + Parsers come in two favors: fixed and dynamically sized parsers. A fixed size + parser has a constant size, it will always parse a fixed number of bytes. The low-level + 'final' parsers (like the integer parsers) are fixed size parsers as are composite parsers + built up only of fixed-size fields. - To implement a packet parser, you need to derive from PacketParserBase and implement several - required members. There are two ways how to do this. - \li If the parser just consists of a simple sequence of consecutive fields, the - SENF_PACKET_PARESR_DEFINE_FIELDS and SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS macros - provide a simple an convenient way to define the packet + A dynamically sized parser on the other hand infers it's size from the contents of + the data parsed. Any parser containing at least one dynamically sized sub-parser will itself + be dynamically sized. + + Both kinds of parser need to derive from PacketParserBase and implement several required + members. Which members to implement depends on the parsers flavor. There are two ways how to + do this. + \li If the parser just consists of a simple sequence of consecutive fields (sub-parsers), + the \ref SENF_PACKET_PARSER_DEFINE_FIELDS and \ref + SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS macros provide a simple and convenient way to + define the packet \li In more complex cases, you need to implement the necessary members manually. - The following example documents the interface (which must be) provided by a parser. + This documentation is about the manual implementation. You should nevertheless read through + this to understand, what above macros are doing. + + The following example documents the interface (which must be) provided by a parser: \code struct FooParser : public PacketParserBase { @@ -85,7 +122,7 @@ namespace senf { // of bytes to allocate when creating a new object static const size_type init_bytes = some_constant_size; - // You also mey define an init() member. This will be called to initialize a newly + // You also may define an init() member. This will be called to initialize a newly // created data object. The default implementation just does nothing. void init() const; @@ -101,13 +138,13 @@ namespace senf { \endcode You should never call the \c bytes() member of a parser directly. Instead you should use the - freestanding senf::bytes() functon. This function will return the correct size even for - fixed-size parsers. You may access \c fixed_bytes directly, however be aware that this will - restrict your code to fixed size parsers (which depending on the circumstances may be + freestanding senf::bytes() function. This function will return the correct size irrespective + of the parsers flavor. You may access \c fixed_bytes directly, however be aware that this + will restrict your code to fixed size parsers (which depending on the circumstances may be exactly what you want). - In the same way, dont access \c init_bytes directly, always use the senf::init_bytes - metafunction class which will correctly support fixed size parsers. + In the same way, don't access \c init_bytes directly, always use the senf::init_bytes + meta-function class which correctly supports fixed size parsers. \ingroup packetparser */ @@ -117,11 +154,11 @@ namespace senf { /////////////////////////////////////////////////////////////////////////// // Types - typedef detail::packet::iterator data_iterator; - typedef detail::packet::size_type size_type; - typedef detail::packet::difference_type difference_type; - typedef detail::packet::byte byte; - typedef PacketData * state_type; + typedef detail::packet::iterator data_iterator; ///< Raw data iterator type + typedef detail::packet::size_type size_type; ///< Unsigned integral type + typedef detail::packet::difference_type difference_type; ///< Signed integral type + typedef detail::packet::byte byte; ///< Unsigned 8bit value, the raw value type + typedef PacketData * state_type; ///< Type of the 'state' parameter /////////////////////////////////////////////////////////////////////////// ///\name Structors and default members @@ -135,23 +172,68 @@ namespace senf { ///@} /////////////////////////////////////////////////////////////////////////// - data_iterator i() const; - state_type state() const; - PacketData & data() const; - - void init() const; + data_iterator i() const; ///< Return beginning of data to parse + /**< The parser is expected to interpret the data beginning + here. The size of the interpreted is given by + senf::bytes(parser + instance). */ + state_type state() const; ///< Return state of this parser + /**< The value returned should be interpreted as an opaque + value provided just to be forwarded to other + parsers. */ + PacketData & data() const; ///< Access the packets raw data container + /**< This member will return the raw data container holding + the data which is parsed by \c this parser. */ + + void init() const; ///< Default implementation + /**< This is just an empty default + implementation. Re-implement this member in your own + parsers if needed. */ protected: - PacketParserBase(data_iterator i, state_type s); - PacketParserBase(data_iterator i, state_type s, size_type size); - - bool check(size_type size); - void validate(size_type size); - - template Parser parse(data_iterator i) const; - template Parser parse(size_type n) const; - - void defaultInit() const; + PacketParserBase(data_iterator i, state_type s); ///< Standard constructor + /**< This is the constructor used by most parsers. The + parameters are just forwarded from the derived classes + constructor parameters. */ + PacketParserBase(data_iterator i, state_type s, size_type size); + ///< Size checking constructor + /**< In addition to the standard constructor, this + constructor will validate, that there is enough data in + the raw data container to parse \a size bytes after \a + i. + + This constructor is called by all 'final' parsers + (e.g. the integer parsers) and \e only by those + parsers. Most parsers do \e not check the validity of + the iterator, this is delayed until the very last + parser. This allows to partial parse truncated + packets. + + \throw TruncatedPacketException if the raw data + container does not hold at least \a size bytes + beginning at \a i. */ + + bool check(size_type size); ///< Check size of data container + /**< \returns \c true, if the data container holds at least + \a size beginning at i(), \c false otherwise. */ + void validate(size_type size); ///< Validate size of data container + /**< \throws TruncatedPacketException if the raw data + container does not hold at least \a size bytes + beginning at i(). */ + + template Parser parse(data_iterator i) const; ///< Create sub-parser + /**< Creates a new instance of \a Parser to parse data + beginning at \a i. Automatically passes \a state() to + the new parser. */ + template Parser parse(size_type n) const; ///< Create sub-parser + /**< Creates a new instance of \a Parser to parse data + * beginning at i() + \a n. Automatically passes \a + state() to the new parser. */ + + void defaultInit() const; ///< Default implementation + /**< This is just an empty default + implementation. Re-implement this member in your own + parsers if needed. */ private: data_iterator end(); @@ -183,16 +265,16 @@ namespace senf { /** \brief Return number of bytes to allocate to new object of given type - This metafcuntion is called like + This meta-function is called like \code senf::init_bytes::value \endcode - This expression evaluates to a compile-time constant itegral expression of type - senf::PacketParserBase::size_type. This metafunction will return \c Parser::fixed_bytes or + This expression evaluates to a compile-time constant integral expression of type + senf::PacketParserBase::size_type. This meta-function will return \c Parser::fixed_bytes or \c Parser::init_bytes depending on the type of parser. - \param[in] Parser Parser to return init_bytes for + \param[in] Parser The Parser to return init_bytes for \returns Number of bytes to allocate to the new object \ingroup packetparser */ @@ -200,12 +282,32 @@ namespace senf { struct init_bytes : public detail::ParserInitBytes {}; + /** \brief Generic parser copying + + This operator allows to copy the values of identical parsers. This operation does \e 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 \a + target parser, it changes the data referenced by the \a target parser. + + \ingroup packetparser + */ template typename boost::enable_if< boost::is_base_of, Parser >::type operator<<(Parser target, Parser source); + /** \brief Generic parser value assignment + + This operator allows to assign a value to parsers which implement a value(\a + value) member. This operator allows to use a common syntax for assigning values or + parsers to a parser. + + \ingroup packetparser + */ template typename boost::enable_if_c < boost::is_base_of::value @@ -216,32 +318,16 @@ namespace senf { /** \defgroup packetparsermacros Helper macros for defining new packet parsers To simplify the definition of simple packet parsers, several macros are provided. Before - using these macros you should familarize yourself with the packet parser interface as + using these macros you should familiarize yourself with the packet parser interface as described in senf::PacketParserBase. These macros simplify providing the above defined interface. A typical packet declaration using these macros has the following form (This is a concrete example from the definition of - the ethernet packet in DefaultBundle//EthernetPacket.hh) - \code - struct Parse_EthVLan : public senf::PacketParserBase - { - SENF_PACKET_PARSER_INIT(Parse_EthVLan); - - // //////////////////////////////////////////////////////////////////////// - - typedef senf::Parse_UIntField < 0, 3 > Parse_Priority; - typedef senf::Parse_Flag < 3 > Parse_CFI; - typedef senf::Parse_UIntField < 4, 16 > Parse_VLanId; - typedef senf::Parse_UInt16 Parse_Type; - - SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS( - ((OverlayField)( priority, Parse_Priority )) - ((OverlayField)( cfi, Parse_CFI )) - ((Field )( vlanId, Parse_VLanId )) - ((Field )( type, Parse_Type )) - ); - }; - \endcode + the ethernet packet in DefaultBundle/EthernetPacket.hh) + + \dontinclude EthernetPacket.hh + \skipline struct Parse_EthVLan : public PacketParserBase + \until }; The macros take care of the following: \li They define the accessor functions returning parsers of the given type. @@ -273,6 +359,13 @@ namespace senf { ) \endcode + For each field, this command will define + \li A method \a name() returning an instance of the \a type parser + \li \a name_t as a typedef for \a type, the fields value + \li \a name_offset to give the offset of the field from the beginning of the + parser. If the parser is a fixed size parser, this will be a static constant, otherwise + it will be a method. + The \a builder argument selects, how the field is defined \li Field defines a field and increments the current position by the size of the field @@ -284,10 +377,10 @@ namespace senf { The \a name argument defines the name of the accessor method. The \a type argument is the parser to return for that field. Since none of the arguments may - contain a komma, This argument cannot be a template. Always use typedefs to access - tempalte parsers as shown above. + contain a comma, This argument cannot be a multi-parameter template. Always use + typedefs to access templated parsers as shown above. - The \ref SENF_PACKET_PARSER_INIT makro defines the constructor and the \c init() member. If + The \ref SENF_PACKET_PARSER_INIT macro defines the constructor and the \c init() member. If you want to provide your own \c init() implementation, use \ref SENF_PACKET_PARSER_NO_INIT. The first statement in your init method should probably to call \c defaultInit(). This will call the \c init() member of all the fields. Afterwards you can @@ -331,7 +424,7 @@ namespace senf { name(data_iterator i, state_type s) : senf::PacketParserBase(i,s) {} \ void init() const { defaultInit(); } - /** \brief Define initialization mebers of a parser except init() + /** \brief Define initialization members of a parser except init() This macro is like SENF_PACKET_PARSER_INIT but does \e not define \c init(). This allows you to provide your own implementation. You should call \c defaultInit() first before @@ -352,8 +445,37 @@ namespace senf { \hideinitializer */ # define SENF_PACKET_PARSER_DEFINE_FIELDS(fields) \ - SENF_PACKET_PARSER_I_DEFINE_FIELDS(fields) + SENF_PACKET_PARSER_I_DEFINE_FIELDS(0,fields) + + /** \brief Define fields for a dynamically sized parser (with offset) + + Define the fields as specified in \a fields. This macro supports dynamically sized + subfields, the resulting parser will be dynamically sized. + + The \a offset argument gives the byte offset at which to start parsing the fields. This + helps defining extended parser deriving from a base parser: + \code + struct ExtendedParser : public BaseParser + { + ExtendedParser(data_iterator i, state_type s) : BaseParser(i,s) {} + SENF_PACKET_PARSER_DEFINE_FIELDS_OFFSET(senf::bytes(BaseParser(*this)), + ( ... fields ... ) ); + + void init() { + BaseParser::init(); + defaultInit(); + // other init code + } + } + \endcode + + \ingroup packetparsermacros + \hideinitializer + */ +# define SENF_PACKET_PARSER_DEFINE_FIELDS_OFFSET(offset,fields) \ + SENF_PACKET_PARSER_I_DEFINE_FIELDS(offset,fields) + /** \brief Define fields for a fixed size parser Define the fields as specified in \a fields. This macro only supports fixed size @@ -363,15 +485,59 @@ namespace senf { \hideinitializer */ # define SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS(fields) \ - SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(fields) + SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(0,fields) + + /** \brief Define fields for a fixed size parser + + Define the fields as specified in \a fields. This macro only supports fixed size + subfields, the resulting parser will also be a fixed size parser. + The \a offset argument gives the byte offset at which to start parsing the fields. This + helps defining extended parser deriving from a base parser: + \code + struct ExtendedParser : public BaseParser + { + ExtendedParser(data_iterator i, state_type s) : BaseParser(i,s) {} + + SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS_OFFSET(BaseParser::fixed_bytes, + ( ... fields ... ) ); + + void init() { + BaseParser::init(); + defaultInit(); + // other init code + } + } + \endcode + + \ingroup packetparsermacros + \hideinitializer + */ +# define SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS_OFFSET(offset,fields) \ + SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(offset,fields) + + /** \brief Default parser parsing nothing + */ struct VoidPacketParser : public PacketParserBase { SENF_PACKET_PARSER_INIT(VoidPacketParser); }; - /** \brief + /** \brief Iterator re-validating Parser wrapper + + An ordinary parser will be invalidated whenever the raw data container's size is + changed. This can complicate some algorithms considerably. + + This wrapper will update the parsers iterator (the value returned by the i() member) on + every access. This ensures that the iterator will stay valid. + + \attention Beware however, if you insert or remove data before the safe wrapper, the + location will \e not be updated accordingly and therefore the parser will be + invalid. + + Additionally a SafePacketparser has an uninitialized state. The only allowed operations in + this state are the boolean test for validity and assigning another parser. */ template class SafePacketParser @@ -385,23 +551,26 @@ namespace senf { ///\name Structors and default members ///@{ - // default default constructor // default copy constructor // default copy assignment // default destructor - SafePacketParser(); + SafePacketParser(); ///< Create an empty uninitialized SafePacketParser // conversion constructors - SafePacketParser(Parser parser); + SafePacketParser(Parser parser); ///< Initialize SafePacketParser from \a parser - SafePacketParser & operator=(Parser parser); + SafePacketParser & operator=(Parser parser); ///< Assign \a parser to \c this ///@} /////////////////////////////////////////////////////////////////////////// - Parser operator*() const; - Parser const * operator->() const; - bool boolean_test() const; + Parser operator*() const; ///< Access the stored parser + /**< On every access, the stored parsers iterator will be + updated / re-validated. */ + Parser const * operator->() const; ///< Access the stored parser + /**< On every access, the stored parsers iterator will be + updated / re-validated. */ + bool boolean_test() const; ///< Check validity protected: @@ -413,6 +582,9 @@ namespace senf { } ///////////////////////////////hh.e//////////////////////////////////////// +#endif +#if !defined(SENF_PACKETS_DECL_ONLY) && !defined(HH_PacketParser_i_) +#define HH_PacketParser_i_ #include "PacketParser.cci" #include "PacketParser.ct" #include "PacketParser.cti" @@ -428,3 +600,4 @@ namespace senf { // compile-command: "scons -u test" // comment-column: 40 // End: +