4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 /** \mainpage Defining and using a new 'libPacket' Packet Type
25 This howto will introduce the facilities needed to define a new packet type. As example, the
26 \c GREPacket type is defined.
31 \section howto_newpacket_overview Overview
33 The packet library supports two basic packet representations, the more generic one being
34 senf::Packet. This representation does not know anything about the type of packet, its fields or
35 properties. It really only is a bunch of bytes. Possibly there is a preceding packet (header) or
36 a following one, but that is all, a senf::Packet knows.
38 The second representation is implemented by senf::ConcretePacket. This representation derives
39 from senf::Packet and adds information about the packet type, its fields, eventually some
40 invariants or packet specific operations etc. In what follows, we will concentrate on this
41 latter representation.
43 A concrete packet type in senf provides a lot of detailed information about a specific type of
46 \li It provides access to the packets fields
47 \li It may provide additional packet specific functions (e.g. calculating or validating a
49 \li It provides information on the nesting of packets
50 \li It implements packet invariants
52 To define a new packet type, we need to implement two classes which together provide all this
55 \li a \e parser (a class derived from senf::PacketParserBase). This class defines the data
56 fields of the packet header and may also provide additional packet specific functionality.
57 \li a \e packet \e type (a class derived from senf::PacketTypeBase). This class defines, how
58 packets are nested and how to initialize and maintain invariants.
60 The following sections describe how to define these classes. Where appropriate, we will use GRE
61 (Generic Routing Encapsulation) as an example.
63 \section howto_newpacket_gre Introducing the GRE example packet type
65 When defining a new packet type, we start out by answering two important questions:
67 \li What kind of parser is needed for this packet type (fixed size or variable sized).
68 \li Whether the packet has another packet as payload (a nested packet) and how the type of this
69 payload is found (whether a registry is used and if yes, which).
71 In the case of GRE, these questions can be answered by just looking at the GRE specification in
72 <a href="http://tools.ietf.org/html/rfc2784">RFC 2784</a>. In Section 2.1 we find the header
76 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
77 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78 |C| Reserved0 | Ver | Protocol Type |
79 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 | Checksum (optional) | Reserved1 (Optional) |
81 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 This header is followed by the payload data.
86 Using this protocol definition, we see that the header incorporates optional fields. Therefore
87 it must be dynamically sized: if the \a Checksum \a Present bit \a C is set, both \a Checksum
88 and \a Reserved1 are present, otherwise both must be omitted.
90 Further inspection of the RFC reveals that the \a Protocol \a Type is used to define the type of
91 payload which directly follows the GRE header. This value is an <a
92 href="http://www.iana.org/assignments/ethernet-numbers">ETHERTYPE</a> value. To allow the packet
93 library to automatically parse the GRE payload data, we need to tell the packet library which
94 ETHERTYPE is implemented by which packet type. This kind of association already exists in the
95 form of the senf::EtherTypes registry. Our GRE packet will therefore use this registry.
99 \li The GRE packet header is a dynamically sized header.
100 \li The GRE packet header uses the senf::EtherTypes registry for next-header selection.
103 \section howto_newpacket_parser Implementing the packet parser
105 Each parser is responsible for turning a bunch of bytes into an interpreted header with specific
106 fields. A parser instance is initialized with an iterator (pointer) to the first byte to be
107 interpreted (the first byte of the packet data) and provides member functions to access the
108 header fields. You could implement these members manually, but the SENF library provides a large
109 set of helper macros which simplify this task considerably.
111 \subsection howto_newpacket_parser_skeleton The PacketParser skeleton
114 #include <senf/Packets.hh>
116 struct GREPacketParser : public senf::PacketParserBase
118 # include SENF_PARSER()
123 SENF_PARSER_FINALIZE(GREPacketParser);
127 This is the standard skeleton of any parser class: We need to inherit senf::PacketParserBase and
128 start out by including either \ref SENF_PARSER() or \ref SENF_FIXED_PARSER(), depending on
129 whether we define a fixed size or a dynamically sized parser. As \c GREPacketParser is
130 dynamically sized, we include \ref SENF_PARSER().
132 The definition of fields will be described in the next subsection.
134 After the fields have been defined, we need to call the \ref SENF_PARSER_FINALIZE() macro to
135 close of the parser definition. This call takes the name of the parser being defined as it's
138 This is already a valid parser, albeit not a very usable one, since it does not define any
139 fields. We now go back to define the parser fields and begin with the simple part: fields which
142 \subsection howto_newpacket_parser_simple Simple field definitions
144 Packet parser fields are defined using special \ref packetpasermacros. We take the fields
145 directly from the packet definition (the GRE RFC in this case). This will give us to the
146 following code fragment:
149 SENF_PARSER_BITFIELD ( checksumPresent, 1, bool );
150 SENF_PARSER_SKIP_BITS ( 12 );
151 SENF_PARSER_BITFIELD ( version, 3, unsigned );
152 SENF_PARSER_BITFIELD ( protocolType, 16, unsigned );
155 This is a correct \c GREPacket header definition, but there is room for a small optimization:
156 Since the \a protocolType field is exactly 2 bytes wide and is aligned on a byte boundary, we
157 can define it as a UInt16 field (instead of a bitfield):
160 SENF_PARSER_BITFIELD ( checksumPresent, 1, bool );
161 SENF_PARSER_SKIP_BITS ( 12 );
162 SENF_PARSER_BITFIELD ( version, 3, unsigned );
163 SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
166 Whereas \ref SENF_PARSER_BITFIELD can only define bit-fields, \ref SENF_PARSER_FIELD can define
167 almost arbitrary field types. The type is specified by passing the name of another parser to
168 \ref SENF_PARSER_FIELD.
170 It is important to understand, that the accessors do \e not return the parsed field value.
171 Instead, they return another \e parser which is used to further interpret the value. This is due
172 to the inherently recursive nature of the SENF packet parsers, that allows us to define rather
173 complex header formats if needed. Of course, at some point we will hit bottom and need real
174 values. This is, what <em>value parsers</em> do: they interpret some bytes or bits and return
175 the value of that field (not a parser). Examples are the bitfield parsers returned by the
176 accessors generated by SENF_PARSER_BITFIELD (like senf::UIntFieldParser) or the
179 What is going on inside the macros above? Basically, they define accessor functions for a
180 specific field, like \a checksumPresent() or \a protocolType(). They also manage a <em>current
181 Offset</em>. This value is advanced according to the field size whenever a new field is defined
182 (and since this parser is defined as a dynamically sized parser, this offset is not constant but
183 an expression which calculates the offset of a field depending on the preceding data).
185 \subsection howto_newpacket_parser_variant Defining optional fields: The 'variant' parser
187 The parser is currently very simple, and it could have been defined as a fixed size parser. Now
188 for the tricky part: defining parsers the optional fields. The mechanism described here is
189 suitable for a single optional field as well as for an optional contiguous sequence of fields.
191 In our GRE example, there are two fields which need to be enabled/disabled en bloc. We first
192 define an auxiliary sub-parser which combines the two fields.
195 struct GREPacketParser_OptFields : public senf::PacketParserBase
197 # include SENF_FIXED_PARSER()
199 // typedef checksum_t uint16_t; XXX defined automatically???
201 SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
202 SENF_PARSER_SKIP ( 2 );
204 SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
208 This parser only parses the two optional fields, the second ("Reserved1") field just being
209 skipped. It is a fixed size parser, as indicated by the SENF_FIXED_PARSER() macro. We can
210 now use \ref SENF_PARSER_VARIANT() to add it as an optional parser to the GRE header in our \c
211 GREPacketParser implementation (the typedef'ed checksum_t will be used later on):
214 SENF_PARSER_BITFIELD ( checksumPresent, 1, bool );
215 SENF_PARSER_SKIP_BITS ( 12 );
216 SENF_PARSER_BITFIELD ( version, 3, unsigned );
218 SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
220 SENF_PARSER_VARIANT ( optionalFields, checksumPresent,
221 (senf::VoidPacketParser)
222 (GREPacketParser_OptFields) );
225 For a variant parser, two things need to be specified: a selector and a list of variant parsers.
226 The selector is a distinct parser field that is used to decide which variant to choose. In this
227 simple case, the field must be an unsigned integer (more precisely: a value parser returning a
228 value which is implicitly convertible to \c unsigned). This value is used as an index into the
229 list of variant types. So in our case, the value 0 (zero) is associated with
230 senf::VoidPacketParser, whereas the value 1 (one) is associated with \c
231 GREPacketParser_OptFields. senf::VoidPacketParser is a special (empty or no-op) parser which is
232 used in a variant to denote a case in which the variant parser should not parse anything.
234 This parser will work, it is however not very safe and not very usable. If \a p is a
235 GREPacketParser instance, than we would access the fields via:
237 p.checksumPresent() = true;
239 p.protocolType() = 0x86dd;
240 p.optionalFields().get<1>().checksum() = 12345u;
243 This code has two problems:
244 \li accessing the checksum field is quite unwieldy
245 \li changing the checksumPresent() value will break the parser
247 The second problem is caused by the fact that the variant parser needs to be informed whenever
248 the selector (here \a checksumPresent) is changed, since the variant parser must ensure that the
249 header data stays consistent. Whenever the checksumPresent field is enabled, the variant parser
250 needs to insert additional 4 bytes of data. And it must remove those bytes whenever the
251 checksumPresent field is disabled.
253 \subsection howto_newpacket_parser_fixvariant Fixing access by providing custom accessor members
255 The problems outlined above will happen whenever we use variant parsers, and they will often
256 occur with other complex parsers too (most XXX \ref parsercollection reference some field
257 external to themselves, and they will break if that value is changed without them knowing about
258 it). There might be other reasons to restrict access to a field: the field may be set
259 automatically or it may be calculated from other values (we'll see later how to do this).
261 In all these cases we will want to disallow the user to directly change the value, while still
262 allowing to read the value. To do this, we mark \e value \e fields as read-only:
265 SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
268 \e Value \e fields are fields implemented by parsers returning a simple value (i.e. bit-field,
269 integer and some additional parsers like those parsing network addresses) as apposed to complex
272 In this case however, we still want to allow the user to change the field value, albeit not
273 directly. We will need to go through the collection parser, in this case the variant. Since the
274 variant syntax to change the currently active variant is not very comfortable, we provide some
278 void enableChecksum() const { optionalFields_().init<1>(); }
279 void disableChecksum() const { optionalFields_().init<0>(); }
282 By changing the collection we automatically change the field, that this collection references.
283 Now we only need to provide an additional \a checksum member to hide the complex variant access
284 syntax from the user. And, since all variant access is now done via specialized members, we can
285 hide the variant field completely from the user:
288 SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
289 (senf::VoidPacketParser)
290 (GREPacketParser_OptFields) );
292 GREPacketParser_OptFields::checksum_t checksum() const
293 { return optionalFields_().get<1>().checksum(); }
296 As we can see here, we have used the <em>name</em><code>_t</code> typedef which is available for
297 all fields to provide the return value for our accessor member.
299 The \c GREPacketParser is now simple and safe to use. The only responsibility of the user now is to
300 only access \a checksum() if the \a checksumPresent() field is set. Otherwise, the behavior is
301 undefined (in debug builds, the parser will terminate the application with an assert).
304 \subsection howto_newpacket_parser_add Providing additional functionality
306 We have now implemented parsing all the header fields. However, often packets would benefit from
307 additional functionality. In the case of GRE, this could be a function to calculate the checksum
308 value if it is enabled. Defining this member will also show, how to safely access the raw packet
309 data from a parser member.
312 #include <senf/Utils/IpChecksum.hh>
314 checksum_t::value_type calculateChecksum() const
316 if (!checksumEnabled())
320 cs.feed( i(), i(4) );
321 // Skip even number of 0 bytes (the 2 bytes checksum field)
322 // cs.feed(0); cs.feed(0);
323 cs.feed( i(6), data().end() );
329 This code just implements what is defined in the RFC: The checksum covers the complete GRE
330 packet including it's header with the checksum field temporarily set to 0. Instead of really
331 changing the checksum field we manually pass the correct data to \a cs.
333 We use the special <tt>i(</tt><i>offset</i><tt>)</tt> helper to get iterators \a offset number
334 of bytes into the data. This helper has the additional benefit of range-checking the returned
335 iterator and is thereby safe from errors due to truncated packets: If the offset is out of
336 range, a TruncatedPacketException will be thrown.
338 The \a data() function on the other hand returns a reference to the complete data container of
339 the packet under inspection (the GRE packet in this case). Access to \a data() should be
340 restricted as much as possible. It is safe when defining new packet parsers (parsers, which
341 parser a complete packet like GREPacketParser). It's usage from sub parsers (like
342 GREPacketParser_OptFields or even senf::UInt16Parser) would be much more arcane and should be
346 \subsection howto_newpacket_parser_final The complete GREPacketParser implementation
348 So this is now the complete implementation of the \c GREPacketParser:
351 #include <senf/Packets.hh>
353 struct GREPacketParser_OptFields : public senf::PacketParserBase
355 # include SENF_FIXED_PARSER()
357 SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
358 SENF_PARSER_SKIP ( 2 );
360 SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
363 struct GREPacketParser : public senf::PacketParserBase
365 # include SENF_PARSER()
367 SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
368 SENF_PARSER_SKIP_BITS ( 12 );
369 SENF_PARSER_BITFIELD ( version, 3, unsigned );
371 SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
373 SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
374 (senf::VoidPacketParser)
375 (GREPacketParser_OptFields) );
377 GREPacketParser_OptFields::checksum_t checksum() const
378 { return optionalFields_().get<1>().checksum(); }
380 void enableChecksum() const { optionalFields_().init<1>(); }
381 void disableChecksum() const { optionalFields_().init<0>(); }
383 SENF_PARSER_FINALIZE(GREPacketParser);
385 checksum_t::value_type calculateChecksum() const;
388 // In the implementation (.cc) file:
390 #include <senf/Utils/IpChecksum.hh>
392 GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const
394 if (!checksumEnabled())
399 cs.feed( i(), i()+4 );
400 // Skip even number of 0 bytes (the 2 bytes checksum field)
401 // cs.feed(0); cs.feed(0);
402 cs.feed( i()+6, data().end() );
409 \section howto_newpacket_type Defining the packet type
411 After defining the packet parser, the <em>packet type</em> must be defined. This class is used
412 as a policy and collects all the information necessary to be known about the packet type.
414 The <em>packet type</em> class is \e never instantiated. It has only typedef, constants or
417 \subsection howto_newpacket_type_skeleton The packet type skeleton
419 For every type of packet, the <em>packet type</em> class will look roughly the same. If the
420 packet uses a registry and is not hopelessly complex, the packet type will almost always
424 #include <senf/Packets.hh>
427 : public senf::PacketTypeBase,
428 public senf::PacketTypeMixin<GREPacketType, EtherTypes>
430 typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
431 typedef senf::ConcretePacket<GREPacketType> packet;
432 typedef senf::GREPacketParser parser;
434 using mixin::nextPacketRange;
435 using mixin::nextPacketType;
437 using mixin::initSize;
439 // Define members here
443 We note, that it derives from two classes: senf::PacketTypeBase and
444 senf::PacketTypeMixin. senf::PacketTypeBase must be inherited by every packet type class. the
445 senf::PacketTypeMixin provides default implementations for some members which are useful for
446 most kinds of packets. If a packet type is very complex and these defaults don't work, the mixin
447 class can and should be left out. More on this (what the default members do exactly and when the
448 mixin can be used) can be found in the senf::PacketTypeMixin documentation.
450 Of the typedefs, only \a parser is mandatory. It defines the packet parser to use to interpret
451 this type of packet. \a mixin and \a packet are defined to simplify the following
452 definitions (More on \a packet and senf::ConcretePacket later).
454 The next block of statements imports all the default implementations provided by the mixin
457 \li \a nextPacketRange provides information about where the next packet lives within the GRE
459 \li \a nextPacketType provides the type of the next packet from information in the GRE packet.
460 \li \a init is called to initialize a new GRE packet. This call is forwarded to \c
461 GREPacketParser::init.
462 \li \a initSize is called to find the size of an empty (newly create) GRE packet. This is also
463 provided by GREPacketParser.
465 With these default implementations provided by the mixin, only a few additional members are
466 needed to complete the \c GREPacketType: \a nextPacketKey, \a finalize, and \a dump.
469 \subsection howto_newpacket_type_registry Utilizing the packet registry
471 A packet registry maps an arbitrary key value to a type of packet represented by a packet
472 factory instance. There may be any number of packet registries. When working with packet
473 registries, there are three separate steps:
474 \li Using the registry to tell the packet library, what type of packet to instantiate for the
476 \li Given a payload packet of some type, set the appropriate payload type field in the packet
477 header to the correct value (inverse of above).
478 \li Adding packets to the registry.
480 We want the GRE packet to utilize the senf::EtherTypes registry to find the type of packet
481 contained in the GRE payload. The details have already been taken care of by the
482 senf::PacketTypeMixin (it provides the \a nextPacketType member). However, to lookup the packet
483 in the registry, the mixin needs to know the key value. To this end, we implement \a
484 nextPacketKey(), which is very simple:
487 static key_t nextPacketKey(packet p) { return p->protocolType(); }
490 Since all \c GREPacketType members are static, they are passed the packet in question as an
491 argument. \a nextPacketKey() just needs to return the value of the correct packet field. And
492 since the \c packet type (as defined as a typedef) allows direct access to the packet parser
493 using the <tt>-></tt> operator, we can simply access that value.
495 The \c key_t return type is a typedef provided by the mixin class. It is taken from the type of
496 registry, in this case it is senf::EtherTypes::key_t (which is defined as a 16 bit unsigned
499 With this information, the packet library can now find out the type of packet needed to parse
500 the GRE payload -- as long as the \a protocolType() is registered with the senf::EtherTypes
501 registry. If this is not the case, the packet library will not try to interpret the payload, it
502 will return a senf::DataPacket.
504 One special case of GRE encapsulation occurs when layer 2 frames and especially ethernet frames
505 are carried in the GRE payload. The ETHERTYPE registry normally only contains layer 3 protocols
506 (like IP or IPX) however for this special case, the value 0x6558 has been added to the ETHERTYPE
507 registry. So we need to add this value to inform the packet library to parse the payload as an
508 ethernet packet if the \a protocolType() is 0x6558. This happens in the implementation file (the
512 #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
514 SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
517 This macro registers the value 0x6558 in the senf::EtherTypes registry and associates it with
518 the packet type senf::EthernetPacket. This macro declares an anonymous static variable, it
519 therefore must always be placed in the implementation file and \e never in an include file.
521 Additionally, we want the GRE packet to be parsed when present as an IP payload. Therefore we
522 additionally need to register GRE in the senf::IpTypes registry. Looking at the <a
523 href="http://www.iana.org/assignments/protocol-numbers">IP protocol numbers</a>, we find that
524 GRE has been assigned the value 47:
527 #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
529 SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
532 But wait -- what is \c GREPacket ? This question is answered a few section further down.
534 The last thing we need to do is, we need to set the \a protocolType() field to the correct value
535 when packets are newly created or changed. This is done within \a finalize:
538 static void finalize(packet p) { p->protocolType() << key(p.next(senf::nothrow)); }
541 The \c key() function is provided by the mixin class: It will lookup the \e type of a packet in
542 the registry and return that packets key in the registry. If the key cannot be found, the return
543 value is such that the assignment is effectively skipped.
546 \subsection howto_newpacket_type_invariants Providing packet invariants
548 Many packets have some invariants that must hold: The payload size must be equal to some field,
549 a checksum must match and so on. When packets are newly created or changed, these invariants
550 have to be updated to be correct. This is the responsibility of the \a finalize() member.
553 static void finalize(packet p)
555 p->protocolType() << key(p.next(senf::nothrow));
556 if (p->checksumPresent())
557 p->checksum() << p->calculateChecksum();
561 We already used finalize above to set the \a protocolType() field. Now we add code to update the
562 \a checksum() field if present (this always needs to be done last since the checksum depends on
563 the other field values).
565 Here we are using the more generic parser assignment expressed using the \c << operator. This
566 operator in the most cases works like an ordinary assignment, however it can also be used to
567 assign parsers to each other efficiently and it supports 'optional values' (as provided by <a
568 href="http://www.boost.org/libs/optional/doc/optional.html">Boost.Optional</a> and as returned
572 \subsection howto_newpacket_type_dump Writing out a complete packet: The 'dump()' member
574 For diagnostic purposes, every packet should provide a meaningful \a dump() member which writes
575 out the complete packet. This member is simple to implement and is often very helpful when
576 tracking down problems.
579 #include <boost/io/ios_state.hpp>
581 static void dump(packet p, std::ostream & os)
583 boost::io::ios_all_saver ias(os);
584 os << "General Routing Encapsulation:\n"
585 << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
586 << " version : " << p->version() << "\n"
587 << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
588 << p->protocolType() << "\n";
589 if (p->checksumPresent())
590 os << " checksum : 0x" << std::hex << std::setw(4)
591 << std::setfill('0') << p->checksum() << "\n";
595 This member is quite straight forward. We should try to adhere to the formating standard shown
596 above: The first line should be the type of packet/header being dumped followed by one line for
597 each protocol field. The colon's should be aligned at column 33 with the field name indented by
600 The \c boost::ios_all_saver is just used to ensure, that the stream formatting state is restored
601 correctly at the end of the method. An instance of this type will save the stream state when
602 constructed and will restore that state when destructed.
604 \subsection howto_newpacket_type_final Final touches
606 The \c GREPacket implementation is now almost complete. The only thing missing is the \c
607 GREPacket itself. \c GREPacket is just a typedef for a specific senf::ConcretePacket template
608 instantiation. Here the complete GREPacket definition:
611 #include <senf/Packets.hh>
614 : public senf::PacketTypeBase,
615 public senf::PacketTypeMixin<GREPacketType, EtherTypes>
617 typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
618 typedef senf::ConcretePacket<GREPacketType> packet;
619 typedef senf::GREPacketParser parser;
621 using mixin::nextPacketRange;
622 using mixin::nextPacketType;
624 using mixin::initSize;
626 static key_t nextPacketKey(packet p) { return p->protocolType(); }
628 static void finalize(packet p) {
629 p->protocolType() << key(p.next(senf::nothrow));
630 if (p->checksumPresent()) p->checksum() << p->calculateChecksum();
633 static void dump(packet p, std::ostream & os);
636 typedef GREPacketType::packet GREPacket;
638 // In the implementation (.cc) file:
640 #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
641 #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
643 SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
644 SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
646 void GREPacketType::dump(packet p, std::ostream & os)
648 boost::io::ios_all_saver ias(os);
649 os << "General Routing Encapsulation:\n"
650 << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
651 << " version : " << p->version() << "\n"
652 << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
653 << p->protocolType() << "\n";
654 if (p->checksumPresent())
655 os << " checksum : 0x" << std::hex << std::setw(4)
656 << std::setfill('0') << p->checksum() << "\n";
661 \section howto_newpacket_advanced Going further
663 \subsection howto_newpacket_advanced_valid Checking the GRE packet for validity
665 We now know how to define packets, but there is more. In this section we will explore the
666 features available to make the packet chaining more flexible. We will show, how to implement
667 more complex logic than simple registry lookup to find the nested packet (the payload) type.
669 In our concrete example, reading the RFC we find there are some restrictions which a GRE packet
670 needs to obey to be considered valid. If the packet is not valid it cannot be parsed and should
671 be dropped. We can't drop it here but if the packet is invalid, we certainly must refrain from
672 trying to parser any payload since we cannot assume the packet to have the format we assume our
675 There are two conditions defined in the RFC which render a GRE packet invalid: If one of the \a
676 reserved0() fields first 5 bits is set or if the version is not 0. We will add a \a valid()
677 check to the parser and utilize this check in the packet type.
679 So first lets update the parser. We will need to change the fields a little bit so we have
680 access to the first 5 bits of \a reserved0. We therefore replace the first three field
684 SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
685 SENF_PARSER_PRIVATE_BITFIELD ( reserved0_5bits_, 5, unsigned );
686 SENF_PARSER_SKIP_BITS ( 7 );
687 SENF_PARSER_BITFIELD_RO ( version, 3, unsigned );
690 We have added an additional private bitfield \a reserved0_5bits_() and we made the \a version()
693 We will now add a simple additional member to the parser:
696 bool valid() const { return version() == 0 && reserved0_5bits_() == 0; }
699 I think, this is quite straight forward: \a valid() will just check the restrictions as defined
702 Now to the packet type. We want to refrain from parsing the payload if the packet is
703 invalid. This is important: If the packet is not valid, we have no idea, whether the payload is
704 what we surmise it to be (if any of the \a reserved0_5bits_() are set, the packet is from an
705 older GRE RFC and the header is quite a bit longer so the payload will be incorrect).
707 So we need to change the logic which is used by the packet library to find the type of the next
708 packet. We have two ways to do this: We keep using the default \c nextPacketType()
709 implementation as provided by the senf::PacketTypeMixin and have our \a nextPacketKey()
710 implementation return a key value which is guaranteed never to be registered in the registry.
712 The more flexible possibility is implementing \c nextPacketType() ourselves. In this case, the
713 first method would suffice, but we will choose to go the second route to show how to write the
714 \c nextPacketType() member. We therefore remove the \c using declaration of \c nextPacketType()
715 and also remove the \a nextPacketKey() implementation. Instead we add the following to the
719 // disabled: using mixin::nextPacketType;
721 factory_t nextPacketType(packet p) { return p->valid() ? lookup(p->protocolType()) : no_factory(); }
724 As we see, this is still quite simple. \c factory_t is provided by senf::PacketTypeBase. For our
725 purpose it is an opaque type which somehow enables the packet library to create a new packet of
726 a specified packet type. The \c factory_t has a special value, \c no_factory() which stands for
727 the absence of any concrete factory. In a boolean context this (and only this) \c factory_t
728 value tests \c false.
730 The \c lookup() member is provided by the senf::PacketTypeMixin. It looks up the key passed as
731 argument in the registry and returns the factory or \c no_factory(), if the key was not found in
734 In this case this is all. But let's elaborate on this example. What if we need to return some
735 specific factory from \a nextPacketType(), e.g. what, if we want to handle the case of
736 transparent ethernet bridging explicitly instead of registering the value in the
737 senf::EtherTypes registry ? Here one way to do this:
740 factory_t nextPacketType(packet p) {
742 if (p->protocolType() == 0x6558) return senf::EthernetPacket::factory();
743 else return lookup(p->protocolType());
745 else return no_factory();
749 As can be seen above, every packet type has a (static) \a factory() member which returns the
750 factory for this type of packet.
753 \subsection howto_newpacket_advanced_init Non-trivial packet initialization
755 Every packet when created is automatically initialized with 0 bytes (all data bytes will be
756 0). In the case of GRE this is enough. But other packets will need other more complex
757 initialization to be performed.
759 Lets just for the sake of experiment assume, the GRE packet would have to set \a version() to 1
760 not 0. In this case, the default initialization would not suffice. It is however very simple to
761 explicitly initialize the packet. The initialization happens within the parser. We just add
764 SENF_PARSER_INIT() { version_() << 1u; }
767 to \c GREPacketParser. For every read-only defined field, the macros automatically define a \e
768 private read-write accessor which may be used internally. This read-write accessor is used here
769 to initialize the value.
772 \section howto_newpacket_final The ultimate GRE packet implementation completed
774 So here we now have \c GREPacket finally complete in all it's glory. First the header file \c
778 #ifndef HH_GREPacket_
779 #define HH_GREPacket_
781 #include <senf/Packets.hh>
783 struct GREPacketParser_OptFields : public senf::PacketParserBase
785 # include SENF_FIXED_PARSER()
787 SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
788 SENF_PARSER_SKIP ( 2 );
790 SENF_PARSER_FINALIZE(GREPacketParser_OptFields);
793 struct GREPacketParser : public senf::PacketParserBase
795 # include SENF_PARSER()
797 SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
798 SENF_PARSER_PRIVATE_BITFIELD ( reserved0_5bits_, 5, unsigned );
799 SENF_PARSER_SKIP_BITS ( 7 );
800 SENF_PARSER_BITFIELD_RO ( version, 3, unsigned );
802 SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
804 SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
805 (senf::VoidPacketParser)
806 (GREPacketParser_OptFields) );
808 typedef version__t version_t;
809 version_t::value_type version() const { return version_(); }
811 bool valid() const { return version() == 0 && reserved0_5bits_() == 0; }
813 typedef GREPacketParser_OptFields::checksum_t checksum_t;
814 checksum_t checksum() const
815 { return optionalFields_().get<1>().checksum(); }
817 void enableChecksum() const { optionalFields_().init<1>(); }
818 void disableChecksum() const { optionalFields_().init<0>(); }
820 SENF_PARSER_FINALIZE(GREPacketParser);
822 checksum_t::value_type calculateChecksum() const;
826 : public senf::PacketTypeBase,
827 public senf::PacketTypeMixin<GREPacketType, EtherTypes>
829 typedef senf::PacketTypeMixin<GREPacketType, EtherTypes> mixin;
830 typedef senf::ConcretePacket<GREPacketType> packet;
831 typedef senf::GREPacketParser parser;
833 using mixin::nextPacketRange;
835 using mixin::initSize;
837 factory_t nextPacketType(packet p)
838 { return p->valid() ? lookup(p->protocolType()) : no_factory(); }
840 static void finalize(packet p) {
841 p->protocolType() << key(p.next(senf::nothrow));
842 if (p->checksumPresent()) p->checksum() << p->calculateChecksum();
845 static void dump(packet p, std::ostream & os);
848 typedef GREPacketType::packet GREPacket;
853 And the implementation file \c GREPacket.cc:
856 #include "GREPacket.hh"
857 #include <senf/Utils/IpChecksum.hh>
858 #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
859 #include <senf/Packets/DefaultBundle/IPv4Packet.hh>
861 SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
862 SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
864 GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const
866 if (!checksumEnabled())
871 cs.feed( i(), i()+4 );
872 // Skip even number of 0 bytes (the 2 bytes checksum field)
873 // cs.feed(0); cs.feed(0);
874 cs.feed( i()+6, data().end() );
879 void GREPacketType::dump(packet p, std::ostream & os)
881 boost::io::ios_all_saver ias(os);
882 os << "General Routing Encapsulation:\n"
883 << " checksum present : " << p->checksumPresent() ? "true" : "false" << "\n"
884 << " version : " << p->version() << "\n"
885 << " protocol type : 0x" << std::hex << std::setw(4) << std::setfill('0')
886 << p->protocolType() << "\n";
887 if (p->checksumPresent())
888 os << " checksum : 0x" << std::hex << std::setw(4)
889 << std::setfill('0') << p->checksum() << "\n";
894 \section howto_newpacket_using Using the newly created GRE packet type
896 The GRE packet is now fully integrated into the packet library framework. For example, we can
897 read GRE packets from a raw INet socket and forward decapsulated Ethernet frames to a packet
902 #include <senf/Packets.hh>
903 #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
904 #include <senf/Socket/Protocols/INet/RawINetProtocol.hh>
905 #include <senf/Socket/Protocols/Raw/PacketSocketHandle.hh>
906 #include "GREPacket.hh"
908 int main(int, char const **)
910 senf::RawV6ClientSocketHandle isock (47u); // 47 = Read GRE packets
911 senf::PacketSocketHandle osock;
915 GREPacket gre (GREPacket::create(senf::noinit));
916 isock.read(gre.data(),0u);
917 if (gre->checksumPresent() && gre->checksum() != gre->calculateChecksum())
918 throw InvalidPacketChainException();
919 osock.write(gre.next<EthernetPacket>().data())
921 catch (senf::TruncatedPacketException & ex) {
922 std::cerr << "Malformed packet dropped\n";
924 catch (senf::InvalidPacketChainException & ex) {
925 std::cerr << "Invalid GRE packet dropped\n";
931 Or we can do the opposite: Read ethernet packets from a \c tap device and send them out GRE
936 #include <senf/Packets.hh>
937 #include <senf/Packets/DefaultBundle/EthernetPacket.hh>
938 #include <senf/Socket/Protocols/INet/RawINetProtocol.hh>
939 #include <senf/Socket/Protocols/Raw/TunTapSocketHandle.hh>
940 #include "GREPacket.hh"
942 int main(int argc, char const ** argv)
945 std::cerr << "Usage: " << argv[0] << " <tunnel endpoint>\n";
949 senf::TapSocketHandle tap ("tap0");
950 senf::ConnectedRawV6ClientSocketHandle osock (47u, senf::INet6SocketAddress(argv[1]));
953 senf::EthernetPacket eth (senf::EthernetPacket::create(senf::noinit));
954 isock.read(eth.data(),0u);
955 GREPacket gre (senf::GREPacket::createBefore(eth));
957 osock.write(gre.data());
963 \section howto_newpacket_further Further reading
965 Lets start with references to the important API's (Use the <i>List of all members</i> link to
966 get the complete API of one of the classes and templates):
968 <table class="senf fixedcolumn">
970 <tr><td>senf::ConcretePacket</td> <td>this is the API provided by the packet handles.</td></tr>
972 <tr><td>senf::PacketData</td> <td>this API provides raw data access accessible via the handles
973 'data' member.</td></tr>
975 <tr><td>senf::PacketParserBase</td> <td>this is the generic parser API. This API is accessible
976 via the packets \c -> operator or via the sub-parsers returned by the field accessors.</td></tr>
980 When implementing new packet's, the following information will be helpful:
982 <table class="senf fixedcolumn">
984 <tr><td>senf::PacketTypeBase</td> <td>here you find a description of the members which need to
985 be implemented to provide a 'packet type'. Most of these members will normally be provided by
986 the mixin helper.</td></tr>
988 <tr><td>senf::PacketTypeMixin</td> <td>here you find all about the packet type mixin and how to
991 <tr><td>\ref packetparser</td> <td>This section describes the packet parser facility.</td></tr>
993 <tr><td>\link packetparsermacros Packet parser macros\endlink</td> <td>A complete list and
994 documentation of all the packet parser macros.</td></tr>
996 <tr><td>\ref parseint, \n \ref parsecollection</td> <td>There are several lists of available
997 reusable packet parsers. However, these lists are not complete as there are other protocol
998 specific reusable parsers (without claiming to be exhaustive: senf::INet4AddressParser,
999 senf::INet6AddressParser, senf::MACAddressParser)</td></tr>
1009 // comment-column: 40
1010 // c-file-style: "senf"
1011 // indent-tabs-mode: nil
1012 // ispell-local-dictionary: "american"
1013 // compile-command: "scons -u doc"
1016 // vim:filetype=doxygen:textwidth=100: