RFC 5444 handler library
Copyright (c) 2010 Henning Rogge <hrogge@googlemail.com>

The rfc 5444 library is a combined parser and generator for the
'Generalized Mobile Ad Hoc Network (MANET) Packet/Message Format'.
The library is split into a separate reader and writer part, both
of them use a generic avl-tree and double-linked list implementation.

The reader and writer allow multiple independent code blocks to read
overlapping parts of a RFC 5444 data stream and assemble packets and
messages together. This allow API users to separate different sub-
protocol in their codebase (e.g. NHDP and OLSRv2).


=======================
    RFC 5444 reader
=======================

1) general information
1.1) using the reader
1.2) rfc5444_reader_tlvblock_context
1.3) rfc5444_reader_tlvblock_entry
1.4) order of callbacks while parsing a rfc5444 stream
2) block callback
2.1) setting up a Block Callback
2.2) implementing the block callback
3) stream callbacks
4) message forwarding


1) General information
**********************

The basic blocks of information of rfc5444 are addresses of 1 to 16
bytes and TLVs. TLV is a shortcut for type-length-value. Each piece
of data which is not included in packet, message or address header
has a type and a length field, which allows a parser which does not
know this TLV to skip to the next TLV without being forced to stop
parsing the data stream.
 
The RFC 5444 reader is based on the concept of tlv-block consumers
as the basic entities for reading RFC 5444. TLV-blocks can exist on
three different levels of hierarchy in RFC 5444:

  * packet TLVs
  * message TLVs
  * address TLVs

Each consumer registers on one of this three levels and can use two
different kinds of callbacks to learn about the relevant TLV-block and
its context. Both group of callbacks can be used in the same consumer.

The first callback is the block_callback. It allows the consumer to
define a set of TLVs it's interested in. The parser collects all
TLV data defined in the set and calls the block_callback.
Hence the processing is block-oriented.

The other callbacks allow a more stream oriented handling of rfc5444
data:

 * start_callback
 * tlv_callback
 * end_callback

For each consumer context (packet, message or address) the parser calls
the start_callback first, then it calls the tlv_callback for each TLV
in the context and finally the end_callback.

Each consumer has a priority, which defines the order in which the
parser calls the consumers. Early consumers can tell the parser to
stop processing the current context or the whole packet, which allows
creating of security or semantic checking consumers.

Both block and stream callbacks return two kind of data structures,
rfc5444_reader_tlvblock_context and rfc5444_reader_tlvblock_entry. The block
callback initializes a pointer to the second one for each
rfc5444_reader_tlvblock_consumer_entry it finds in the rfc5444 stream.


1,1) Using the reader
*********************

The rfc5444 reader is very simple to use.

First the user has to allocate a rfc5444 reader context with
rfc5444_reader_init(). All functions of the reader require a pointer to this
context as a parameter.
If you don't need the reader anymore, call rfc5444_reader_cleanup() to
free all allocated memory of the reader including the reader context
itself.

After allocating the context you can register and unregister all
necessary callbacks to the reader, as long as you do this outside
of the reader callbacks itself!

To parse binary rfc5444, just call rfc5444_reader_handle_packet(). 

Example:
  (...)
  struct rfc5444_reader *context;

  context = rfc5444_reader_init();
  assert(context);
  (...)



1.2) rfc5444_reader_tlvblock_context
********************************

There is a second context: the tlvblock context tells the callback about the
enclosing packet header, message header and address entry. It contains all
relevant fields of the parsed headers.

The first field "type" of the context is an enum that defines the scope of the
context.

A type CONTEXT_PACKET applies to callbacks for packet scope and packet tlvs,
which means that one the fields relevant for packets are initialized. This
context will contain the packet version, it's flags field and (if available)
the packet sequence number.

A type CONTEXT_MESSAGE applies to callbacks for message scope and tlvs. It
contains all the data of the packet context in addition to the relevant fields
of the message header (msg type, flags, address length and the four optional
fields hopcount, hoplimit, originator address and sequence number).

The last type CONTEXT_ADDRESS applies to an address and it's tlvs.  It contains
all data of packet and message context plus the address and its prefix length.

The whole struct is read-only, DO NOT MODIFY the fields inside
a callback.


1.3) rfc5444_reader_tlvblock_entry
******************************

The tlvblock entry contains the data about a single tlv. It
contains the type of the tlv (including the extension type),
the raw flags field, the length of the value, a pointer to
the binary value (NULL if length is zero) and the index fields
for address block TLVs.

The whole struct is read-only, DO NOT MODIFY the fields inside
a callback.


1.4) Order of callbacks while parsing a rfc5444 stream
*******************************************************

The total order of callbacks can be described as:

 * packet consumer "order 1"
   - start_callback
   - tlv_callback (tlv_1)
   - ...
   - tlv_callback (tlv_n)
   - block_callback (tlvs)
 * packet_consumer "order 2"
   - start_callback
   - tlv_callback (tlv_1)
   - ...
   - tlv_callback (tlv_n)
   - block_callback (tlvs)
 
   * message_consumer "order 1"
     - start_callback
     - tlv_callback (tlv_1)
     - ...
     - tlv_callback (tlv_n)
     - block_callback (tlvs)

   * address_consumer "order 1"
     * address addr_1
       - start_callback (addr_1)
       - tlv_callback (addr_1, tlv_1)
       - ...
       - tlv_callback (addr_1, tlv_n)
       - block_callback (tlvs)
       - end_callback (addr_1)
   
     * address addr_2
       - start_callback (addr_2)
       - tlv_callback (addr_2, tlv_1)
       - ...
       - tlv_callback (addr_2, tlv_n)
       - block_callback (tlvs)
       - end_callback (addr_2)
 
   * message_consumer "order 1"
     - end_callback
  
   * message_consumer "order 2"
     - start_callback
     - tlv_callback (tlv_1)
     - ...
     - tlv_callback (tlv_n)
     - block_callback (tlvs)
     - end_callback

   * address_consumer "order 3"
     * address addr_1
       - start_callback (addr_1)
       - tlv_callback (addr_1, tlv_1)
       - ...
       - tlv_callback (addr_1, tlv_n)
       - block_callback (tlvs)
       - end_callback (addr_1)
   
     * address addr_2
       - start_callback (addr_2)
       - tlv_callback (addr_2, tlv_1)
       - ...
       - tlv_callback (addr_2, tlv_n)
       - block_callback (tlvs)
       - end_callback (addr_2)
 
 * packet_consumer "order 2"
   - end_callback
 
 * packet_consumer "order 1"
   - end_callback

 
2.) Block callback
******************

The block callback allows you to define a filter for a specific context
(packet, message or address) and get data about a fixed number of TLVs
your callback needs to know about.

This makes writing multiple TLV callbacks just to collect the different
TLVs from a message or address just to store them in a temporary data
structure unnecessary.


2.1) Setting up a Block Callback
********************************

The first option to parse rfc5444 data is the block_callback.
The block_callback is initialized by an array of
rfc5444_reader_tlvblock_consumer_entry objects.

struct rfc5444_reader_tlvblock_consumer_entry consumer[] = {
    { .type = 4 },
    { .type = 1, .match_type_ext = true, .type_ext = 1 },
    { .type = 2, .mandatory = true },
    { .type = 6, .copy_value = &value, .copy_value_maxlen = sizeof(value) }
};

...
    struct rfc5444_reader *context;
    struct rfc5444_reader_tlvblock_consumer *consumer;
    ....
    
    consumer = rfc5444_reader_add_message_consumer(context,
      consumer, ARRAYSIZE(consumer),
      2, /* msg type */
      1  /* order */);
    consumer->block_callback = my_block_callback;

Each entry of the array represents a single TLV type of
the context. There are three different optional settings
in addition to the tlv type, which can be combined:

 * mandatory (boolean)
 * match_type_ext (boolean) and type_ext (uint8_t)
 * copy_value (void *) and copy_value_maxlen (size_t)

By setting mandatory to 'true' in an entry of the
rfc5444_reader_tlvblock_consumer_entry array the consumer tells the parser that
the TLV is mandatory. The parser keeps track if all mandatory TLVs
are available and tells the consumer this by a callback parameter.

Setting match_type_ext to 'true' let the consumer specify which extended
type of the TLV is relevant for this entry. If not set the type_ext
variable is ignored and the first TLV with a matching type (and any kind
of extended type) will be used for the block callback.

Copy_value tells the parser to copy the value of the TLV to a private
buffer of the consumer. The copy_value_maxlen variable has to be set
to the maximum number of bytes available in the private buffer to
prevent a buffer overflow.


2.2) Implementing the block callback
***********************************

The block callback has three parameters. The first is a pointer to the
tlvblock consumer object returned by the register function. The second
one is a pointer to a rfc5444_reader_tlvblock_context object, which contains the
packet sequence number, the decoded content of the message header (for
message and address consumers) and the current address (only for address
consumers). The last parameter mandatory_missing is true if one of the
tlv entries marked as mandatory is missing.

enum rfc5444_result
callback(struct rfc5444_reader_tlvblock_consumer *consumer,
      struct rfc5444_reader_tlvblock_context *context,
      bool mandatory_missing) {
    ...
    return RFC5444_OKAY;
}

For each tlv in the block with a corresponding array entry, the parser
initializes a rfc5444_reader_tlvblock_entry pointer in the entry which contains
all data about the original tlv, including type(ext), value and/or
indices.

The default return value of the block callback is RFC5444_OKAY, which tells
the parser that everything is right and the next callback should be called.

See README.dropcontext for other return values.


3) Stream callbacks
*******************

The stream callbacks are a group of three callbacks that are similar
to a SAX XML parser. The consumer gets one callback when its
context (the packet, message or address) starts, one callback for each
TLV inside the context (with a pointer to a rfc5444_reader_tlvblock_context and
a rfc5444_reader_tlvblock_entry object) and one callback when the context ends.

enum rfc5444_result
my_start_callback(struct rfc5444_reader_tlvblock_consumer *,
       struct rfc5444_reader_tlvblock_context *context) {
    ...
    return RFC5444_OKAY;
}

enum rfc5444_result
my_tlv_callback(struct rfc5444_reader_tlvblock_entry *,
      struct rfc5444_reader_tlvblock_context *context) {
    ...
    return RFC5444_OKAY;
}

enum rfc5444_result
my_end_callback(struct rfc5444_reader_tlvblock_consumer *, 
      struct rfc5444_reader_tlvblock_context *context,
      bool dropped);
    ...
    return RFC5444_OKAY;
}

...
    struct rfc5444_reader *context;
    struct rfc5444_reader_tlvblock_consumer *consumer;
    ....
    
    consumer = rfc5444_reader_add_message_consumer(context,
      consumer, ARRAYSIZE(consumer),
      2, /* msg type */
      1  /* order */);
    consumer->start_callback = my_start_callback;
    consumer->tlv_callback = my_tlv_callback;
    consumer->end_callback = my_end_callback;

These three callbacks allow the consumer to analyze the whole tlv-block
stream in a linear order without missing duplicate TLVs or unknown one,
but are more complex to use for most protocol implementations.

The end callback has an additional boolean parameter called drop, which tells
the callback that the context has been dropped by an earlier callback.

The default return value of this callbacks is RFC5444_OKAY, which tells
the parser that everything is right and the next callback should be called.

See README.dropcontext for other return values.


4) message forwarding
*********************

Each time after a message has been parsed by all callbacks the parser
checks if it should be forwarded to other nodes (hoplimit must be present
in the header for this). If yes the message will be forwarded by calling
the forward_message() callback of the reader context.
