HowTos/NewPacket: More stuff
g0dil [Thu, 24 Jan 2008 13:26:55 +0000 (13:26 +0000)]
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

HowTos/NewPacket/Mainpage.dox
admin/build.sh
senf.dict
senfscons/Doxygen.py
senfscons/SENFSCons.py

index 8f2a868..ef7a92d 100644 (file)
 
     \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
     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 <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
+
+    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 <senf/Packets.hh>
+    #include <senf/Utils/IpChecksum.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(GREPacketParser_OptFields);
     };
     {
     #   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
         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 <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.
+        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
     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
         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.
     
     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 <a
+    href="http://www.boost.org/libs/optional/doc/optional.html">Boost.Optional</a> 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
index ac3fdef..e500ea7 100755 (executable)
@@ -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" \
index acecafb..8a155b1 100644 (file)
--- 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
index bff85de..6ef69d1 100644 (file)
@@ -464,7 +464,7 @@ def generate(env):
       'Doxygen': doxyfile_builder,
    })
 
-   env.AppendUnique(
+   env.SetDefault(
       DOXYGEN = 'doxygen',
    )
 
index b6e1c0e..faa08ee 100644 (file)
@@ -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))