From: g0dil Date: Thu, 24 Jan 2008 13:26:55 +0000 (+0000) Subject: HowTos/NewPacket: More stuff X-Git-Url: http://g0dil.de/git?p=senf.git;a=commitdiff_plain;h=20048a9d9b3f61365a1b344d9c42031be8291bfe HowTos/NewPacket: More stuff admin: Make build.sh do a parallel build and use 'nice' admin: Make build.sh import an optional DOXYGEN envvar senfscons: Allow setting arbitrary simple build environment variables from the command line git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@636 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/HowTos/NewPacket/Mainpage.dox b/HowTos/NewPacket/Mainpage.dox index 8f2a868..ef7a92d 100644 --- a/HowTos/NewPacket/Mainpage.dox +++ b/HowTos/NewPacket/Mainpage.dox @@ -208,7 +208,7 @@ \subsection howto_newpacket_parser_fixvariant Fixing access by providing custom accessor members - Since we don't want to allow che \a checksumPresent() field to be changed directly, we mark this + Since we don't want to allow the \a checksumPresent() field to be changed directly, we mark this field as read-only: \code @@ -232,15 +232,85 @@ 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 GREPacketParser + 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 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. + + 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 + + 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 + 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. + + 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 + #include + 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(GREPacketParser_OptFields); }; @@ -249,15 +319,15 @@ { # 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) - (GREPacketParser_OptFields) ); + (GREPacketParser_OptFields) ); typedef GREPacketParser_OptFields::checksum_t checksum_t; checksum_t checksum() const @@ -267,22 +337,24 @@ void disableChecksum() const { optionalFields_().init<0>(); } SENF_PARSER_FINALIZE(GREPacketParser); + + checksum_t::value_type calculateChecksum() const; }; - \endcode + + GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const + { + if (!checksumEnabled()) + return 0; - 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. + 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() ); - 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). + return cs.sum() + } + \endcode \section howto_newpacket_type Defining the packet type @@ -291,6 +363,7 @@ 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 @@ -330,7 +403,7 @@ 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. + GREPacketParser::init. \li \a initSize is called to find the size of an empty (newly create) GRE packet. This is also provided by GREPacketParser. @@ -389,7 +462,35 @@ SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket ); \endcode - But wait -- what is \c GREPacket ? This question is answered a few section farther on. + 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) + { + if (p->checksumPresent()) + p->checksum() << p->calculateChecksum(); + p->protocolType() << key(p->next(senf::nothrow)); + } + \endcode + + \a finalize() first updates the \a checksum() field if present. It then 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. + + 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 Boost.Optional and as returned + by \c key()). \fixme Document the needed \c \#include files \fixme Provide an advanced section with additional info: How to ensure, that the first 5 bits in diff --git a/admin/build.sh b/admin/build.sh index ac3fdef..e500ea7 100755 --- a/admin/build.sh +++ b/admin/build.sh @@ -24,12 +24,12 @@ if grep -qv '^At ' ../svn-update.log; then fi rm -f ../svn-update.log -echo '$ scons -k all' -scons -k all -echo '$ scons linklint' -scons linklint -echo '$ scons fixlinks' -scons fixlinks +echo "\$ nice scons -kj2 all ${DOXYGEN:+DOXYGEN="$DOXYGEN"}" +nice scons -kj2 all ${DOXYGEN:+DOXYGEN="$DOXYGEN"} +echo "\$ nice scons linklint ${DOXYGEN:+DOXYGEN="$DOXYGEN"}" +nice scons linklint ${DOXYGEN:+DOXYGEN="$DOXYGEN"} +echo "\$ nice scons fixlinks ${DOXYGEN:+DOXYGEN="$DOXYGEN"}" +nice scons fixlinks ${DOXYGEN:+DOXYGEN="$DOXYGEN"} echo -n '# Build completed at '; date --utc exec >../upload.log 2>&1 @@ -41,6 +41,8 @@ fi echo -n '# Upload started at '; date --utc rsync -rzv --del --delete-excluded \ --filter="- .svn" \ + --filter="- linklint" \ + --filter="- debian" \ --filter="+ */" \ --filter="+ *.html" \ --filter="+ *.css" \ diff --git a/senf.dict b/senf.dict index acecafb..8a155b1 100644 --- a/senf.dict +++ b/senf.dict @@ -20,12 +20,14 @@ args async Augustin autoThrottling +autotoc aVectorCollection BaseParser berlios bitfield bool bund +calculateChecksum callback callbacks catched @@ -104,6 +106,7 @@ FileHandle findNext findPrev fixme +fixvariant fokus foo FooParser @@ -113,6 +116,8 @@ FroblizerArea GlobalScope GRE GREPacket +GREPacketParser +GREPacketType GREParser hangup HangupException @@ -122,6 +127,7 @@ Hmm hostname hostnames howto +HowTo href htm html @@ -147,12 +153,15 @@ InvalidPacketChainException IOEvent IOEventInfo ip +IpTypes IpV ipv +IPX iterator join key li +libPacket libPackets LinkScope ListB diff --git a/senfscons/Doxygen.py b/senfscons/Doxygen.py index bff85de..6ef69d1 100644 --- a/senfscons/Doxygen.py +++ b/senfscons/Doxygen.py @@ -464,7 +464,7 @@ def generate(env): 'Doxygen': doxyfile_builder, }) - env.AppendUnique( + env.SetDefault( DOXYGEN = 'doxygen', ) diff --git a/senfscons/SENFSCons.py b/senfscons/SENFSCons.py index b6e1c0e..faa08ee 100644 --- a/senfscons/SENFSCons.py +++ b/senfscons/SENFSCons.py @@ -218,11 +218,12 @@ def MakeEnvironment(): global opts, finalizers InitOpts() env = SCons.Environment.Environment(options=opts) - for opt in opts.options: - if SCons.Script.SConscript.Arguments.get(opt.key): - env[opt.key] = SCons.Script.SConscript.Arguments.get(opt.key) - if SCons.Script.SConscript.Arguments.get('final'): - env['final'] = 1 + env.Replace(**SCons.Script.SConscript.Arguments) + #for opt in opts.options: + # if SCons.Script.SConscript.Arguments.get(opt.key): + # env[opt.key] = SCons.Script.SConscript.Arguments.get(opt.key) + #if SCons.Script.SConscript.Arguments.get('final'): + # env['final'] = 1 env.Help("\nSupported build variables (either in SConfig or on the command line:\n") env.Help(opts.GenerateHelpText(env))