X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=HowTos%2FNewPacket%2FMainpage.dox;h=0ca058e0815a447f2c374a5265efa54c60faf8cf;hb=6df7613bc4c5a8c27a6af11450d2cb8fbb6ea3a6;hp=aa400f97b8d86b524b9c7db94682763ce33fbc0a;hpb=f47679431aa3461936ee4a85c0c4216e44292b55;p=senf.git diff --git a/HowTos/NewPacket/Mainpage.dox b/HowTos/NewPacket/Mainpage.dox index aa400f9..0ca058e 100644 --- a/HowTos/NewPacket/Mainpage.dox +++ b/HowTos/NewPacket/Mainpage.dox @@ -2,28 +2,33 @@ // // Copyright (C) 2008 // Fraunhofer Institute for Open Communication Systems (FOKUS) -// Competence Center NETwork research (NET), St. Augustin, GERMANY -// Stefan Bund // -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. +// The contents of this file are subject to the Fraunhofer FOKUS Public License +// Version 1.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://senf.berlios.de/license.html // -// 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. +// The Fraunhofer FOKUS Public License Version 1.0 is based on, +// but modifies the Mozilla Public License Version 1.1. +// See the full license text for the amendments. // -// 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. +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the License. +// +// The Original Code is Fraunhofer FOKUS code. +// +// The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. +// (registered association), Hansastraße 27 c, 80686 Munich, Germany. +// +// Contributor(s): +// Stefan Bund -/** \mainpage Defining and using a new 'libPacket' Packet Type + +/** \mainpage Defining and using a new 'libPacket' Packet Type This howto will introduce the facilities needed to define a new packet type. As example, the - \c GREPacket type is defined. + \c GREPacket type is defined. \autotoc @@ -50,7 +55,7 @@ \li It implements packet invariants To define a new packet type, we need to implement two classes which together provide all this - information: + information: \li a \e parser (a class derived from senf::PacketParserBase). This class defines the data fields of the packet header and may also provide additional packet specific functionality. @@ -151,11 +156,11 @@ SENF_PARSER_BITFIELD ( version, 3, unsigned ); SENF_PARSER_BITFIELD ( protocolType, 16, unsigned ); \endcode - + This is a correct \c GREPacket header definition, but there is room for a small optimization: Since the \a protocolType field is exactly 2 bytes wide and is aligned on a byte boundary, we can define it as a UInt16 field (instead of a bitfield): - + \code SENF_PARSER_BITFIELD ( checksumPresent, 1, bool ); SENF_PARSER_SKIP_BITS ( 12 ); @@ -189,13 +194,13 @@ suitable for a single optional field as well as for an optional contiguous sequence of fields. In our GRE example, there are two fields which need to be enabled/disabled en bloc. We first - define an auxiliary sub-parser which combines the two fields. + define an auxiliary sub-parser which combines the two fields. \code struct GREPacketParser_OptFields : public senf::PacketParserBase { # include SENF_FIXED_PARSER() - + // typedef checksum_t uint16_t; XXX defined automatically??? SENF_PARSER_FIELD ( checksum, senf::UInt16Parser ); @@ -239,7 +244,7 @@ p.protocolType() = 0x86dd; p.optionalFields().get<1>().checksum() = 12345u; \endcode - + This code has two problems: \li accessing the checksum field is quite unwieldy \li changing the checksumPresent() value will break the parser @@ -248,7 +253,7 @@ the selector (here \a checksumPresent) is changed, since the variant parser must ensure that the header data stays consistent. Whenever the checksumPresent field is enabled, the variant parser needs to insert additional 4 bytes of data. And it must remove those bytes whenever the - checksumPresent field is disabled. + checksumPresent field is disabled. \subsection howto_newpacket_parser_fixvariant Fixing access by providing custom accessor members @@ -259,7 +264,7 @@ automatically or it may be calculated from other values (we'll see later how to do this). In all these cases we will want to disallow the user to directly change the value, while still - allowing to read the value. To do this, we mark \e value \e fields as read-only: + allowing to read the value. To do this, we can mark \e value \e fields as read-only: \code SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool ); @@ -270,37 +275,39 @@ sub-parsers. In this case however, we still want to allow the user to change the field value, albeit not - directly. We will need to go through the collection parser, in this case the variant. Since the - variant syntax to change the currently active variant is not very comfortable, we provide some - helper functions: + directly. We will need to go through the collection parser, in this case the variant. + + The syntax for accessing a variant is quite cumbersome. Therefore we adjust the variant + definition to generate a more usable interface: \code - void enableChecksum() const { optionalFields_().init<1>(); } - void disableChecksum() const { optionalFields_().init<0>(); } + SENF_PARSER_VARIANT ( optionalFields_, checksumPresent, + (novalue(disable_checksum, senf::VoidPacketParser)) + ( id(checksum, GREPacketParser_OptFields)) ); \endcode - By changing the collection we automatically change the field, that this collection references. - Now we only need to provide an additional \a checksum member to hide the complex variant access - syntax from the user. And, since all variant access is now done via specialized members, we can - hide the variant field completely from the user: - + Here, we added some optional information to the variants type list. + + 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 - SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent, - (senf::VoidPacketParser) - (GREPacketParser_OptFields) ); + void disable_checksum() const { optionalFields_().init<0>; } - GREPacketParser_OptFields::checksum_t checksum() const - { return optionalFields_().get<1>().checksum(); } + typedef GREPacketParser_OptFields checksum_t; + checksum_t checksum() const { return optionalFields_().get<1>(); } + void init_checksum() const { optionalFields_().init<1>; } + bool has_checksum() const { return optionalFields_().variant() == 1; } \endcode + (Again: We don't implement these fields ourselves, this is done by \ref SENF_PARSER_VARIANT()) - As we can see here, we have used the name_t typedef which is available for - all fields to provide the return value for our accessor member. + \c disable_checksum() and \c init_checksum() change the selected variant. This will + automatically change the \c checksumPresent() field accordingly. 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 We have now implemented parsing all the header fields. However, often packets would benefit from @@ -311,9 +318,9 @@ \code #include - checksum_t::value_type calculateChecksum() const + checksum_t::checksum_t::value_type calculateChecksum() const { - if (!checksumEnabled()) + if (!checksumEnabled()) return 0; senf::IpChecksum cs; @@ -328,7 +335,7 @@ 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. + changing the checksum field we manually pass the correct data to \a cs. We use the special i(offset) helper to get iterators \a offset number of bytes into the data. This helper has the additional benefit of range-checking the returned @@ -349,7 +356,7 @@ \code #include - + struct GREPacketParser_OptFields : public senf::PacketParserBase { # include SENF_FIXED_PARSER() @@ -371,27 +378,21 @@ SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser ); SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent, - (senf::VoidPacketParser) - (GREPacketParser_OptFields) ); - - GREPacketParser_OptFields::checksum_t checksum() const - { return optionalFields_().get<1>().checksum(); } + (novalue(disable_checksum, senf::VoidPacketParser)) + ( id(checksum, GREPacketParser_OptFields)) ); - void enableChecksum() const { optionalFields_().init<1>(); } - void disableChecksum() const { optionalFields_().init<0>(); } - SENF_PARSER_FINALIZE(GREPacketParser); - checksum_t::value_type calculateChecksum() const; + checksum_t::checksum_t::value_type calculateChecksum() const; }; // In the implementation (.cc) file: #include - + GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const { - if (!checksumEnabled()) + if (!checksumEnabled()) return 0; validate(6); @@ -430,7 +431,7 @@ typedef senf::PacketTypeMixin mixin; typedef senf::ConcretePacket packet; typedef senf::GREPacketParser parser; - + using mixin::nextPacketRange; using mixin::nextPacketType; using mixin::init; @@ -453,7 +454,7 @@ The next block of statements imports all the default implementations provided by the mixin class: - + \li \a nextPacketRange provides information about where the next packet lives within the GRE packet. \li \a nextPacketType provides the type of the next packet from information in the GRE packet. @@ -461,7 +462,7 @@ GREPacketParser::init. \li \a initSize is called to find the size of an empty (newly create) GRE packet. This is also provided by GREPacketParser. - + With these default implementations provided by the mixin, only a few additional members are needed to complete the \c GREPacketType: \a nextPacketKey, \a finalize, and \a dump. @@ -530,7 +531,7 @@ \endcode But wait -- what is \c GREPacket ? This question is answered a few section further down. - + The last thing we need to do is, we need to set the \a protocolType() field to the correct value when packets are newly created or changed. This is done within \a finalize: @@ -550,7 +551,7 @@ have to be updated to be correct. This is the responsibility of the \a finalize() member. \code - static void finalize(packet p) + static void finalize(packet p) { p->protocolType() << key(p.next(senf::nothrow)); if (p->checksumPresent()) @@ -565,8 +566,8 @@ Here we are using the more generic parser assignment expressed using the \c << operator. This operator in the most cases works like an ordinary assignment, however it can also be used to assign parsers to each other efficiently and it supports 'optional values' (as provided by Boost.Optional and as returned - by \c key()). + href="http://www.boost.org/doc/libs/release/libs/optional/index.html">Boost.Optional and + as returned by \c key()). \subsection howto_newpacket_type_dump Writing out a complete packet: The 'dump()' member @@ -574,7 +575,7 @@ For diagnostic purposes, every packet should provide a meaningful \a dump() member which writes out the complete packet. This member is simple to implement and is often very helpful when tracking down problems. - + \code #include @@ -588,14 +589,14 @@ << p->protocolType() << "\n"; if (p->checksumPresent()) os << " checksum : 0x" << std::hex << std::setw(4) - << std::setfill('0') << p->checksum() << "\n"; + << std::setfill('0') << p->checksum() << "\n"; } \endcode - + This member is quite straight forward. We should try to adhere to the formating standard shown above: The first line should be the type of packet/header being dumped followed by one line for each protocol field. The colon's should be aligned at column 33 with the field name indented by - 2 spaces. + 2 spaces. The \c boost::ios_all_saver is just used to ensure, that the stream formatting state is restored correctly at the end of the method. An instance of this type will save the stream state when @@ -617,24 +618,24 @@ typedef senf::PacketTypeMixin mixin; typedef senf::ConcretePacket packet; typedef senf::GREPacketParser parser; - + using mixin::nextPacketRange; using mixin::nextPacketType; using mixin::init; using mixin::initSize; static key_t nextPacketKey(packet p) { return p->protocolType(); } - + static void finalize(packet p) { - p->protocolType() << key(p.next(senf::nothrow)); + p->protocolType() << key(p.next(senf::nothrow)); if (p->checksumPresent()) p->checksum() << p->calculateChecksum(); } - + static void dump(packet p, std::ostream & os); }; typedef GREPacketType::packet GREPacket; - + // In the implementation (.cc) file: #include @@ -653,7 +654,7 @@ << p->protocolType() << "\n"; if (p->checksumPresent()) os << " checksum : 0x" << std::hex << std::setw(4) - << std::setfill('0') << p->checksum() << "\n"; + << std::setfill('0') << p->checksum() << "\n"; } \endcode @@ -670,7 +671,7 @@ needs to obey to be considered valid. If the packet is not valid it cannot be parsed and should be dropped. We can't drop it here but if the packet is invalid, we certainly must refrain from trying to parser any payload since we cannot assume the packet to have the format we assume our - GRE packet to have. + GRE packet to have. There are two conditions defined in the RFC which render a GRE packet invalid: If one of the \a reserved0() fields first 5 bits is set or if the version is not 0. We will add a \a valid() @@ -720,7 +721,7 @@ factory_t nextPacketType(packet p) { return p->valid() ? lookup(p->protocolType()) : no_factory(); } \endcode - + As we see, this is still quite simple. \c factory_t is provided by senf::PacketTypeBase. For our purpose it is an opaque type which somehow enables the packet library to create a new packet of a specified packet type. The \c factory_t has a special value, \c no_factory() which stands for @@ -737,7 +738,7 @@ senf::EtherTypes registry ? Here one way to do this: \code - factory_t nextPacketType(packet p) { + factory_t nextPacketType(packet p) { if (p->valid()) { if (p->protocolType() == 0x6558) return senf::EthernetPacket::factory(); else return lookup(p->protocolType()); @@ -779,7 +780,7 @@ #define HH_GREPacket_ #include - + struct GREPacketParser_OptFields : public senf::PacketParserBase { # include SENF_FIXED_PARSER() @@ -801,22 +802,15 @@ SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser ); - SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent, - (senf::VoidPacketParser) - (GREPacketParser_OptFields) ); + SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent, + (novalue(disable_checksum, senf::VoidPacketParser)) + ( id(checksum, GREPacketParser_OptFields)) ); bool valid() const { return version() == 0 && reserved0_5bits_() == 0; } - 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>(); } - SENF_PARSER_FINALIZE(GREPacketParser); - checksum_t::value_type calculateChecksum() const; + checksum_t::checksum_t::value_type calculateChecksum() const; }; struct GREPacketType @@ -826,24 +820,24 @@ typedef senf::PacketTypeMixin mixin; typedef senf::ConcretePacket packet; typedef senf::GREPacketParser parser; - + using mixin::nextPacketRange; using mixin::init; using mixin::initSize; - factory_t nextPacketType(packet p) + factory_t nextPacketType(packet p) { return p->valid() ? lookup(p->protocolType()) : no_factory(); } - + static void finalize(packet p) { p->protocolType() << key(p.next(senf::nothrow)); if (p->checksumPresent()) p->checksum() << p->calculateChecksum(); } - + static void dump(packet p, std::ostream & os); }; typedef GREPacketType::packet GREPacket; - + #endif \endcode @@ -858,9 +852,9 @@ SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket ); SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket ); - GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const + GREPacketParser::checksum_t::checksum_t::value_type GREPacketParser::calculateChecksum() const { - if (!checksumEnabled()) + if (!checksumEnabled()) return 0; validate(6); @@ -883,7 +877,7 @@ << p->protocolType() << "\n"; if (p->checksumPresent()) os << " checksum : 0x" << std::hex << std::setw(4) - << std::setfill('0') << p->checksum() << "\n"; + << std::setfill('0') << p->checksum() << "\n"; } \endcode @@ -945,18 +939,18 @@ senf::TapSocketHandle tap ("tap0"); senf::ConnectedRawV6ClientSocketHandle osock (47u, senf::INet6SocketAddress(argv[1])); - + while (true) { senf::EthernetPacket eth (senf::EthernetPacket::create(senf::noinit)); isock.read(eth.data(),0u); GREPacket gre (senf::GREPacket::createBefore(eth)); - gre.finalize(); + gre.finalizeAll(); osock.write(gre.data()); } } \endcode - + \section howto_newpacket_further Further reading Lets start with references to the important API's (Use the List of all members link to @@ -977,7 +971,7 @@ When implementing new packet's, the following information will be helpful: - + @@ -986,10 +980,10 @@ use it. - + - +
senf::PacketTypeBase here you find a description of the members which need to be implemented to provide a 'packet type'. Most of these members will normally be provided by the mixin helper.
\ref packetparser This section describes the packet parser facility.
\link packetparsermacros Packet parser macros\endlink A complete list and documentation of all the packet parser macros.
\ref parseint, \n \ref parsecollection There are several lists of available reusable packet parsers. However, these lists are not complete as there are other protocol specific reusable parsers (without claiming to be exhaustive: senf::INet4AddressParser,