Skip to content
Snippets Groups Projects
Packet.hh 23.8 KiB
Newer Older
sbund's avatar
sbund committed
// $Id$
//
// Copyright (C) 2006
sbund's avatar
sbund committed
// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
//     Stefan Bund <stefan.bund@fokus.fraunhofer.de>
//
// 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
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

/** \file
    \brief Main packet interface
sbund's avatar
sbund committed

    \todo Implement assign() method akin to reinterpret(). However,
    instead of using the data already present, assign() will replace
    the date of the current packet with the given Packet.
sbund's avatar
sbund committed

    \todo Implement wrapping-constructor. Somehow we want to have a
    constructor, which allows creating a chain of packet interpreters
    with as little overhead as possible.
sbund's avatar
sbund committed

    \todo Document the additional concrete Packet facade requirements
    explicitly and not only within the Parser requirements (check(),
    bytes() and min_bytes() members ...)
sbund's avatar
sbund committed

    \todo Implement special container replacing vector which manages
    some headroom to allow efficient insertion of elements at the
tho's avatar
tho committed
    beginning. This really is just another type of dequeue
    implementation.
sbund's avatar
sbund committed
 */

#ifndef HH_Packet_
#define HH_Packet_ 1

// Custom includes
#include <boost/utility.hpp> // for boost::noncopyable
#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <list>
#include <vector>
#include <iostream>

#include "Packet.mpp"
// ////////////////////////////hh.p////////////////////////////////////////

sbund's avatar
sbund committed
    namespace impl { template <class OtherPacket> class PkReg_EntryImpl; }
    namespace impl { class PacketImpl; }

    /** \brief Basic interface to all packet facades

        \section packet_overview Overview
sbund's avatar
sbund committed
        This class is the base class of all Packets. It implements the
        generic Packet interface and provides the packet management
        framework. senf::Packet manages the necessary memory
tho's avatar
tho committed
        resources and controls the chain of packet interpreters.
sbund's avatar
sbund committed

        The Packet user always interfaces with the pkf via a Packet
        derived class. This is the only external entity ever held by a
        library user. The interface is implemented using a reference
        counted smart pointer, so resource management is quasi
        automatic.

        \image html structure.png Overview
sbund's avatar
sbund committed
        Internally, every Packet references a PacketImpl instance which
        manages the raw packet data and the interpreter list. This raw
        data is interpreted by the concrete Packet derived class
tho's avatar
tho committed
        according to the definition of that derived classes packet
sbund's avatar
sbund committed
        type (i.e. EthernetPacket or UDPPacket).

        Packet provides several interfaces:
sbund's avatar
sbund committed
        - Creation of Packet instances: create()

        - Access to the chain of interpreters: next(), prev(), head(),
          last(), find_next(), find_prev(), get_next(), get_prev(),
          is(), as() and reinterpret()

        - Access to the raw packet data: begin(), end(), size(),
          insert() and erase()

        - An interface to the derived class: v_nextInterpreter(),
          v_finalize(), registerInterpreter()


        \section packet_der Implementing new Packet facades

        To implement a new Packet facade, publically derive from
        Packet. You need to implement the following minimal interface:

        - You need to provide a new #ptr typedef

        - You have to implement v_nextInterpreter() and v_finalize()

        - The constructor should be private

        - You must make Packet a \c friend of the new Packet facade

        - You must implement a static check() method which validates
          a byte region as your new Packet

        \code
            class ExamplePacket
                : public senf::Packet
sbund's avatar
sbund committed
            {
            public:
                typedef ptr_t<ExamplePacket>::ptr ptr;

                static bool check(Packet::iterator begin, Packet::iterator end)
                {
                    // Validate, that the region [begin,end) can be
                    // interpreted as an ExamplePacket without risking
                    // memory access violations.
                }

            private:
                template <class Arg>
                ExamplePacket(Arg arg [, other args ... ])
                    : senf::Packet(arg)
sbund's avatar
sbund committed
                {}

                virtual void v_nextInterpreter() const
                {
                    // NextPacketType and header_length of course
                    // depend on the packet type
                    registerInterpreter<NextPacketType>(begin()+header_length, end());
                }

                virtual void v_finalize()
                {
                    // calculate checksum etc
                }

                friend class senf::Packet;
sbund's avatar
sbund committed
            };
        \endcode

        Please do not implement the methods inline to not clutter up
        the header file. This is done here in the example to simplify
        it. If a class is to be registered in some
        senf:PacketRegistry, it must not take any additional
sbund's avatar
sbund committed
        constructor parameters.

tho's avatar
tho committed
        After having implemented the bare framework, the most common
sbund's avatar
sbund committed
        way to implement access to the packets specific data is to use
        the parser framework by additionally inheriting a
        corresponding parser. This also automatically implements the
        check() method, which is provided by the Parser.

        In the following example we only show the differences from the
        previous example:

        \code
            class ExamplePacket
                : public senf::Packet,
                  public Parse_Example<senf::Packet::iterator,
sbund's avatar
sbund committed
                                       ExamplePacket>
            {

                // check does not need to be implemented here, it is
                // inherited from the parser
sbund's avatar
sbund committed

            private:
                template <class InputIterator>
                ExamplePacket(InputIterator begin, InputIterator end)
                    : senf::Packet(begin,end)
sbund's avatar
sbund committed
                {}
            };
        \endcode

        See the senf::ParserBase Documentation for how to
sbund's avatar
sbund committed
        implement Parse_Example.

        The implementation of v_nextInterpreter most of the time
        relies on some packet registry. This is simplified using the
        senf::PacketRegistryMixin class as follows. Again, we
sbund's avatar
sbund committed
        only show the differences from the preceding Example:

        \code
            struct ExampleRegistry {
                type boost::uint16_t key_t;
            };

            class ExamplePacket
                : public senf::Packet,
                  public Parse_Example<senf::Packet::iterator,
sbund's avatar
sbund committed
                                       ExamplePacket>,
                  public senf::PacketRegistryMixin<ExampleRegistry,
sbund's avatar
sbund committed
                                                          ExamplePacket>
            {
                using senf::Packet::registerInterpreter;
                using senf::PacketRegsitryMixin<ExampleRegistry,ExamplePacket>::registerInterpreter;
sbund's avatar
sbund committed
            private:
                virtual void v_nextInterpreter() const
                {
                    // nextType() is defined in Parse_Example and
                    // returns the key in the ExampleRegistry of the
                    // next Packet.
                    registerInterpreter(nextType(),begin()+header_length, end());
                }
            };
        \endcode

        For further details on the packet registry, see
        senf::PacketRegistry.
sbund's avatar
sbund committed

        \section packet_impl Implementation details

        The Packet interface is implemented to minimize overhead as
        far as possible without getting to complex. One area for
tho's avatar
tho committed
        improvement is the container class used to hold the raw
sbund's avatar
sbund committed
        data. This currently is an \a std::vector. This could be
tho's avatar
tho committed
        improved by either allocating some headroom/tailroom in the
sbund's avatar
sbund committed
        vector and using this when inserting data at the beginning or
        end. Alternatively, a new container class (like the
        senf::deque_list) could be used to support zero-copy
sbund's avatar
sbund committed
        semantics.

        At the moment, we leave the implementation at
        std::vector. This container is very simple and especially it
        can directly be sent out using the operating system since a \a
        vector stores data at contiguous memory locations. An \a
        std::deque could be used with \a writev(), however since we
        have no access to the implementation details of the \a deque,
        we cannot construct the \a writev() data structures.

        The interpreter list managed by Packet is lazy, meaning packet
tho's avatar
tho committed
        interpreter facades are added only when requested by next(),
sbund's avatar
sbund committed
        last() or find_next(). v_nextInterpreter() is called if
        necessary by these methods to complete the interpreter chain.

        To implement the automatic memory management, every Packet
        facade is reference counted. Additionally, the number of
        (indirect) references to PacketImpl is counted. This allows to
        manage the PacketImpl instance automatically. To make this
        work, it is necessary to ensure throughout the Packet code,
        that the reference count of a Packet is not accidentally
        decremented to zero. Also, the internal pointers from the
        interpreter list to the Packet facades must not be
        counted. They are therefore implemented differently (
        boost::shared_ptr vs. boost::intrusive_ptr). The choice of
tho's avatar
tho committed
        boost::intrusive_ptr for the externally visible smart pointer
sbund's avatar
sbund committed
        for all Packet facades is taken to reduce the overhead (an
        intrusive_ptr is only the size of an ordinary pointer, a
        smart_ptr has the size of two pointers).

        \nosubgrouping
      */
    class Packet : boost::noncopyable
    {
    public:
        ///\name Types
        ///@{
        typedef boost::uint8_t byte; //!< single byte datatype
        ///@}

    private:
        ///\name Implementation
        ///@{
        // These types are implementation details. They are however
        // needed to provide the correct typedefs for the user
        // interface. Hiding these classes would incur a huge
        // additional indirection overhead.

        typedef std::vector<byte> raw_container;
        typedef boost::shared_ptr<Packet> interpreter_list_ptr;
        typedef std::list<senf::Packet::interpreter_list_ptr> interpreter_list;
sbund's avatar
sbund committed
        typedef unsigned refcount_t;

        ///@}

    public:

        ///////////////////////////////////////////////////////////////////////////
        ///\name Types
        ///@{
sbund's avatar
sbund committed
        /** \brief smart pointer template for all Packet classes
sbund's avatar
sbund committed
            This struct is just a template typedef. It defines the
            smart pointer used for all Packet classes.
         */
        template <class T> struct ptr_t { typedef boost::intrusive_ptr<T> ptr; };

        /** \brief smart pointer to the Packet facades

            Every derived class \e must redeclare this member for it's
            derived type:
sbund's avatar
sbund committed
                typedef ptr_t<DerivedClass>::ptr ptr
            \endcode
         */
        typedef ptr_t<Packet>::ptr ptr;
sbund's avatar
sbund committed
        typedef raw_container::iterator iterator; //!< raw data iterator
        typedef raw_container::size_type size_type;
        typedef raw_container::difference_type difference_type;

        ///@}

        // ////////////////////////////////////////////////////////////////////////

        ///\name Creating packets
        ///@{

        /** \brief create new Packet
sbund's avatar
sbund committed
            This method is used to create a new Packet. All Packet
            instances are created via this method, they are \e never
            created directly from the Packet derived class.

            \param OtherPacket Type of Packet to create, a Packet
                    derived class
            \param b begin iterator of byte range to create the Packet
                    from
            \param e corresponding end iterator
            \return smart pointer to new packet
            \throws TruncatedPacketException The data cannot be parsed
tho's avatar
tho committed
                    securely (the data might be truncated or just
sbund's avatar
sbund committed
                    plain invalid)
         */
        template <class OtherPacket, class InputIterator>
        static typename ptr_t<OtherPacket>::ptr create(InputIterator b, InputIterator e);

        template <class OtherPacket>
        static typename ptr_t<OtherPacket>::ptr create();

        template <class OuterPacket>
        static typename ptr_t<OuterPacket>::ptr create(Packet::ptr payload);

        ///@}

        ///\name Interpreter chain
        ///@{
sbund's avatar
sbund committed
        /** \brief get next packet from the interpreter chain
            \return smart pointer to next packet or 0 if last packet */
        ptr next() const;
        /** \brief get previous packet from the interpreter chain
            \return smart pointer to previous packet or 0 if last packet */
        ptr prev() const;
        /** \brief first packet of the interpreter chain
            \return smart pointer to first packet */
        ptr head() const;
        /** \brief get last packet of the interpreter chain
            \return smart pointer to last packet */
        ptr last() const;
sbund's avatar
sbund committed
        /** \brief first packet of given type after the current packet
            \return smart pointer to first following packet of type \a
                OtherPacket or 0, if no such packet exists */
        template <class OtherPacket> typename ptr_t<OtherPacket>::ptr find_next() const;
        /** \brief first packet of given type before the current packet
            \return smart pointer to first preceding packet of type \a
                OtherPacket or 0, if no such packet exists */
        template <class OtherPacket> typename ptr_t<OtherPacket>::ptr find_prev() const;

        /** \brief first packet of given type after the current packet
            \return smart pointer to first following packet of type \a
            OtherPacket. \e Assert's, that a packet of this type exists */
        template <class OtherPacket> typename ptr_t<OtherPacket>::ptr get_next() const;
        /** \brief first packet of given type before the current packet
            \return smart pointer to first preceding packet of type \a
            OtherPacket. \e Assert's, that a packet of this type exists */
        template <class OtherPacket> typename ptr_t<OtherPacket>::ptr get_prev() const;

tho's avatar
tho committed
        /** \brief check, whether the packet is of the given type
            \return true, if packet is of type \a OtherPacket, false
sbund's avatar
sbund committed
                otherwise */
        template <class OtherPacket> bool is() const;
sbund's avatar
sbund committed
        /** \brief cast packet pointer to the given type
            \return a properly cast smart pointer if packet is of type
                \a OtherPacket. Otherwise return 0 */
        template <class OtherPacket> typename ptr_t<OtherPacket>::ptr as();
sbund's avatar
sbund committed

        /** \brief replace current packet interpreter

            This method will \e replace the current packet facade in
            the interpreter list with a new interpreter given by \a
            OtherPacket.
sbund's avatar
sbund committed

            \attention This invalidates the packet instance \e
            this. You must ensure, not to use the Packet instance any
            further after this call
sbund's avatar
sbund committed

            \return smart pointer to a \e new packet facade
            \throws TruncatedPacketException there is not enough data
tho's avatar
tho committed
                to safely interpret the packet as the given type. The
sbund's avatar
sbund committed
                original packet is \e not invalidated
         */
        template <class OtherPacket>
        typename ptr_t<OtherPacket>::ptr reinterpret();

        ///@}

        ///\name Raw packet data
        ///@{

tho's avatar
tho committed
        /** \brief begin iterator of raw packet data
sbund's avatar
sbund committed
            This iterator allows access to the raw data interpreted by
            the packet facade. This \e includes any header possibly
            interpreted by the derived packet instance. To access the
            payload of the packet, use next()->begin().

            \return random access iterator to the begin of the raw
                data */
        iterator begin() const;
        /** \brief past-the-end iterator of raw packet data
sbund's avatar
sbund committed
            This iterator allows access to the raw data interpreted by
            the packet facade. This \e includes any header possibly
            interpreted by the derived packet instance. To access the
            payload of the packet, use next()->end().

            \return random access past-the-end iterator of the raw
                data */
        iterator end() const;
        /** \brief raw data size of packet
            \return size of the raw data interpreted by this
                packet in bytes. This is \e not necessarily the size of
                the complete packet, use head()->size() for this. */
        size_t size() const;

        // Modifying the raw packet data

        typedef enum { AUTO, BEFORE, INSIDE, OUTSIDE, AFTER } Whence;

        /** \brief insert single byte \a v before pos

            \attention The change will \e not be validated by the
            derived packet instance. This method is mostly to be used
            by the derived class implementation and their helper
            classes. */
        void insert(iterator pos, byte v, Whence whence = AUTO);
        /** \brief insert \a n copies of byte \a v before pos

            \attention The change will \e not be validated by the
            derived packet instance. This method is mostly to be used
            by the derived class implementation and their helper
            classes. */
        void insert(iterator pos, size_type n, byte v, Whence whence = AUTO);
        /** \brief insert a copy of the given range before pos

            \attention The change will \e not be validated by the
            derived packet instance. This method is mostly to be used
            by the derived class implementation and their helper
            classes. */
        template <class InputIterator>
sbund's avatar
sbund committed
        void insert(iterator pos, InputIterator f, InputIterator l, Whence whence = AUTO);

        /** \brief erase single byte

            \attention The change will \e not be validated by the
            derived packet instance. This method is mostly to be used
            by the derived class implementation and their helper
            classes. */
        void erase(iterator pos);
        /** \brief erase range

            \attention The change will \e not be validated by the
            derived packet instance. This method is mostly to be used
            by the derived class implementation and their helper
            classes. */
        void erase(iterator first, iterator last);

        ///@}

        void dump(std::ostream & os) const;

    protected:
        ///\name Derived class interface
        ///@{

        /** \brief create new interpreter facade for an existing packet
sbund's avatar
sbund committed
            This constructor is called, when a new interpreter is to
            be added to the interpreter chain. The constructor is
            called indirectly from registerInterpreter() or
            reinterpret() via the derived classes template
            constructor.
         */
        template <class Operation>
        Packet(Operation const & arg);
        virtual ~Packet();
sbund's avatar
sbund committed
    private:
        /** \brief create next packet interpreter

            This method is called by next(), last() or find_next() to
            create any missing interpreters in the interpreter
            chain. This method must be overridden in the derived class
            to register the next packet interpreter in the interpreter
            chain with the packet framework.

            To register the new interpreter, use
            registerInterpreter() to create the new Packet
            instance. The new instance is automatically added to the
            interpreter chain after the current interpreter.

            See also senf::PacketRegistryMixin on how to
sbund's avatar
sbund committed
            use a Registry to find the next interpreters implementing
            class.
         */
        virtual void v_nextInterpreter() const = 0;

        /** \brief finalize packet for sending

            This method is called by the packet framework to let the
            interpreter facade do some final calculations/packet
            cleanup before the packet is sent out or digested in some
tho's avatar
tho committed
            other way. This is the place to calculate checksums and
sbund's avatar
sbund committed
            such.

tho's avatar
tho committed
            This method is automatically called for all interpreters on
sbund's avatar
sbund committed
            the interpreter chain.
         */
        virtual void v_finalize() = 0;

        virtual void v_dump(std::ostream & os) const = 0;

    protected:
        /** \brief add interpreter to interpreter chain

            This method is used by v_nextInterpreter() in the derived
            classes to add a new interpreter to the interpreter
            chain. This method will call \c OtherPacket's constructor
            with the correct arguments and insert the new interpreter
            into the interpreter list. This method is used, if no
            further arguments are to be passed to the \c OtherPacket
            constructor. If additional arguments are necessary, just
            add them after \c end. The compiler will then choose the
            correct overload to use.
         */
        template <class OtherPacket>
        typename ptr_t<OtherPacket>::ptr registerInterpreter(
            raw_container::iterator begin, raw_container::iterator end) const;
        template <class OtherPacket, class A0>
        typename ptr_t<OtherPacket>::ptr registerInterpreter(
            raw_container::iterator begin, raw_container::iterator end,
            A0 const & a0) const;

#       define BOOST_PP_ITERATION_PARAMS_1 (4, (2, 9, "Packets/Packet.mpp", 3))
#       include BOOST_PP_ITERATE()

        ///@}

    private:

        ///\name Implementation
        ///@{

        void add_ref() const;
        bool release();
        bool unlink();

        struct PacketOp_register;
        friend class PacketOp_register;
sbund's avatar
sbund committed
        void i_registerInterpreter(Packet * p) const;

        struct PacketOp_replace;
        friend class PacketOp_replace;
sbund's avatar
sbund committed
        void i_replaceInterpreter(Packet * p);

        struct PacketOp_set;
        friend class PacketOp_set;
sbund's avatar
sbund committed
        void i_setInterpreter(impl::PacketImpl * i);

    private:
        friend class impl::PacketImpl;
        template <class OtherPacket> friend class impl::PkReg_EntryImpl;

        impl::PacketImpl* impl_;
        size_type begin_;
        size_type end_;
        interpreter_list::iterator self_;
        mutable bool parsed_;
        mutable refcount_t refcount_;

        ///@}
    };

    /** \brief dump packet to stream
        \related Packet */
    // std::ostream & operator<<(std::ostream & os, Packet const & packet);

    /** \brief smart pointer handling
        \relates Packet */
    void intrusive_ptr_add_ref(Packet const *);
    /** \brief smart pointer handling
        \relates Packet */
    void intrusive_ptr_release(Packet *);

    struct TruncatedPacketException : public std::exception
    { virtual char const * what() const throw() { return "truncated packet"; } };

sbund's avatar
sbund committed

// ////////////////////////////hh.e////////////////////////////////////////
#include "Packet.cci"
#include "Packet.ct"
#include "Packet.cti"

#include "Packet.mpp"
#endif


// Local Variables:
// mode: c++
// fill-column: 100
// c-file-style: "senf"
// indent-tabs-mode: nil
// ispell-local-dictionary: "american"
// compile-command: "scons -u test"
// comment-column: 40
sbund's avatar
sbund committed
// End: