\see \ref console_testserver for a complete example application
- \section intro_usage Access: Configuration files, Network console, ...
+ \section intro_usage Using the Console: Configuration files, Network console, ...
There are several ways to access the node tree:
\li By parsing configuration files
\see \ref node_tree
- \section intro_commands Registering console/config commands
+ \section intro_commands Implementing console/config commands
The console/config language does not define, how arguments are passed to the commands, it just
tokenizes the input and passes the tokens to the commands which then handle the
\code
// Create a console/config aware object and place it into the node tree
FooObject foo;
- senf::console::add("foo", foo.dir);
+ senf::console::root().add("foo", foo.dir);
// Open configuration file
senf::console::ConfigFile cf ("/etc/myserver.conf");
\li senf::console::DirectoryNode provides internal nodes with an arbitrary number of children
\li senf::console::CommandNode describes a command entry in the tree
+ \li senf::console::LinkNode is a link to another node. It works much like a symlink on POSIX
+ systems.
senf::console::CommandNode is the base-class of all command nodes of which there are several,
depending on the type of command.
\li A senf::console::CommandNode is normally added to the tree by directly adding a callback
using one of the overloaded senf::console::DirectoryNode::add() members. See \ref
console_commands.
+ \li A senf::console::LinkNode is created by senf::console::DirectoryNode::link()
When directly adding a node callback, the type of node added depends on the type of
callback. The callback types which can be added are listed at \ref console_callbacks.
"$(TOPDIR)/PPI/doc/PPI.tag" \
"$(TOPDIR)/Scheduler/doc/Scheduler.tag" \
"$(TOPDIR)/Packets/doc/Packets.tag" \
- "$(TOPDIR)/Packets/DefaultBundle/doc/DefaultBundle.tag" \
"$(TOPDIR)/Socket/doc/Socket.tag" \
"$(TOPDIR)/Utils/doc/Utils.tag" \
"$(TOPDIR)/senfscons/doc/senfscons.tag"
TAGFILES = \
"$(TOPDIR)/Scheduler/doc/Scheduler.tag" \
"$(TOPDIR)/Packets/doc/Packets.tag" \
- "$(TOPDIR)/Packets/DefaultBundle/doc/DefaultBundle.tag" \
"$(TOPDIR)/Socket/doc/Socket.tag" \
"$(TOPDIR)/Utils/doc/Utils.tag" \
"$(TOPDIR)/doc/overview.tag"
<table class="glossary">
+ <tr><td>collection parser</td> <td>libPackets</td> <td>A packet parser which allows to repeat
+ another parser or which uses some additional information to decide on the type or number of
+ sub-parsers.</td></tr>
+
<tr><td>complete policy</td> <td>libSocket</td> <td>socket policy where each axis is specified
completely</td></tr>
+ <tr><td>composite parser</td> <td>libPackets</td> <td>A packet parser providing access to named
+ sub-fields which are parsers themselves.</td></tr>
+
<tr><td>incomplete policy</td> <td>libSocket</td> <td>socket policy, where at least one axis is
not fully specified</td></tr>
+ <tr><td>packet interpreter</td> <td>libPackets</td> <td>Internal datastructure which represents
+ one protocol or header in the chain of interpreters representing a complete packet</td></tr>
+
+ <tr><td>packet parser</td> <td>libPackets</td> <td>lightweight class to convert between byte
+ representation and interpreted value. Derived from senf::PacketParserBase</td></tr>
+
<tr><td>policy axis</td> <td>libSocket</td> <td>one aspect defined in the socket policy, typedef
and member of the senf::SocketPolicy template</td></tr>
senf::ProtocolClientSocketHandle::protocol()/senf::ProtocolServerSocketHandle::protocol()
member</td></tr>
+ <tr><td>protocol parser</td> <td>libPackets</td> <td>A composite parser which defines a network
+ protocol.</td></tr>
+
<tr><td>socket policy</td> <td>libSocket</td> <td>another name for 'policy'</td></tr>
+ <tr><td>value parser</td> <td>libPackets</td> <td>A packet parser which parses a simple value
+ like a number or network address.</td></tr>
+
</table>
*/
// c-file-style: "senf"
// indent-tabs-mode: nil
// ispell-local-dictionary: "american"
-// compile-command: "scons -u test"
+// compile-command: "scons -u doc"
// mode: flyspell
// mode: auto-fill
// End:
@INCLUDE = "$(TOPDIR)/doclib/Doxyfile.global"
+INPUT = .
PROJECT_NAME = "HowTos"
@INCLUDE = "$(TOPDIR)/doclib/Doxyfile.global"
+INPUT = .
PROJECT_NAME = NewPacket
EXAMPLE_PATH = .
"$(TOPDIR)/PPI/doc/PPI.tag" \
"$(TOPDIR)/Scheduler/doc/Scheduler.tag" \
"$(TOPDIR)/Packets/doc/Packets.tag" \
- "$(TOPDIR)/Packets/DefaultBundle/doc/DefaultBundle.tag" \
"$(TOPDIR)/Socket/doc/Socket.tag" \
"$(TOPDIR)/Utils/doc/Utils.tag"
definition to generate a more usable interface:
\code
- SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
- (novalue(disable_checksum, senf::VoidPacketParser))
- ( id(checksum, GREPacketParser_OptFields)) );
+ SENF_PARSER_VARIANT ( optionalFields_, checksumPresent,
+ (novalue(disable_checksum, senf::VoidPacketParser))
+ ( id(checksum, GREPacketParser_OptFields)) );
\endcode
- Here, we changed to things:
- \li We made the variant private
- \li We added some optional information to the variants type list
+ Here, we added some optional information to the variants type list.
- With this information, \ref SENF_PARSER_PRIVATE_VARIANT() will create some additional \e public
- accessor members (those are public, only the variant itself is private). The members generated
- work like:
+ With this information, \ref SENF_PARSER_VARIANT() will create some additional \e public accessor
+ members and will automatically make the variant itself private. The members generated work like:
\code
void disable_checksum() const { optionalFields_().init<0>; }
+++ /dev/null
-@INCLUDE = "$(TOPDIR)/doclib/Doxyfile.global"
-
-PROJECT_NAME= DefaultBundle
-GENERATE_TAGFILE = doc/DefaultBundle.tag
-INPUT = .
-ALPHABETICAL_INDEX = NO
-
-TAGFILES = \
- "$(TOPDIR)/Packets/doc/Packets.tag" \
- "$(TOPDIR)/Socket/doc/Socket.tag" \
- "$(TOPDIR)/Utils/doc/Utils.tag"
validate(bytes(*this));
IpChecksum summer;
summer.feed( i(), i()+checksum_offset );
- // Not needed since the number of 0-bytes is even
- // summer.feed( 0u );
- // summer.feed( 0u );
summer.feed( i()+checksum_offset+2, i()+bytes(*this) );
return summer.sum();
}
value_type value() const { return value_type::from_data(i()); }
void value(value_type const & v) { std::copy(v.begin(), v.end(), i()); }
- operator value_type() { return value(); }
- byte & operator[](size_type index) { return *boost::next(i(),index); }
+ operator value_type() const { return value(); }
+ byte & operator[](size_type index) const { return *boost::next(i(),index); }
INet4AddressParser const & operator= (value_type const & other)
{ value(other); return *this; }
};
value_type value() const { return value_type::from_data(i()); }
void value(value_type const & v) { std::copy(v.begin(), v.end(), i()); }
- operator value_type() { return value(); }
- byte & operator[](size_type index) { return *boost::next(i(),index); }
+ operator value_type() const { return value(); }
+ byte & operator[](size_type index) const { return *boost::next(i(),index); }
INet6AddressParser const & operator= (value_type const & other)
{ value(other); return *this; }
};
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-/** \mainpage The Default Bundle
-
- The default bundle combines a set of basic low level protocols like Ethernet or IP. Find the
- list of protocols implemented at \ref protocolbundle_default
- */
-
-/** \defgroup protocolbundle_default Protocols contained in the DefaultBundle
+/** \defgroup protocolbundle_default The DefaultBundle
Each protocol consists of several objects
no_includes = 1)
SConscript(glob.glob("*/SConscript"))
-
-SENFSCons.Doxygen(env)
PROJECT_NAME = libPackets
GENERATE_TAGFILE = doc/Packets.tag
INPUT = .
+RECURSIVE = YES
EXAMPLE_PATH = . DefaultBundle
TAGFILES = \
Here \c EltParser can be an arbitrary parser and need not have a fixed size.
+ \warning Realize, that the \a size field is controlled by the list parser. This field
+ should therefore be declared either read-only or private and must be changed only via
+ the list parser.
+
Further additional tags are supported which modify the type of list created:
<table class="senf fixedcolumn">
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief DTCPPacket non-inline non-template implementation */
+//#include "DTCPPacket.hh"
+//#include "DTCPPacket.ih"
+
+// Custom includes
#include "DTCPPacket.hh"
#include <boost/io/ios_state.hpp>
#include <iomanip>
#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
-prefix_ void senf::DTCPPacketType::dump(packet p, std::ostream & os)
+prefix_ void senf::DTCPHelloPacketType::dump(packet p, std::ostream & os)
{
+ static char const * COMMANDS[] = { "0", "JOIN", "LEAVE", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15" };
boost::io::ios_all_saver ias(os);
- os << "DTCPPacket" << std::endl
- << " version : " << p->version_number() << std::endl
- << " command : " << p->command() << std::endl
- << " interval : " << p->interval() << std::endl
- << " sequence_number : " << p->sequence_number() << std::endl
- << " receive_capable_feed : " << p->receive_capable_feed() << std::endl
- << " ip_version : " << p->ip_version() << std::endl
- << " tunnel_protocol : " << p->tunnel_protocol() << std::endl
- ;
-
- //TODO: print included IPs
+ os << "DTCP HELLO Packet:" << std::endl
+ << " version : " << p->versionNumber() << std::endl
+ << " command : " << COMMANDS[p->command()] << std::endl
+ << " interval : " << unsigned(p->interval()) << std::endl
+ << " sequence number : " << p->sequenceNumber() << std::endl
+ << " receive capable feed : " << (p->receiveCapableFeed() ? "yes" : "no") << std::endl
+ << " ip version : " << p->ipVersion() << std::endl
+ << " tunnel protocol : " << unsigned(p->tunnelProtocol()) << std::endl
+ << " number of BDL ips : " << unsigned(p->fbipCount()) << std::endl
+ << " feed BDL ips : ";
+
+ switch (p->ipVersion()) {
+ case 4 : {
+ typedef DTCPHelloPacket::Parser::v4fbipList_t FBIPList;
+ FBIPList::container fbips (p->v4fbipList());
+ FBIPList::container::iterator i (fbips.begin());
+ FBIPList::container::iterator const i_end (fbips.end());
+ for (; i != i_end; ++i)
+ os << "\n " << *i;
+ break;
+ }
+ case 6 : {
+ typedef DTCPHelloPacket::Parser::v6fbipList_t FBIPList;
+ FBIPList::container fbips (p->v6fbipList());
+ FBIPList::container::iterator i (fbips.begin());
+ FBIPList::container::iterator const i_end (fbips.end());
+ for (; i != i_end; ++i)
+ os << "\n " << *i;
+ break;
+ }
+ default:
+ os << "unknown ip version";
+ }
+
+ os << std::endl;
}
#undef prefix_
+
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/** \file
+ \brief DTCPPacket public header */
-#ifndef DTCPPACKET_HH_
-#define DTCPPACKET_HH_
+#ifndef HH_DTCPPacket_
+#define HH_DTCPPacket_ 1
+// Custom includes
#include "../../Packets/Packets.hh"
#include "../../Packets/DefaultBundle/IPv4Packet.hh"
#include "../../Packets/DefaultBundle/IPv6Packet.hh"
-#define DTCP_V4_MCADDRESS "224.0.0.36"
-#define DTCP_V6_MCADDRESS "FF02:0:0:0:0:0:1:4"
-#define DTCP_UDP_PORT 652
+//#include "DTCPPacket.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
-
- //first we have to define some helpers
- struct DTCPIPv4AddressListParser : public PacketParserBase {
+
+# define DTCP_V4_MCADDRESS "224.0.0.36"
+# define DTCP_V6_MCADDRESS "FF02:0:0:0:0:0:1:4"
+# define DTCP_UDP_PORT 652
+
+ struct DTCPIPv4AddressListParser : public PacketParserBase
+ {
# include SENF_PARSER()
- SENF_PARSER_PRIVATE_FIELD ( num_of_fbips, UInt8Parser );
- SENF_PARSER_PRIVATE_FIELD ( reserved , UInt8Parser ); //must be zero
- SENF_PARSER_VECTOR ( fbiplist, num_of_fbips, INet4AddressParser );
+
+ SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser );
+ SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser );
+
+ SENF_PARSER_VECTOR( fbips, fbipCount_, INet4AddressParser );
+
+ // Needed since we do NOT want to init fbipCount_ or reseverd_. And since
+ // INet4AddressParser::init() is a no-op, we can just as well disable init completely
+ SENF_PARSER_INIT() {}
SENF_PARSER_FINALIZE(DTCPIPv4AddressListParser);
};
- struct DTCPIPv6AddressListParser : public PacketParserBase {
+ struct DTCPIPv6AddressListParser : public PacketParserBase
+ {
# include SENF_PARSER()
- SENF_PARSER_PRIVATE_FIELD ( num_of_fbips, UInt8Parser );
- SENF_PARSER_PRIVATE_FIELD ( reserved, UInt8Parser ); //must be zero
- SENF_PARSER_VECTOR ( fbiplist, num_of_fbips, INet6AddressParser );
+
+ SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser );
+ SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser );
+
+ SENF_PARSER_VECTOR( fbips, fbipCount_, INet6AddressParser );
+
+ // Needed since we do NOT want to init fbipCount_ or reseverd_. And since
+ // INet4AddressParser::init() is a no-op, we can just as well disable init completely
+ SENF_PARSER_INIT() {}
SENF_PARSER_FINALIZE(DTCPIPv6AddressListParser);
};
- /** \brief Parse a DTCP packet
+ /** \brief Parse a DTCP HELLO packet
Parser implementing the DTCP packet according to RFC 3077
\see DTCPPacketType
*/
- struct DTCPPacketParser : public PacketParserBase
+ struct DTCPHelloPacketParser : public PacketParserBase
{
# include SENF_PARSER()
- SENF_PARSER_BITFIELD ( version_number, 4, unsigned ); // =1 according to rfc3077
- SENF_PARSER_BITFIELD ( command, 4, unsigned ); // 1=JOIN 2=LEAVE
- SENF_PARSER_FIELD ( interval, UInt8Parser ); // 5 according to rfc3077
- SENF_PARSER_FIELD ( sequence_number, UInt16Parser );
- SENF_PARSER_PRIVATE_BITFIELD ( reserved, 3, unsigned );
- SENF_PARSER_BITFIELD ( receive_capable_feed, 1, bool ); // 0=send only, 1=receive_capable_feed
- SENF_PARSER_BITFIELD_RO ( ip_version, 4, unsigned ); // 4=IPv4, 6=IPv6
- SENF_PARSER_FIELD ( tunnel_protocol, UInt8Parser );
- /* Please consider the following comments on the implementation given in this class:
- * 1. you could think of simply using SENF_PARSER_PRIVATE_VARIANT and List / Vectorparser like this:
- * SENF_PARSER_PRIVATE_VARIANT ( fbiplist, ip_version,
- * (senf::VoidPacketParser) //ip_version=0
- * (senf::VoidPacketParser) //1
- * (senf::VoidPacketParser) //2
- * (senf::VoidPacketParser) //3
- * (senf::ListBParser< IPv4Packet, num_of_fbips>) //4
- * (senf::VoidPacketParser) //5
- * (senf::ListBParser< IPv6Packet, num_of_fbips>) ); //6
- * This can't work for two reasons:
- * -SENF_PARSER_PRIVATE_VARIANT only accepts 6 templates in types but you have to start from 0.
- * -you NEVER can use templated Parsers in these macros since the macro-preprocessor won't recognize the <> brackets and will
- * interpret the ","
- *
- * The first problem is solved by using key()
- * The second problem is solved by introducing Helper-Parser which cover both the list and the number field. By that no
- * templates have to be used.
- */
-
- SENF_PARSER_VARIANT( fbiplist, ip_version,
- ( ids(getIpv4AddressList, na, setIpVersion4,
- key(4, senf::DTCPIPv4AddressListParser)) ) //IPv4
- ( ids(getIpv6AddressList, na, setIpVersion6,
- key(6, senf::DTCPIPv6AddressListParser)) ) ); //IPv6
+ SENF_PARSER_BITFIELD ( versionNumber, 4, unsigned ); // must be 1
+ SENF_PARSER_BITFIELD ( command, 4, unsigned );
+
+ enum Command { JOIN=1, LEAVE=2 };
+
+ SENF_PARSER_FIELD ( interval, UInt8Parser ); // should be 5
+ SENF_PARSER_FIELD ( sequenceNumber, UInt16Parser );
+
+ SENF_PARSER_PRIVATE_BITFIELD ( reserved0_, 3, unsigned );
+ SENF_PARSER_BITFIELD ( receiveCapableFeed, 1, bool );
+ SENF_PARSER_BITFIELD_RO ( ipVersion, 4, unsigned ); // 4=IPv4, 6=IPv6
+
+ SENF_PARSER_FIELD ( tunnelProtocol, UInt8Parser );
+ SENF_PARSER_FIELD_RO ( fbipCount, UInt8Parser );
+ SENF_PARSER_PRIVATE_FIELD ( reserved1_, UInt8Parser ); //must be zero
+
+ // Go back to fbipCount so the variant has access to that field
+ SENF_PARSER_GOTO( fbipCount );
+
+ SENF_PARSER_VARIANT ( fbipList_, ipVersion,
+ ( ids(na, has_v4fbipList, init_v4fbipList,
+ key(4, DTCPIPv4AddressListParser)) )
+ ( ids(na, has_v6fbipList, init_v6fbipList,
+ key(6, DTCPIPv6AddressListParser)) ) );
+
+ // We define the two variant accessors ourselves so we can directly return the vector and
+ // not the collection parser which contains the vector ...
+
+ typedef DTCPIPv4AddressListParser::fbips_t v4fbipList_t;
+ v4fbipList_t v4fbipList() { return fbipList_().get<0>().fbips(); }
+
+ typedef DTCPIPv6AddressListParser::fbips_t v6fbipList_t;
+ v6fbipList_t v6fbipList() { return fbipList_().get<1>().fbips(); }
- SENF_PARSER_FINALIZE(DTCPPacketParser);
+ SENF_PARSER_FINALIZE(DTCPHelloPacketParser);
};
- /** \brief DTCP packet
+ /** \brief DTCP HELLO packet
\par Packet type (typedef):
- \ref DTCPPacket
+ \ref DTCPHelloPacket
\par Fields:
- \ref DTCPPacketParser
+ \ref DTCPHelloPacketParser
\ingroup protocolbundle_mpegdvb
*/
- struct DTCPPacketType
+ struct DTCPHelloPacketType
: public PacketTypeBase,
- public PacketTypeMixin<DTCPPacketType>
+ public PacketTypeMixin<DTCPHelloPacketType>
{
- typedef PacketTypeMixin<DTCPPacketType> mixin;
- typedef ConcretePacket<DTCPPacketType> packet;
- typedef DTCPPacketParser parser;
+ typedef PacketTypeMixin<DTCPHelloPacketType> mixin;
+ typedef ConcretePacket<DTCPHelloPacketType> packet;
+ typedef DTCPHelloPacketParser parser;
using mixin::nextPacketRange;
using mixin::init;
};
/** \brief DTCP packet typedef */
- typedef DTCPPacketType::packet DTCPPacket;
+ typedef DTCPHelloPacketType::packet DTCPHelloPacket;
}
-#endif /*DTCPPACKET_HH_*/
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "DTCPPacket.cci"
+//#include "DTCPPacket.ct"
+//#include "DTCPPacket.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
--- /dev/null
+// $Id$
+//
+// Copyright (C) 2008
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+// Stefan Bund <g0dil@berlios.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 DTCPPacket.test unit tests */
+
+//#include "DTCPPacket.test.hh"
+//#include "DTCPPacket.test.ih"
+
+// Custom includes
+#include "DTCPPacket.hh"
+
+#include "../../Utils/auto_unit_test.hh"
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+BOOST_AUTO_UNIT_TEST(dtcpPacket)
+{
+ unsigned char data[] = { 0x11, // versionNumber = 1, command = JOIN
+ 5, // interval
+ 0x0A, 0x0B, // sequence number
+ 0x14, // receiveCapable = true, ipVersion = 4
+ 23, // tunnelProtocol
+ 2, // fbipCount
+ 0x00,
+ 101, 102, 103, 104, // fbip1
+ 201, 202, 203, 204 }; // fbip2
+
+ senf::DTCPHelloPacket hello (senf::DTCPHelloPacket::create(
+ boost::make_iterator_range(data, data+sizeof(data))));
+
+ BOOST_CHECK_EQUAL_COLLECTIONS( data, data+sizeof(data),
+ hello.data().begin(), hello.data().end() );
+
+ BOOST_CHECK_EQUAL( senf::bytes(hello.parser()), 16u );
+
+ BOOST_CHECK_EQUAL( hello->versionNumber(), 1u );
+ BOOST_CHECK_EQUAL( hello->command(), unsigned(senf::DTCPHelloPacket::Parser::JOIN) );
+ BOOST_CHECK_EQUAL( hello->interval(), 5u );
+ BOOST_CHECK_EQUAL( hello->sequenceNumber(), 0x0A0Bu );
+ BOOST_CHECK_EQUAL( hello->receiveCapableFeed(), true );
+ BOOST_CHECK_EQUAL( hello->ipVersion(), 4u );
+ BOOST_CHECK_EQUAL( hello->tunnelProtocol(), 23u );
+ BOOST_CHECK_EQUAL( hello->fbipCount(), 2u );
+ BOOST_CHECK( hello->has_v4fbipList() );
+ BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(hello->v4fbipList()[0]), "101.102.103.104" );
+ BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(hello->v4fbipList()[1]), "201.202.203.204" );
+
+ std::stringstream ss;
+ hello.dump(ss);
+ BOOST_CHECK_EQUAL( ss.str(),
+ "DTCP HELLO Packet:\n"
+ " version : 1\n"
+ " command : JOIN\n"
+ " interval : 5\n"
+ " sequence number : 2571\n"
+ " receive capable feed : yes\n"
+ " ip version : 4\n"
+ " tunnel protocol : 23\n"
+ " number of BDL ips : 2\n"
+ " feed BDL ips : \n"
+ " 101.102.103.104\n"
+ " 201.202.203.204\n" );
+
+ senf::DTCPHelloPacket hello2 (senf::DTCPHelloPacket::create());
+
+ SENF_CHECK_NO_THROW( hello2->versionNumber() = 1u );
+ SENF_CHECK_NO_THROW( hello2->command() = senf::DTCPHelloPacket::Parser::JOIN );
+ SENF_CHECK_NO_THROW( hello2->interval() = 5u );
+ SENF_CHECK_NO_THROW( hello2->sequenceNumber() = 0x0A0B );
+ SENF_CHECK_NO_THROW( hello2->receiveCapableFeed() = true );
+ SENF_CHECK_NO_THROW( hello2->tunnelProtocol() = 23u );
+ SENF_CHECK_NO_THROW( hello2->init_v4fbipList() );
+ SENF_CHECK_NO_THROW( hello2->v4fbipList().push_back( senf::INet4Address(0x65666768u) ) );
+ SENF_CHECK_NO_THROW( hello2->v4fbipList().push_back( senf::INet4Address(0xC9CACBCCu) ) );
+
+ BOOST_CHECK_EQUAL( senf::bytes(hello2.parser()), 16u );
+ BOOST_CHECK_EQUAL_COLLECTIONS( data, data+sizeof(data),
+ hello2.data().begin(), hello2.data().end() );
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
+++ /dev/null
-@INCLUDE = "$(TOPDIR)/doclib/Doxyfile.global"
-
-PROJECT_NAME = MPEGDVBBundle
-GENERATE_TAGFILE = doc/MPEGDVBBundle.tag
-INPUT = .
-ALPHABETICAL_INDEX = NO
-
-TAGFILES = \
- "$(TOPDIR)/Packets/doc/Packets.tag" \
- "$(TOPDIR)/Packets/DefaultBundle/doc/DefaultBundle.tag" \
- "$(TOPDIR)/Socket/doc/Socket.tag" \
- "$(TOPDIR)/Utils/doc/Utils.tag"
-
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-/** \mainpage The MPEG/DVB Bundle
-
- \ref protocolbundle_mpegdvb
- */
-
-/** \defgroup protocolbundle_mpegdvb Protocols of the MPEG/DVB bundle
+/** \defgroup protocolbundle_mpegdvb The MPEG/DVB bundle
Each protocol consists of several objects
library = 'Packets_MPEGDVBBundle',
sources = sources[0],
no_includes = 1)
-
-SENFSCons.Doxygen(env, extra_sources = [
- env.Dia2Png("TLV.dia")
-])
/** \mainpage The SENF Packet Library
- \section arch Overall Architecture
+ The SENF Packet library provides facilities to analyze, manipulate and create structured packet
+ oriented data (e.g. network packets).
+
+ \autotoc
+
+
+ \section packet_intro_arch Overall Architecture
The Packet library consists of several components:
All these components work together to provide a hopefully simple and intuitive interface to
packet parsing and creation.
- \section intro Introduction
+ \see \ref packet_arch
+
+
+ \section packet_intro_usage Using the packet library
+
+ This chapter discusses the usage of the packet library from a high level view.
+
+ \see \ref packet_usage
+
+
+ \section packet_intro_parser Parsing packet data
+
+ This chapter goes into more detail discussing the usage of packet parsers.
+
+ \li categorizing packet parsers
+ \li reading and writing values
+ \li using complex parsers
+
+ \see \ref packetparser
+
+
+ \section protocolbundles Supported packet types (protocols)
+
+ Each protocol bundle provides a collection of related concrete packet classes for a group of
+ related protocols:
+
+ \li \ref protocolbundle_default : Some basic default protocols: Ethernet, Ip, TCP, UDP
+ \li \ref protocolbundle_mpegdvb : MPEG and DVB protocols
+
+ There are two ways to link with a bundle
+
+ \li If you only work with known packets which you explicitly reference you may just link with
+ the corresponding library.
+ \li If you need to parse unknown packets and want those to be parsed as complete as possible
+ without explicitly referencing the packet type, you will need to link against the combined
+ object file built for every bundle. This way, all packets defined in the bundle will be
+ included whether they are explicitly referenced or not (and they will all automatically be
+ registered).
+
+
+ \section packet_intro_new Defining new packet types
+
+ The packet library provides the framework which allows to define arbitrary packet types. There
+ is quite some information needed to completely specify a specific type of paceket.
+
+ \see \ref packet_new
+ */
+
+/** \page packet_arch Overall Packet library Architecture
+
+ The packet library handles network packets of a large number of protocols. We work with a packet
+ on three levels
+
+ \autotoc
+
+
+ \section packet_arch_handle The Packet handle
+
+ Whenever we are using a Packet, we are talking about a senf::Packet (or a
+ senf::ConcretePacket). This class is a \e handle referencing an internally managed packet data
+ structure. So even though we pass senf::Packet instances around by value, they work like
+ references. The packet library automatically manages all required memory resources using
+ reference counting.
+
+ Different Packet handles may really internally share one Packet data structure if they both
+ point to the same packet.
+
+
+ \section packet_arch_data The Packet as a 'bunch of bytes'
+
+ From the outside, a packet is just a bunch of bytes just as it is read from (or will be
+ written to) the wire. At this low-level view, we can access the data in it's raw form but
+ have no further information about what kind of packet we have.
+
+ The packet library provides a consistent container interface for this representation.
+
+ \code
+ Packet p = ...;
+
+ // Change first byte of packet to 0
+ p.data()[0] = 1u;
+
+ // Copy packet data into a vector
+ std::vector<char> data (p.data().size());
+ std::copy(p.data().begin(), p.data().end(), data.begin());
+ \endcode
+
+ This type of access is primarily needed when reading or writing packets (e.g. to/from the
+ network).
+
+ \see senf::Packet::data() \n
+ senf::PacketData
+
+
+ \section packet_arch_chain The Interpreter Chain
+
+ On the next level, the packet is divided into a nested list of sub-packets (or headers) called
+ interpreters. Each senf::Packet handle internally points to an interpreter or header. This
+ allows us to access one and the same packet in different ways.
+
+ Consider an Ethernet Packet with an IP payload holding a UDP packet. We may reference either the
+ Ethernet packet as a whole or we may reference the IP or UDP interpreters (sub-packets or
+ headers). All handles really refer to the \e same data structure but provide access to a
+ different (sub-)range of the data in the packet.
+
+ We can navigate around this chained structure using appropriate members:
+
+ \code
+ // eth, ip and udp all reference the same internal packet data albeit at different data ranges
+ Packet eth = ...;
+ Packet ip = eth.next();
+ Packet udp = ip.next();
+
+ eth.next() == ip // true
+ eth.next().is<IPv4Packet>() // true
+ eth.next().next() == udp // true
+ eth.next().is<UDPPacket>() // false
+ eth.find<UDPPacket>() == udp // true
+
+ udp.find<EthernetPacket>() // throws InvalidPacketChainException
+ udp.find<EthernetPacket>(senf::nothrow) // An in-valid() senf::Packet which tests as 'false'
+ udp.find<UDPPacket> == udp // true
+ udp.first<IPv4Packet>() // throws InvalidPacketChainException
+
+ udp.prev() == ip // true
+ udp.prev<EthernetPacket>() // throws Inv
+ \endcode
+
+ \see \ref packet_module
+
+
+ \section packet_arch_parser Parsing specific Protocols
- Whenever using the library, you will probably need to \c \#include it's header:
+ On the next level, the packet library allows us to parse the individual protocols. This gives us
+ access to the protocol specific data members of a packet and allows us to access or manipulate a
+ packet in a protocol specific way.
+
+ To access this information, we need to use a protocol specific handle, the senf::ConcretePacket
+ which takes as a template argument the specific type of packet to be interpreted. This allows us
+ to easily interpret or create packets. Here an example on how to create a new Etheret / IP / UDP
+ / Payload packet interpreter chain:
\code
- #include "Packets/Packets.hh"
+ // EthernetPacket, IPv4Packet, UDPPacket and DataPacket are typedefs for corresponding
+ // ConcretePacket instantiations
+ senf::EthernetPacket eth (senf::EthernetPacket::create());
+ senf::IPv4Packet ip (senf::IPv4Packet ::createAfter(eth));
+ senf::UDPPacket udp (senf::UDPPacket ::createAfter(ip));
+ senf::DataPacket payload (senf::DataPacket ::createAfter(udp,
+ std::string("Hello, world!")));
+
+ udp->source() = 2000u;
+ udp->destination() = 2001u;
+ ip->ttl() = 255u;
+ ip->source() = senf::INet4Address::from_string("192.168.0.1");
+ ip->destination() = senf::INet4Address::from_string("192.168.0.2");
+ eth->source() = senf::MACAddress::from_string("00:11:22:33:44:55");
+ eth->destination() = senf::MACAddress::from_string("00:11:22:33:44:66");
+
+ eth.finalize();
\endcode
- \warning Never include any other Packets library header directly, always include \c
- Packets/Packets.hh.
+ Again, realize, that \a eth, \a ip, \a udp and \a payload share the same internal packet
+ data structure (the respective \c data() members all provide access to the same underlying
+ container however at different byte ranges): The complete packet can be accessed at
+ <tt>eth.data()</tt> whereas <tt>payload.data()</tt> only holds UDP payload (in this case the
+ string "Hello, world!").
+
+ \see \ref packetparser \n
+ \ref protocolbundles
+ */
+
+/** \page packet_usage Using the packet library
+
+ \autotoc
+
+ \section packet_usage_intro Includes
- Additionally you will have to include the header files for the packet types you use, e.g. \c
- Packets/DefaultBundle/EthernetPacket.hh etc.
+ To use the library, you need to include the appropriate header files. This will probably happen
+ automatically when including the specific protocol headers. If needed, you may explicitly use
+
+ \code
+ #include "Packets.hh"
+ \endcode
+ explicitly.
+
+ \warning Never include any other Packets library header directly, only include \c
+ Packets.hh or one (or several) protocol headers from the protocol bundles.
+
Most every use of the packet library starts with some concrete packet typedef. Some fundamental
- packet types are provided by \ref protocolbundle_default. Building on those packet types, this
- example will build a complex packet: This will be an Ethernet packet containing an IPv4 UDP
- packet. We begin by building the raw packet skeleton:
+ packet types are provided by \ref protocolbundle_default.
+
+
+ \section packet_usage_create Creating a new packet
+
+ Building on those packet types, this example will build a complex packet: This will be an
+ Ethernet packet containing an IPv4 UDP packet. We begin by building the raw packet skeleton:
\code
+ #include "Packets/DefaultBundle/EthernetPacket.hh"
+ #include "Packets/DefaultBundle/IPv4Packet.hh"
+ #include "Packets/DefaultBundle/UDPPacket.hh"
+
senf::EthernetPacket eth (senf::EthernetPacket::create());
senf::IPv4Packet ip (senf::IPv4Packet ::createAfter(eth));
senf::UDPPacket udp (senf::UDPPacket ::createAfter(ip));
structure. This data structure will be freed when the last reference to it goes out of
scope.
- The packet created above already has the correct payload however all protocol fields are
- empty. We need to set those protocol fields:
+ The packet created above already has the correct UDP payload (The string "Hello, world!")
+ however all protocol fields are empty. We need to set those protocol fields:
\code
udp->source() = 2000u;
As seen above, packet fields are accessed using the <tt>-></tt> operator whereas other packet
facilities (like \c finalize()) are directly accessed using the member operator. The field
- values are simple set using appropriately named accessors. As a last step, the \c finalize()
+ values are simply set using appropriately named accessors. As a last step, the \c finalize()
call will update all calculated fields (fields like next-protocol, header or payload length,
checksums etc). Now the packet is ready. We may now send it out using a packet socket
sock.write(eth.data());
\endcode
- The packet library also provides lot's of facilities to navigate the packet chain:
-
- \code
- eth.next() == ip; // true
- eth.next().is<IPv4Packet>(); // true
- eth.next().next() == udp; // true
- eth.next().is<UDPPacket>(); // false
- eth.find<UDPPacket>() == udp; // true
-
- udp.find<EthernetPacket>(); // throws InvalidPacketChainException
- udp.find<EthernetPacket>(senf::nothrow); // An in-valid() senf::Packet which tests as 'false'
- udp.find<UDPPacket()> == udp; // true
- udp.first<IPv4Packet>(); // throws InvalidPacketChainException
-
- udp.prev() == ip; // true
- udp.prev<EthernetPacket>(); // throws Inv
- \endcode
- ... and so on. See the senf::Packet documentation for more. Using these members, the complete
- chain of packet interpreters (as these sub-packets or headers are called) may be traversed from
- any packet handle.
+ \section packet_usage_read Reading and parsing packets
- These chain navigation functions are also used to parse a packet. Let's read an Ethernet packet
+ The chain navigation functions are also used to parse a packet. Let's read an Ethernet packet
from a packet socket handle:
\code
packet). More generally, whenever a field cannot be accessed because it would be out of bounds
of the data read, this exception is generated.
- This is only a very short introduction to the library to give a feel for the implementation. For
- a detailed discussion see the respective reference documentation.
+
+ \section packet_usage_container The raw data container
+
+ Every packet is based internally on a raw data container holding the packet data. This container
+ is accessed via senf::Packet::data() member.
+
+ This container is a random access container. It can be used like an ordinary STL container and
+ supports all the standard container members.
+
+ \code
+ Packet p = ...;
+
+ // Insert 5 0x01 bytes
+ p.data().insert(p.data().begin()+5, 5, 0x01);
+
+ // Insert data from another container
+ p.data().insert(p.data().end(), other.begin(), other.end());
+
+ // Erase a single byte
+ p.data().erase(p.data().begin()+5);
+
+ // XOR byte 5 with 0xAA
+ p.data()[5] ^= 0xAA;
+ \endcode
+
+ A packet consists of a list of interpreters (packet headers or protocols) which all reference
+ the same data container at different byte ranges. Each packet consists of the protocol header \e
+ plus the packets payload. This means, that the data container ranges of successive packets from
+ a single interpreter chain are nested.
+
+ Example: The packet created above (the Ethernet-IP-UDP packet with payload "Hello, world!") has
+ 4 Interpreters: Ethernet, IPv4, UDP and the UDP payload data. The nested data containers lead to
+ the following structure
+
+ \code
+ // The ethernet header has a size of 14 bytes
+ eth.data().begin() + 14 == ip.data().begin()
+ eth.data().end() == ip.data().end()
+
+ // The IP header has a size of 20 bytes and therefore
+ ip.data().begin() + 20 == udp.data().begin()
+ ip.data().end() == udp.data().end()
+
+ // The UDP header has a size of 8 bytes and thus
+ udp.data().begin() + 8 == payload.data().begin()
+ udp.data().end() == payload.data().end()
+ \endcode
+
+ This nesting will (and must) always hold: The data range of a subsequent packet will always be
+ within the range of it's preceding packet.
+
+ \warning It is forbidden to change the data of a subsequent packet interpreter from the
+ preceding packet even if the data container includes this data. If you do so, you may
+ corrupt the data structure (especially when changing it's size).
+
+ Every operation on a packet is considered to be \e within this packet and \e without and
+ following packet. When inserting or erasing data, the data ranges are all adjusted
+ accordingly. So the following are \e not the same even though \c eth.end(), \c ip.end() and \c
+ udp.end() are identical.
+
+ \code
+ eth.data().insert(eth.data().end(), 5, 0x01);
+ assert( eth.data().end() == ip.data().end() + 5
+ && ip.data().end() == udp.data().end() );
+
+ // Or alternatively: (You could even use eth.data().end() here ... it's the same)
+ ip.data().insert(ip.data().end(), 5, 0x01);
+ assert( eth.data().end() == ip.data().end()
+ && ip.data().end() == udp.data().end() + 5 );
+ \endcode
+
+ \warning When accessing the packet data via the container interface, you may easily build
+ invalid packets since the packet will not be validated against it's protocol.
+
+
+ \section packet_usage_fields Protocol fields
+
+ When working with concrete protocols, the packet library provides direct access to all the
+ protocol information.
+
+ \code
+ udp->source() = 2000u;
+ udp->destination() = 2001u;
+ ip->ttl() = 255u;
+ ip->source() = senf::INet4Address::from_string("192.168.0.1");
+ ip->destination() = senf::INet4Address::from_string("192.168.0.2");
+ eth->source() = senf::MACAddress::from_string("00:11:22:33:44:55");
+ eth->destination() = senf::MACAddress::from_string("00:11:22:33:44:66");
+ \endcode
+
+ The protocol field members above do \e not return references, they return parser instances.
+ Protocol fields are accessed via parsers. A parser is a very lightweight class which points into
+ the raw packet data and converts between raw data bytes and it's interpreted value: For example
+ a senf::UInt16Parser accesses 2 bytes (in network byte order) and converts them to or from a 16
+ bit integer. There are a few properties about parsers which need to be understood:
+
+ \li Parsers are created only temporarily when needed. They are created when accessing a protocol
+ field and are returned by value.
+
+ \li A parser never contains a value itself, it just references a packets data container.
+
+ \li Parsers can be built using other parsers and may have members which return further parsers.
+
+ The top-level interface to a packets protocol fields is provided by a protocol parser. This
+ protocol parser is a composite parser which has members to access the protocol fields (compare
+ with the example code above). Some protocol fields may be more complex than a simple value. In
+ this case, those accessors may return other composite parsers or collection parsers. Ultimately,
+ a value parser will be returned.
+
+ The simple value parsers which return plain values (integer numbers, network addresses etc) can
+ be used like those values and can also be assigned corresponding values. More complex parsers
+ don't allow simple assignment. However, they can always be copied from another parser <em>of the
+ same type</em> using the generalized parser assignment. This type of assignment also works for
+ simple parsers and is then identical to a normal assignment.
+
+ \code
+ // Copy the complete udp parser from udp packet 2 to packet 1
+ udp1.parser() << udp2.parser();
+ \endcode
+
+ Additionally, the parsers have a parser specific API which allows to manipulate or query the
+ value.
+
+ This is a very abstract description of the parser structure. For a more concrete description, we
+ need to differentiate between the different parser types
+
+ \subsection packet_usage_fields_value Value parsers
+
+ We have already seen value parsers: These are the lowest level building blocks witch parse
+ numbers, addresses etc. They return some type of value and can be assigned such a value. More
+ formally, they have a \c value_type typedef member which gives the type of value they accept and
+ they have an overloaded \c value() member which is used to read or set the value. Some parsers
+ have additional functionality: The numeric parser for Example provide conversion and arithmetic
+ operators so they can be used like a numeric value.
+
+ If you have a value parser \c valueParser with type \c ValueParser, the following will always be
+ valid:
+ \code
+ // You can read the value and assign it to a variable of the corresponding value_type
+ ValueParser::value_type v (valueParser.value());
+
+ // You can assign that value to the parser
+ valueParser.value(v);
+
+ // The assignment can also be done using the generic parser assignment
+ valueParser << v;
+ \endcode
+
+
+ \subsection packet_usage_fields_composite Composite and protocol parsers
+
+ A composite parser is a parser which just combines several other parsers into a structure: For
+ example, the senf::EthernetPacketParser has members \c destination(), \c source() and \c
+ type_length(). Those members return parsers again (in this case value parsers) to access the
+ protocol fields.
+
+ Composite parsers can be nested; A composite parser may be returned by another composite
+ parser. The protocol parser is a composite parser which defines the field for a specific
+ protocol header like Ethernet.
+
+ \subsection packet_usage_fields_collection Collection parsers
+
+ Besides simple composites, the packet library has support for more complex collections.
+
+ \li The senf::ArrayParser allows to repeat an arbitrary parser a fixed number of times.
+ \li senf::VectorParser and senf::ListParser are two different types of lists with variable
+ number of elements
+ \li The senf::VariantParser is a discriminated union: It will select one of several parsers
+ depending on the value of a discriminant.
+
+
+ \subsubsection packet_usage_collection_vector Vector and List Parsers
+
+ Remember, that a parser does \e not contain any data: It only points into the raw data
+ container. This is also true for the collection parsers. VectorParser and ListParser provide an
+ interface which looks like an STL container to access the elements.
+
+ We will use an MLDv2 Query as an example (see <a
+ href="http://tools.ietf.org/html/rfc3810#section-5">RFC 3810</a>).
+
+ \code
+ MLDv2QueryPacket mld = ...;
+
+ // Instantiate a collection wrapper for the source list
+ MLDv2QueryPacket::Parser::sources_t::container sources (mld->sources());
+
+ // Iterate over all the addresses in that list
+ for (MLDv2QueryPacket::Parser::sources_t::container::iterator i (sources.begin());
+ i != sources.end(); ++i)
+ std::cout << *i << std::endl;
+ \endcode
+
+ Beside other fields, the MLDv2Query consists of a list of source addresses. The \c sources()
+ member returns a VectorParser for these addresses. The collection parsers can only be accessed
+ completely using a container wrapper. This is, what we do in above example.
+
+ The wrapper can also be used to manipulate that list. Here we copy a list of addresses from an
+ \c std::vector into the packet:
+
+ \code
+ std::vector<senf::INet6Address> addrs (...);
+
+ sources.resize(addrs.size());
+ std::copy(addrs.begin(), addrs.end(), sources.begin())
+ \endcode
+
+ Collection parsers may also be nested. To access a nested collection parser, such a container
+ wrapper should be allocated for each level. An MLD Report (which is a composite parser) includes
+ a list of multicast address records called \c records(). Each record is again a composite which
+ contains a list of sources called \c sources():
+
+ \code
+ MLDv2ReportPacket report = ...;
+
+ // Instantiate a collection wrapper for the list of records:
+ MLDv2ReportPacket::Parser::records_t::container records (report->records());
+
+ // Iterate over the multicast address records
+ for (MLDv2ReportPacket::Parser::records_t::container::iterator i (records.begin());
+ i != records.end(); ++i) {
+ // Allocate a collection wrapper for the multicast address record
+ typedef MLDv2ReportPackte::Parser::records_t::value_type::sources_t Sources;
+ Sources::container sources (i->sources());
+
+ // Iterate over the sources in this record
+ for (Sources::container::iterator i (sources.begin());
+ i != sources.end(); ++i)
+ std::cout << *i << std::endl;
+ }
+ \endcode
+
+ In this example we also see how to find the type of a parser or container wrapper.
+ \li Composite parsers have typedefs for each their fields with a \c _t postfix
+ \li The vector or list parsers have a \c value_type typedef which gives the type of the
+ element.
+
+ By traversing this hierarchical structure, the types of all the fields can be found.
+
+ The container wrapper is only temporary (even though it has a longer lifetime than a
+ parser). Any change made to the packet not via the collection wrapper has the potential to
+ invalidate the wrapper if it changes the packets size.
+
+ \see
+ senf::VectorParser_Container Interface of the vector parser container wrapper \n
+ senf::ListParser_Container Interface of the list parser container wrapper
+
+
+ \subsubsection packet_usage_collection_variant The Variant Parser
+
+ The senf::VariantParser is a discriminated union of parsers. It is also used for optional fields
+ (using senf::VoidPacketParser as one possible variant which is a parser parsing nothing). A
+ senf::VariantParser is not really a collection in the strict sense: It only ever contains one
+ element, the \e type of which is determined by the discriminant.
+
+ For Example, we look at the DTCP HELLO Packet as defined in the UDLR Protocol (see <a
+ href="http://tools.ietf.org/html/rfc3077">RFC 3077</a>)
+
+ \code
+ DTCPHelloPacket hello (...);
+
+ if (hello->ipVersion() == 4) {
+ typedef DTCPHelloPacket::Parser::v4fbipList_t FBIPList;
+ FBIPList::container fbips (hello->v4fbipList());
+ for (FBIPList::container::iterator i (fbips.begin()); i != fbips.end(); ++i)
+ std::cout << *i << std::endl;
+ }
+ else { // if (hello->ipVersion() == 6)
+ typedef DTCPHelloPacket::Parser::v6fbipList_t FBIPList;
+ FBIPList::container fbips (hello->v6fbipList());
+ for (FBIPList::container::iterator i (fbips.begin()); i != fbips.end(); ++i)
+ std::cout << *i << std::endl;
+ }
+ \endcode
+
+ This packet has a field \c ipVersion() which has a value of 4 or 6. Depending on the version,
+ the packet contains a list of IPv4 or IPv6 addresses. Only one of the fields \c v4fbipList() and
+ \c v6fbipList() is available at a time. Which one is decided by the value of \c
+ ipVersion(). Trying to access the wrong one will provoke undefined behavior.
+
+ Here we have used the variants discriminant (the \c ipVersion() field) to select, which field to
+ parse. More generically, every variant field should have a corresponding member to test for it's
+ existence:
+ \code
+ if (hello->has_v4fbipList()) {
+ ...
+ }
+ else { // if (hello->has_v6fbipList())
+ ...
+ }
+ \endcode
+
+ A variant can have more than 2 possible types and you can be sure, that exactly one type will be
+ accessible at any time.
+
+ It is not possible to change a variant by simply changing the discriminant:
+ \code
+ // INVALID CODE:
+ hello->ipVersion() = 6;
+ \endcode
+ Instead, for each variant field there is a special member which switches the variant to that
+ type. After switching the type, the field will be in it's initialized (that is mostly zero)
+ state.
+ \code
+ std::vector<senf::INet6Address> addrs (...);
+
+ // Initialize the IPv6 list
+ hello->init_v6fbipList();
+
+ // Copy values into that list
+ DTCPHelloPacket::Parser::v6fbipList_t::container fbips (hello->v6fbipList());
+ fbips.resize(addrs.size());
+ std::copy(addrs.begin(), addrs.end(), fbips.begin());
+ \endcode
+
+ \note Here we have documented the default variant interface as it is preferred. It is possible
+ to define variants in a different way giving other names to the special members (\c has_\e
+ name or \c init_\e name etc.). This must be documented with the composite or protocol parser
+ which defines the variant.
*/
-/** \defgroup protocolbundles Protocol Bundles
+/** \page packet_new Defining new Packet types
- Each protocol bundle provides a collection of related concrete packet classes for a group of
- related protocols:
+ Each packet is specified by the following two components:
- \li <a href="../../DefaultBundle/doc/html/index.html">DefaultBundle</a>: Some basic
- default protocols: Ethernet, Ip, TCP, UDP
- \li <a href="../../MPEGDVBBundle/doc/html/index.html">MPEGDVBBundle</a>: MPEG and DVB
- protocols
+ \li A protocol parser which defines the protocol specific fields
+ \li A packet type class which is a policy class defining the packet
- There are two ways to link with a bundle
+ \autotoc
+
+ \see <a href="../../../HowTos/NewPacket/doc/html/index.html">NewPacket HowTo</a>
+
+ \section packet_new_parser The protocol parser
+
+ The protocol parser is simply a composite parser. It defines all the protocol
+ fields. Additionally, the protocol parser may have additional members which will then be
+ accessible via the \c -> operator of the packet. Possibilities here are e.g. checksum
+ calculation and validation, packet validation as a whole and so on.
+
+ Defining a protocol parser is quite simple:
+ \code
+ struct EthernetPacketParser : public PacketParserBase
+ {
+ # include SENF_FIXED_PARSER()
+
+ SENF_PARSER_FIELD( destination, MACAddressParser );
+ SENF_PARSER_FIELD( source, MACAddressParser );
+ SENF_PARSER_FIELD( type_length, UInt16Parser );
+
+ SENF_PARSER_FINALIZE(EthernetPacketParser);
+ };
+ \endcode
- \li If you only work with known packets which you explicitly reference you may just link with
- the corresponding library.
- \li If you need to parse unknown packets and want those to be parsed as complete as possible
- without explicitly referencing the packet type, you will need to link against the combined
- object file built for every bundle. This way, all packets defined in the bundle will be
- included whether they are explicitly referenced or not (and they will all automatically be
- registered).
+ There are a lot of other possibilities to define fields. See \ref packetparsermacros for a
+ detailed description of the macro language which is used to define composite parsers.
+
+ \see
+ \ref packetparsermacros
+
+ \section packet_new_type The packet type policy class
+
+ This is a class which provides all the information needed to integrate the new packet type into
+ the packet library:
+
+ \li It provides the type of the protocol parser to use
+ \li It provides information on how the next protocol can be found and where the payload resides
+ in this packet
+ \li It provides methods to initialize a new packet and get information about the packet size
+
+ All this information is provided via static or typedef members.
+
+ \code
+ struct EthernetPacketType
+ : public PacketTypeBase,
+ public PacketTypeMixin<EthernetPacketType, EtherTypes>
+ {
+ typedef PacketTypeMixin<EthernetPacketType, EtherTypes> mixin;
+ typedef ConcretePacket<EthernetPacketType> packet;
+ typedef EthernetPacketParser parser;
+
+ using mixin::nextPacketRange;
+ using mixin::initSize;
+ using mixin::init;
+
+ static factory_t nextPacketType(packet p);
+ static void dump(packet p, std::ostream & os);
+ static void finalize(packet p);
+ };
+
+ typedef EthernetPacketType::packet EthernetPacket;
+ \endcode
+
+ The definition of senf::EthernetPacket is quite straight forward. This template works for most
+ simple packet types.
+
+ \see \ref senf::PacletTypeMixin
+ \ref senf::PacketTypeBase
+ \ref senf::PacketRegistry
*/
\f
parsers). When defining composite parsers without the help of the \ref packetparsermacros, you
should provide those same members.
- \subsection parserimpl_packet Packet parsers
-
- Packet parsers are composite parsers with relaxed requirements. Since a packet parser will never
- be used as a sub-parser (it will not be used within another composite parser or as value type in
- a collection parser), the value returned by senf::bytes for this parser must not necessarily
- cover the complete packet (e.g. if the packet has a trailer, the trailer will live outside the
- range given by senf::bytes). You may define any member you want to have in your packets field
- interface. These members may access the packet data in any way. You just need to ensure, that
- the integration into the packet-type is correct (the senf::PacketTypeMixin will by default use
- senf::bytes() to find the end of the header).
+ \subsection parserimpl_packet Protocol parsers
+
+ Protocol parsers are composite parsers with relaxed requirements. Since a Protocol parser will
+ never be used as a sub-parser (it will not be used within another composite parser or as value
+ type in a collection parser), the value returned by senf::bytes for this parser must not
+ necessarily cover the complete packet (e.g. if the packet has a trailer, the trailer will live
+ outside the range given by senf::bytes). You may define any member you want to have in your
+ packets field interface. These members may access the packet data in any way. You just need to
+ ensure, that the integration into the packet-type is correct (the senf::PacketTypeMixin will by
+ default use senf::bytes() to find the end of the header).
<hr>
*/
size_type BOOST_PP_CAT(name,_offset)() const { \
return field_offset_(static_cast<senf::mpl::rv<BOOST_PP_CAT(name,_index)-1>*>(0)); \
} \
- static size_type const BOOST_PP_CAT(name, _init_bytes) = SENF_MPL_SLOT_GET(init_bytes);
+ static size_type const BOOST_PP_CAT(name, _init_bytes) = \
+ SENF_MPL_SLOT_GET(init_bytes);
#
# define SENF_PARSER_I_FIELD_OFS_fix(name, type, access) \
- static size_type const BOOST_PP_CAT(name, _offset) = SENF_MPL_SLOT_GET(offset);
+ static size_type const BOOST_PP_CAT(name, _offset) = \
+ SENF_MPL_SLOT_GET(offset);
#
# ////////////////////////////////////////
# // SENF_PARSER_I_ADVANCE_OFS_*
return field_offset_(static_cast<senf::mpl::rv<BOOST_PP_CAT(name,_index)-1>*>(0)) \
- SENF_MPL_SLOT_GET(bitfield_size); \
} \
- static size_type const BOOST_PP_CAT(name, _init_bytes) = SENF_MPL_SLOT_GET(init_bytes) \
- - SENF_MPL_SLOT_GET(bitfield_size);
+ static size_type const BOOST_PP_CAT(name, _init_bytes) = \
+ SENF_MPL_SLOT_GET(init_bytes) - SENF_MPL_SLOT_GET(bitfield_size);
#
# define SENF_PARSER_I_BITFIELD_OFS_fix(name, type, access) \
- static size_type const BOOST_PP_CAT(name, _offset) = SENF_MPL_SLOT_GET(offset) \
- - SENF_MPL_SLOT_GET(bitfield_size);
+ static size_type const BOOST_PP_CAT(name, _offset) = \
+ SENF_MPL_SLOT_GET(offset) - SENF_MPL_SLOT_GET(bitfield_size);
#
# ////////////////////////////////////////
# // SENF_PARSER_I_BITFIELD_RESET
sources = SENFSCons.GlobSources(),
LIBS = [ 'Utils' ])
SENFSCons.Doxygen(env, extra_sources = [
- env.Dia2Png("structure.dia")
+ env.Dia2Png("structure.dia"),
+ env.Dia2Png("MPEGDVBBundle/TLV.dia")
])
SConscript(glob.glob("*/SConscript"))
SENF_PARSER_PRIVATE_FIELD( type, senf::UInt8Parser );
SENF_PARSER_VARIANT( content, type,
- (novalue( disable, senf::VoidPacketParser ))
- ( id( uint8, senf::UInt8Parser ))
- ( id( uint16, senf::UInt16Parser ))
- ( id( uint24, senf::UInt24Parser ))
- ( id( uint32, senf::UInt32Parser )) );
+ (novalue( disable, senf::VoidPacketParser ))
+ ( id( uint8value, senf::UInt8Parser ))
+ ( id( uint16value, senf::UInt16Parser ))
+ ( id( uint24value, senf::UInt24Parser ))
+ ( id( uint32value, senf::UInt32Parser )) );
SENF_PARSER_FINALIZE(SomeParser);
};
The variant \c content chooses one of the sub parsers depending on the \c type field. If \c
type is 0, senf::VoidPacketParser is selected, if it is 1, senf::UInt8Parser and so on.
+ \warning Realize, that the \a chooser field is controlled by the variant parser. This field
+ should therefore be declared either read-only or private and must be changed only via
+ the variant parser.
+
The \a types parameter specifies the types of sub-objects supported by this variant
parser. This parameter is a (Boost.Preprocessor style) sequence
<pre>(\a type) (\a type) ...</pre>
SENF_PARSER_VECTOR ( vec, vec_size_, senf::UInt32Parser );
\endcode
+ \warning Realize, that the \a size field is controlled by the vector parser. This field
+ should therefore be declared either read-only or private and must be changed only via
+ the vector parser.
+
Further additional tags are supported which modify the way, the \a size field is
interpreted:
// ispell-local-dictionary: "american"
// mode: flyspell
// mode: auto-fill
+// compile-command: "scons -u doc"
// End:
It is \e not possible to create or store BSDSocketAddress instances: You must either store
an address in one of the specifically typed subclasses or using GenericBSDSocketAddress.
+ A BSDSocketAddress or GenericBSDSocketAddress can be cast (like a downcast) to (the correct)
+ type specific cast using sockaddr_cast:
+
+ \code
+ void foo(senf::BSDSOcketAddress const & addr)
+ {
+ if (addr.family() == senf::INet4SocketAddress::addressFamily) {
+ senf::INet4SocketAddress i4addr (
+ senf::sockaddr_cast<senf::INet4SocketAddress>(addr) );
+ ...
+ }
+ }
+ \endcode
+
All these classes provide a generic \c sockaddr API to interface with legacy \c sockaddr
based code (e.g. the BSD socket API). In this base-class, this interface is read-only, the
derived classes however provide a read-write interface.
*/
#define SENF_CHECK_NO_THROW(expr) \
BOOST_CHECK_NO_THROW( \
- try { (void) expr ; } \
+ try { expr ; } \
catch (std::exception & e) { std::cerr << e.what() << std::endl; throw; } )
///////////////////////////////hh.e////////////////////////////////////////
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = .
+##INPUT = .
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
import os.path, glob
import SCons.Options, SCons.Environment, SCons.Script.SConscript, SCons.Node.FS
import SCons.Defaults, SCons.Action
+from SCons.Script import *
## \defgroup use Predefined Framework Configurators
#
#
# \ingroup target
def Doxygen(env, doxyfile = "Doxyfile", extra_sources = []):
+ if not 'all' in BUILD_TARGETS and not 'doc' in BUILD_TARGETS and not 'all_docs' in BUILD_TARGETS:
+ return []
# ARGHHH !!! without the [:] we are changing the target list
# ||| WITHIN THE DOXYGEN BUILDER
docs = env.Doxygen(doxyfile)[:]