Merge branch 'develop' into staging staging
authorHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Tue, 15 May 2018 06:37:09 +0000 (08:37 +0200)
committerHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Tue, 15 May 2018 06:37:09 +0000 (08:37 +0200)
34 files changed:
1  2 
apps/dlep-radio/CMakeLists.txt
apps/dlep-router/CMakeLists.txt
apps/olsrd2-dlep/CMakeLists.txt
apps/olsrd2/CMakeLists.txt
include/oonf/base/oonf_layer2.h
include/oonf/generic/dlep/dlep_session.h
include/oonf/generic/dlep/radio/dlep_radio_session.h
include/oonf/generic/layer2_export/layer2_export.h
include/oonf/generic/layer2_import/layer2_import.h
include/oonf/libcommon/netaddr.h
include/oonf/nhdp/nhdp/nhdp_domain.h
include/oonf/olsrv2/olsrv2_l2import/olsrv2_l2import.h
src/base/oonf_layer2.c
src/generic/CMakeLists.txt
src/generic/dlep/dlep_session.c
src/generic/dlep/dlep_writer.c
src/generic/dlep/ext_base_ip/ip.c
src/generic/dlep/ext_lid/lid.c
src/generic/dlep/radio/dlep_radio.c
src/generic/dlep/router/dlep_router.c
src/generic/layer2_export/CMakeLists.txt
src/generic/layer2_export/layer2_export.c
src/generic/layer2_export/olsrv2_l2import.c
src/generic/layer2_generator/layer2_generator.c
src/generic/layer2_import/CMakeLists.txt
src/generic/layer2_import/lan_import.c
src/generic/layer2_import/layer2_import.c
src/generic/layer2info/layer2info.c
src/generic/nl80211_listener/nl80211_get_station_dump.c
src/libcommon/netaddr.c
src/nhdp/nhdp/nhdp_domain.c
src/olsrv2/CMakeLists.txt
src/olsrv2/olsrv2_l2import/CMakeLists.txt
src/olsrv2/olsrv2_l2import/olsrv2_l2import.c

Simple merge
Simple merge
Simple merge
Simple merge
index 0000000,744e8d7..3bf6348
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,897 +1,952 @@@
 -  uint64_t last_seen;
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #ifndef OONF_LAYER2_H_
+ #define OONF_LAYER2_H_
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/os_interface.h>
+ /*! subsystem identifier */
+ #define OONF_LAYER2_SUBSYSTEM "layer2"
+ /*! memory class for layer2 neighbor */
+ #define LAYER2_CLASS_NEIGHBOR "layer2_neighbor"
+ /*! memory class for layer2 network */
+ #define LAYER2_CLASS_NETWORK "layer2_network"
+ /*! memory class for layer2 destination */
+ #define LAYER2_CLASS_DESTINATION "layer2_destination"
+ /*! memory class for layer2 network address */
+ #define LAYER2_CLASS_NETWORK_ADDRESS "layer2_network_address"
+ /*! memory class for layer2 neighbor address */
+ #define LAYER2_CLASS_NEIGHBOR_ADDRESS "layer2_neighbor_address"
+ /*! memory class for layer2 tracking of next link-id per neighbor */
+ #define LAYER2_CLASS_LID "layer2_lid"
+ enum {
+   /*! maximum length of link id for layer2 neighbors */
+   OONF_LAYER2_MAX_LINK_ID = 16,
+ };
+ /* configuration Macros for Layer2 keys */
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 interface keys
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_VALIDATE_LAYER2_NET_KEY(p_name, p_def, p_help, args...)                                                    \
+   CFG_VALIDATE_CHOICE_CB_ARG(p_name, p_def, p_help, oonf_layer2_cfg_get_l2net_key, OONF_LAYER2_NET_COUNT, NULL, ##args)
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 interface keys
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_VALIDATE_LAYER2_NEIGH_KEY(p_name, p_def, p_help, args...)                                                  \
+   CFG_VALIDATE_CHOICE_CB_ARG(                                                                                          \
+     p_name, p_def, p_help, oonf_layer2_cfg_get_l2neigh_key, OONF_LAYER2_NEIGH_COUNT, NULL, ##args)
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 data comparators
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_VALIDATE_LAYER2_COMP(p_name, p_def, p_help, args...)                                                       \
+   CFG_VALIDATE_CHOICE_CB_ARG(                                                                                          \
+     p_name, p_def, p_help, oonf_layer2_cfg_get_l2comp, OONF_LAYER2_DATA_CMP_COUNT, NULL, ##args)
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 interface keys
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_MAP_CHOICE_L2NET(p_reference, p_field, p_name, p_def, p_help, args...)                                     \
+   CFG_MAP_CHOICE_CB_ARG(                                                                                               \
+     p_reference, p_field, p_name, p_def, p_help, oonf_layer2_cfg_get_l2net_key, OONF_LAYER2_NET_COUNT, NULL, ##args)
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 interface keys
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_MAP_CHOICE_L2NEIGH(p_reference, p_field, p_name, p_def, p_help, args...)                                   \
+   CFG_MAP_CHOICE_CB_ARG(p_reference, p_field, p_name, p_def, p_help, oonf_layer2_cfg_get_l2neigh_key,                  \
+     OONF_LAYER2_NEIGH_COUNT, NULL, ##args)
+ /**
+  * Creates a cfg_schema_entry for a parameter that can be choosen
+  * from the layer2 data comparators
+  * @param p_name parameter name
+  * @param p_def parameter default value
+  * @param p_help help text for configuration entry
+  * @param args variable list of additional arguments
+  */
+ #define CFG_MAP_CHOICE_L2COMP(p_reference, p_field, p_name, p_def, p_help, args...)                                    \
+   CFG_MAP_CHOICE_CB_ARG(                                                                                               \
+     p_reference, p_field, p_name, p_def, p_help, oonf_layer2_cfg_get_l2comp, OONF_LAYER2_DATA_CMP_COUNT, NULL, ##args)
+ /**
+  * priorities of layer2 originators
+  */
+ enum oonf_layer2_origin_priority
+ {
+   OONF_LAYER2_ORIGIN_UNKNOWN = 0,
+   OONF_LAYER2_ORIGIN_UNRELIABLE = 10,
+   OONF_LAYER2_ORIGIN_CONFIGURED = 20,
+   OONF_LAYER2_ORIGIN_RELIABLE = 30,
+ };
+ /**
+  * Origin for layer2 data
+  */
+ struct oonf_layer2_origin {
+   const char *name;
+   /*! true if data will be constantly updated by a plugin */
+   bool proactive;
+   /*! priority of this originator */
+   enum oonf_layer2_origin_priority priority;
+   /*! true if this originator creates l2neighbor LID entries */
+   bool lid;
+   /* index number of the originator for LID creation */
+   uint32_t lid_index;
+   /*! node for tree of originators */
+   struct avl_node _node;
+ };
+ enum oonf_layer2_data_type
+ {
+   OONF_LAYER2_NO_DATA,
+   OONF_LAYER2_INTEGER_DATA,
+   OONF_LAYER2_BOOLEAN_DATA,
+   OONF_LAYER2_NETWORK_DATA,
+   OONF_LAYER2_DATA_TYPE_COUNT,
+ };
+ union oonf_layer2_value {
+   int64_t integer;
+   bool boolean;
+   struct netaddr addr;
+ };
+ /**
+  * Single data entry of layer2 network or neighbor
+  */
+ struct oonf_layer2_data {
+   /*! data value */
+   union oonf_layer2_value _value;
+   /*! type of data contained in this element */
+   enum oonf_layer2_data_type _type;
+   /*! layer2 originator id */
+   const struct oonf_layer2_origin *_origin;
+ };
+ /**
+  * Metadata of layer2 data entry for automatic processing
+  */
+ struct oonf_layer2_metadata {
+   /*! type of data */
+   const char key[16];
+   /*! data type */
+   enum oonf_layer2_data_type type;
+   /*! unit (bit/s, byte, ...) */
+   const char unit[8];
+   /*! number of fractional digits of the data */
+   const int fraction;
+ };
+ /**
+  * Comparator options for layer2 data
+  */
+ enum oonf_layer2_data_comparator_type
+ {
+   OONF_LAYER2_DATA_CMP_EQUALS,
+   OONF_LAYER2_DATA_CMP_NOT_EQUALS,
+   OONF_LAYER2_DATA_CMP_LESSER,
+   OONF_LAYER2_DATA_CMP_LESSER_OR_EQUALS,
+   OONF_LAYER2_DATA_CMP_GREATER,
+   OONF_LAYER2_DATA_CMP_GREATER_OR_EQUALS,
+   OONF_LAYER2_DATA_CMP_COUNT,
+   OONF_LAYER2_DATA_CMP_ILLEGAL = -1,
+ };
+ /**
+  * list of layer2 network metrics
+  */
+ enum oonf_layer2_network_index
+ {
+   /*! primary center frequency */
+   OONF_LAYER2_NET_FREQUENCY_1,
+   /*! optional secondary center frequency */
+   OONF_LAYER2_NET_FREQUENCY_2,
+   /*! primary bandwidth */
+   OONF_LAYER2_NET_BANDWIDTH_1,
+   /*! optional secondary bandwidth */
+   OONF_LAYER2_NET_BANDWIDTH_2,
+   /*! noise level in dBm */
+   OONF_LAYER2_NET_NOISE,
+   /*! total time in ns the channel was active */
+   OONF_LAYER2_NET_CHANNEL_ACTIVE,
+   /*! total time in ns the channel was busy */
+   OONF_LAYER2_NET_CHANNEL_BUSY,
+   /*! total time in ns the channel was receiving */
+   OONF_LAYER2_NET_CHANNEL_RX,
+   /*! total time in ns the channel was transmitting */
+   OONF_LAYER2_NET_CHANNEL_TX,
+   /*! outgoing broadcast bitrate in bit/s */
+   OONF_LAYER2_NET_TX_BC_BITRATE,
+   /*! maixmum size of an ethernet/IP packet the router is allowed to send */
+   OONF_LAYER2_NET_MTU,
+   /*! true if unicast traffic is necessary for ratecontrol */
+   OONF_LAYER2_NET_MCS_BY_PROBING,
+   /*! true if interface does not support incoming broadcast/multicast */
+   OONF_LAYER2_NET_RX_ONLY_UNICAST,
+   /*! true if interface does not support incoming broadcast/multicast */
+   OONF_LAYER2_NET_TX_ONLY_UNICAST,
+   /*! true if radio provides multihop forwarding transparently */
+   OONF_LAYER2_NET_RADIO_MULTIHOP,
+   /*!
+    * true if first frequency is uplink, second is downlink.
+    * false if reported frequencies can be used for both reception/transmission
+    */
+   OONF_LAYER2_NET_BAND_UP_DOWN,
+   /*! number of layer2 network metrics */
+   OONF_LAYER2_NET_COUNT,
+ };
+ /**
+  * list with types of layer2 networks
+  */
+ enum oonf_layer2_network_type
+ {
+   OONF_LAYER2_TYPE_UNDEFINED,
+   OONF_LAYER2_TYPE_WIRELESS,
+   OONF_LAYER2_TYPE_ETHERNET,
+   OONF_LAYER2_TYPE_TUNNEL,
+   OONF_LAYER2_TYPE_COUNT,
+ };
+ /**
+  * list of layer2 neighbor metrics
+  */
+ enum oonf_layer2_neighbor_index
+ {
+   /*! outgoing signal in milli dBm */
+   OONF_LAYER2_NEIGH_TX_SIGNAL,
+   /*! incoming signal in milli dBm */
+   OONF_LAYER2_NEIGH_RX_SIGNAL,
+   /*! outgoing bitrate in bit/s */
+   OONF_LAYER2_NEIGH_TX_BITRATE,
+   /*! incoming bitrate in bit/s */
+   OONF_LAYER2_NEIGH_RX_BITRATE,
+   /*! maximum possible outgoing bitrate in bit/s */
+   OONF_LAYER2_NEIGH_TX_MAX_BITRATE,
+   /*! maximum possible incoming bitrate in bit/s */
+   OONF_LAYER2_NEIGH_RX_MAX_BITRATE,
+   /*! total number of transmitted bytes */
+   OONF_LAYER2_NEIGH_TX_BYTES,
+   /*! total number of received bytes */
+   OONF_LAYER2_NEIGH_RX_BYTES,
+   /*! total number of transmitted frames */
+   OONF_LAYER2_NEIGH_TX_FRAMES,
+   /*! total number of received frames */
+   OONF_LAYER2_NEIGH_RX_FRAMES,
+   /*! average outgoing throughput in bit/s */
+   OONF_LAYER2_NEIGH_RX_THROUGHPUT,
+   /*! average incoming throughput in bit/s */
+   OONF_LAYER2_NEIGH_TX_THROUGHPUT,
+   /*! total number of frame retransmission of other radio*/
+   OONF_LAYER2_NEIGH_RX_RETRIES,
+   /*! total number of frame retransmission */
+   OONF_LAYER2_NEIGH_TX_RETRIES,
+   /*! total number of failed frame receptions */
+   OONF_LAYER2_NEIGH_RX_FAILED,
+   /*! total number of failed frame transmissions */
+   OONF_LAYER2_NEIGH_TX_FAILED,
+   /*! relative transmission link quality (0-100) */
+   OONF_LAYER2_NEIGH_TX_RLQ,
+   /*! relative receiver link quality (0-100) */
+   OONF_LAYER2_NEIGH_RX_RLQ,
+   /*! incoming broadcast bitrate in bit/s */
+   OONF_LAYER2_NEIGH_RX_BC_BITRATE,
+   /*! incoming broadcast loss in 1/1000 */
+   OONF_LAYER2_NEIGH_RX_BC_LOSS,
+   /*! latency to neighbor in microseconds */
+   OONF_LAYER2_NEIGH_LATENCY,
+   /*! available resources of radio (0-100) */
+   OONF_LAYER2_NEIGH_RESOURCES,
+   /*! number of radio hops to neighbor, only available for multihop capable radios */
+   OONF_LAYER2_NEIGH_RADIO_HOPCOUNT,
+   /*!
+    *IP hopcount (including ethernet between radio and router) to neighbor router,
+    * only available for multihop capable radios
+    */
+   OONF_LAYER2_NEIGH_IP_HOPCOUNT,
+   /*! number of neighbor metrics */
+   OONF_LAYER2_NEIGH_COUNT,
+ };
+ /**
+  * representation of a layer2 interface
+  */
+ struct oonf_layer2_net {
+   /*! name of local interface */
+   char name[IF_NAMESIZE];
+   /*! optional identification string */
+   char if_ident[64];
+   /*! interface type */
+   enum oonf_layer2_network_type if_type;
+   /*! interface data is delivered by DLEP */
+   bool if_dlep;
+   /*! interface listener to keep track of events and local mac address */
+   struct os_interface_listener if_listener;
+   /*! tree of remote neighbors */
+   struct avl_tree neighbors;
+   /*! tree of IP addresses/prefixes of local radio/modem */
+   struct avl_tree local_peer_ips;
+   /*! global tree of all remote neighbor IPs */
+   struct avl_tree remote_neighbor_ips;
+   /*! absolute timestamp when network has been active last */
+   uint64_t last_seen;
+   /*! network wide layer 2 data */
+   struct oonf_layer2_data data[OONF_LAYER2_NET_COUNT];
+   /*! default values of neighbor layer2 data */
+   struct oonf_layer2_data neighdata[OONF_LAYER2_NEIGH_COUNT];
+   /*! node to hook into global l2network tree */
+   struct avl_node _node;
+ };
+ /**
+  * IP addresses that are attached to a local radio/modem
+  */
+ struct oonf_layer2_peer_address {
+   /*! ip address attached to a local radio/modem */
+   struct netaddr ip;
+   /*! backlink to layer2 network */
+   struct oonf_layer2_net *l2net;
+   /*! origin of this address */
+   const struct oonf_layer2_origin *origin;
+   /*! node for global tree of network IP addresses */
+   struct avl_node _global_node;
+   /*! node for tree of ip addresses in network */
+   struct avl_node _net_node;
+ };
+ /**
+  * unique key of a layer2 neighbor
+  */
+ struct oonf_layer2_neigh_key {
+   /*! mac address of the neighbor */
+   struct netaddr addr;
+   /*! length of link id in octets */
+   uint8_t link_id_length;
+   /*! stored link id of neighbor (padded with zero bytes) */
+   uint8_t link_id[OONF_LAYER2_MAX_LINK_ID];
+ };
+ /**
+  * String buffer for text representation of neighbor key
+  */
+ union oonf_layer2_neigh_key_str {
+   struct netaddr_str nbuf;
+   char buf[sizeof(struct netaddr_str) + 5 + 16*2];
+ };
++enum oonf_layer2_neigh_mods {
++  OONF_LAYER2_NEIGH_MODIFY_NONE       = 0,
++  OONF_LAYER2_NEIGH_MODIFY_NEXTHOP_V4 = 1<<0,
++  OONF_LAYER2_NEIGH_MODIFY_NEXTHOP_V6 = 1<<1,
++  OONF_LAYER2_NEIGH_MODIFY_LASTSEEN   = 1<<2,
++};
++
+ /**
+  * representation of a remote layer2 neighbor
+  */
+ struct oonf_layer2_neigh {
+   /*! remote mac address of neighbor */
+   struct oonf_layer2_neigh_key key;
+   /*! back pointer to layer2 network */
+   struct oonf_layer2_net *network;
++  /*! fields modified since last commit */
++  enum oonf_layer2_neigh_mods modified;
++
++  /* (linklocal) ip address to read neighbor with IPv4 */
++  struct netaddr _next_hop_v4;
++
++  /* (linklocal) ip address to read neighbor with IPv6 */
++  struct netaddr _next_hop_v6;
++
+   /*! tree of proxied destinations */
+   struct avl_tree destinations;
+   /*! tree of IP addresses/prefixes of remote neighbor router */
+   struct avl_tree remote_neighbor_ips;
+   /*! absolute timestamp when neighbor has been active last */
++  uint64_t _last_seen;
+   /*! neigbor layer 2 data */
+   struct oonf_layer2_data data[OONF_LAYER2_NEIGH_COUNT];
+   /*! node to hook into tree of layer2 network */
+   struct avl_node _node;
+ };
+ /**
+  * IP addresses that are attached to a remote router
+  */
+ struct oonf_layer2_neighbor_address {
+   /*! ip address attached to a remote router */
+   struct netaddr ip;
+   /*! backlink to layer2 neighbor*/
+   struct oonf_layer2_neigh *l2neigh;
+   /*! origin of this address */
+   const struct oonf_layer2_origin *origin;
+   /*! (interface) global tree of neighbor IP addresses */
+   struct avl_node _net_node;
+   /*! node for tree of ip addresses */
+   struct avl_node _neigh_node;
+ };
+ /**
+  * representation of a bridged MAC address behind a layer2 neighbor
+  */
+ struct oonf_layer2_destination {
+   /*! proxied mac address behind a layer2 neighbor */
+   struct netaddr destination;
+   /*! back pointer to layer2 neighbor */
+   struct oonf_layer2_neigh *neighbor;
+   /*! origin of this proxied address */
+   const struct oonf_layer2_origin *origin;
+   /*! node to hook into tree of layer2 neighbor */
+   struct avl_node _node;
+ };
+ struct oonf_layer2_lid {
+   struct netaddr mac;
+   uint32_t next_id;
+   struct avl_node _node;
+ };
+ EXPORT void oonf_layer2_origin_add(struct oonf_layer2_origin *origin);
+ EXPORT void oonf_layer2_origin_remove(struct oonf_layer2_origin *origin);
+ EXPORT int oonf_layer2_data_parse_string(
+   union oonf_layer2_value *value, const struct oonf_layer2_metadata *meta, const char *input);
+ EXPORT const char *oonf_layer2_data_to_string(
+   char *buffer, size_t length, const struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, bool raw);
+ EXPORT bool oonf_layer2_data_set(struct oonf_layer2_data *data, const struct oonf_layer2_origin *origin,
+   enum oonf_layer2_data_type type, const union oonf_layer2_value *input);
+ EXPORT bool oonf_layer2_data_compare(const union oonf_layer2_value *left, const union oonf_layer2_value *right,
+   enum oonf_layer2_data_comparator_type comparator, enum oonf_layer2_data_type data_type);
+ EXPORT enum oonf_layer2_data_comparator_type oonf_layer2_data_get_comparator(const char *);
+ EXPORT const char *oonf_layer2_data_get_comparator_string(enum oonf_layer2_data_comparator_type type);
+ EXPORT const char *oonf_layer2_data_get_type_string(enum oonf_layer2_data_type type);
+ EXPORT struct oonf_layer2_net *oonf_layer2_net_add(const char *ifname);
+ EXPORT bool oonf_layer2_net_remove(struct oonf_layer2_net *, const struct oonf_layer2_origin *origin);
+ EXPORT bool oonf_layer2_net_cleanup(
+   struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin, bool cleanup_neigh);
+ EXPORT bool oonf_layer2_net_commit(struct oonf_layer2_net *);
+ EXPORT void oonf_layer2_net_relabel(struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *new_origin,
+   const struct oonf_layer2_origin *old_origin);
+ EXPORT struct oonf_layer2_peer_address *oonf_layer2_net_add_ip(
+   struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin, const struct netaddr *ip);
+ EXPORT int oonf_layer2_net_remove_ip(struct oonf_layer2_peer_address *ip, const struct oonf_layer2_origin *origin);
+ EXPORT struct oonf_layer2_neighbor_address *oonf_layer2_net_get_best_neighbor_match(const struct netaddr *addr);
+ EXPORT struct avl_tree *oonf_layer2_net_get_remote_ip_tree(void);
+ EXPORT int oonf_layer2_neigh_generate_lid(struct oonf_layer2_neigh_key *, struct oonf_layer2_origin *origin, const struct netaddr *mac);
+ EXPORT struct oonf_layer2_neigh *oonf_layer2_neigh_add_lid(struct oonf_layer2_net *, const struct oonf_layer2_neigh_key *key);
+ EXPORT bool oonf_layer2_neigh_cleanup(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin);
+ EXPORT bool oonf_layer2_neigh_remove(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin);
+ EXPORT bool oonf_layer2_neigh_commit(struct oonf_layer2_neigh *l2neigh);
+ EXPORT void oonf_layer2_neigh_relabel(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *new_origin,
+   const struct oonf_layer2_origin *old_origin);
++EXPORT int oonf_layer2_neigh_set_nexthop(struct oonf_layer2_neigh *neigh, const struct netaddr *nexthop);
+ EXPORT struct oonf_layer2_neighbor_address *oonf_layer2_neigh_add_ip(
+   struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin, const struct netaddr *ip);
+ EXPORT int oonf_layer2_neigh_remove_ip(
+   struct oonf_layer2_neighbor_address *ip, const struct oonf_layer2_origin *origin);
+ EXPORT struct oonf_layer2_destination *oonf_layer2_destination_add(
+   struct oonf_layer2_neigh *l2neigh, const struct netaddr *destination, const struct oonf_layer2_origin *origin);
+ EXPORT void oonf_layer2_destination_remove(struct oonf_layer2_destination *);
+ EXPORT struct oonf_layer2_data *oonf_layer2_neigh_query(
+   const char *ifname, const struct netaddr *l2neigh, enum oonf_layer2_neighbor_index idx, bool get_default);
+ EXPORT struct oonf_layer2_data *oonf_layer2_neigh_add_path(
+   const char *ifname, const struct netaddr *l2neigh_addr, enum oonf_layer2_neighbor_index idx);
+ EXPORT struct oonf_layer2_data *oonf_layer2_neigh_get_data(
+   struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx);
+ EXPORT const struct oonf_layer2_metadata *oonf_layer2_neigh_metadata_get(enum oonf_layer2_neighbor_index);
+ EXPORT const struct oonf_layer2_metadata *oonf_layer2_net_metadata_get(enum oonf_layer2_network_index);
+ EXPORT const char *oonf_layer2_cfg_get_l2net_key(size_t index, const void *unused);
+ EXPORT const char *oonf_layer2_cfg_get_l2neigh_key(size_t index, const void *unused);
+ EXPORT const char *oonf_layer2_cfg_get_l2comp(size_t index, const void *unused);
+ EXPORT const char *oonf_layer2_net_get_type_name(enum oonf_layer2_network_type);
+ EXPORT struct avl_tree *oonf_layer2_get_net_tree(void);
+ EXPORT struct avl_tree *oonf_layer2_get_origin_tree(void);
+ EXPORT int oonf_layer2_avlcmp_neigh_key(const void *p1, const void *p2);
+ EXPORT const char *oonf_layer2_neigh_key_to_string(union oonf_layer2_neigh_key_str *buf,
+     const struct oonf_layer2_neigh_key *key, bool show_mac);
+ /**
+  * Checks if a layer2 originator is registered
+  * @param origin originator
+  * @return true if registered, false otherwise
+  */
+ static INLINE bool
+ oonf_layer2_origin_is_added(const struct oonf_layer2_origin *origin) {
+   return avl_is_node_added(&origin->_node);
+ }
+ /**
+  * Get a layer-2 interface object from the database
+  * @param ifname name of interface
+  * @return layer-2 addr object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_net *
+ oonf_layer2_net_get(const char *ifname) {
+   struct oonf_layer2_net *l2net;
+   return avl_find_element(oonf_layer2_get_net_tree(), ifname, l2net, _node);
+ }
+ /**
+  * Get a layer-2 local peer ip address object from the database
+  * @param l2net layer-2 network/interface object
+  * @param addr ip address of local radio/modem
+  * @return layer-2 ip address object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_peer_address *
+ oonf_layer2_net_get_local_ip(const struct oonf_layer2_net *l2net, const struct netaddr *addr) {
+   struct oonf_layer2_peer_address *l2ip;
+   return avl_find_element(&l2net->local_peer_ips, addr, l2ip, _net_node);
+ }
+ /**
+  * Get a layer-2 ip address object from the database
+  * @param l2net layer-2 network object
+  * @param addr ip address of remote router
+  * @return layer-2 ip address object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_neighbor_address *
+ oonf_layer2_net_get_remote_ip(const struct oonf_layer2_net *l2net, const struct netaddr *addr) {
+   struct oonf_layer2_neighbor_address *l2ip;
+   return avl_find_element(&l2net->remote_neighbor_ips, addr, l2ip, _net_node);
+ }
+ static INLINE struct oonf_layer2_neigh *
+ oonf_layer2_neigh_add(struct oonf_layer2_net *net, const struct netaddr *l2neigh) {
+   struct oonf_layer2_neigh_key key;
+   memset(&key, 0, sizeof(key));
+   memcpy(&key.addr, l2neigh, sizeof(*l2neigh));
+   return oonf_layer2_neigh_add_lid(net, &key);
+ }
+ /**
+  * Get a layer-2 neighbor object from the database
+  * @param l2net layer-2 network/interface object
+  * @param addr remote mac address of neighbor
+  * @return layer-2 neighbor object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_neigh *
+ oonf_layer2_neigh_get(const struct oonf_layer2_net *l2net, const struct netaddr *addr) {
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_neigh_key key;
+   memset(&key, 0, sizeof(key));
+   memcpy(&key.addr, addr, sizeof(*addr));
+   return avl_find_element(&l2net->neighbors, &key, l2neigh, _node);
+ }
+ /**
+  * Get a layer-2 neighbor object from the database
+  * @param l2net layer-2 network/interface object
+  * @param key unique key of remote neighbor
+  * @return layer-2 neighbor object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_neigh *
+ oonf_layer2_neigh_get_lid(const struct oonf_layer2_net *l2net, const struct oonf_layer2_neigh_key *key) {
+   struct oonf_layer2_neigh *l2neigh;
+   return avl_find_element(&l2net->neighbors, key, l2neigh, _node);
+ }
++static INLINE bool
++oonf_layer2_neigh_is_modified(const struct oonf_layer2_neigh *neigh, enum oonf_layer2_neigh_mods mod_mask) {
++  return (neigh->modified & mod_mask) != 0;
++}
++
++static INLINE const struct netaddr *
++oonf_layer2_neigh_get_nexthop(const struct oonf_layer2_neigh *neigh, int af_type) {
++  switch (af_type) {
++    case AF_INET:
++      return &neigh->_next_hop_v4;
++    case AF_INET6:
++      return &neigh->_next_hop_v6;
++    default:
++      return NULL;
++  }
++}
++
++static INLINE bool
++oonf_layer2_neigh_has_nexthop(const struct oonf_layer2_neigh *neigh, int af_type) {
++  const struct netaddr *next_hop;
++
++  next_hop = oonf_layer2_neigh_get_nexthop(neigh, af_type);
++  return next_hop != NULL && netaddr_get_address_family(next_hop) == af_type;
++}
++
++static INLINE uint64_t
++oonf_layer2_neigh_get_lastseen(const struct oonf_layer2_neigh *neigh) {
++  return neigh->_last_seen;
++}
++
++static INLINE void
++oonf_layer2_neigh_set_lastseen(struct oonf_layer2_neigh *neigh, uint64_t lastseen) {
++  if (neigh->_last_seen != lastseen) {
++    neigh->_last_seen = lastseen;
++    neigh->modified |= OONF_LAYER2_NEIGH_MODIFY_LASTSEEN;
++  }
++}
++
+ /**
+  * Get a layer-2 ip address object from the database
+  * @param l2neigh layer-2 neighbor object
+  * @param addr ip address of remote router
+  * @return layer-2 ip address object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_neighbor_address *
+ oonf_layer2_neigh_get_remote_ip(const struct oonf_layer2_neigh *l2neigh, const struct netaddr *addr) {
+   struct oonf_layer2_neighbor_address *l2ip;
+   return avl_find_element(&l2neigh->remote_neighbor_ips, addr, l2ip, _neigh_node);
+ }
+ /**
+  * Get a layer-2 destination (secondary MAC) for a neighbor
+  * @param l2neigh layer-2 neighbor object
+  * @param destination mac address of destination
+  * @return layer-2 destination object, NULL if not found
+  */
+ static INLINE struct oonf_layer2_destination *
+ oonf_layer2_destination_get(const struct oonf_layer2_neigh *l2neigh, const struct netaddr *destination) {
+   struct oonf_layer2_destination *l2dst;
+   return avl_find_element(&l2neigh->destinations, destination, l2dst, _node);
+ }
+ /**
+  * @param l2data layer-2 data object
+  * @return true if object contains a value, false otherwise
+  */
+ static INLINE bool
+ oonf_layer2_data_has_value(const struct oonf_layer2_data *l2data) {
+   return l2data->_type != OONF_LAYER2_NO_DATA;
+ }
+ /**
+  * @param l2data layer-2 data object
+  * @return type of data in object
+  */
+ static INLINE enum oonf_layer2_data_type
+ oonf_layer2_data_get_type(const struct oonf_layer2_data *l2data) {
+   return l2data->_type;
+ }
+ /**
+  * @param l2data layer-2 data object
+  * @param def default value to return
+  * @return value of data object, default value if not net
+  */
+ static INLINE int64_t
+ oonf_layer2_data_get_int64(const struct oonf_layer2_data *l2data, int64_t def) {
+   if (l2data->_type != OONF_LAYER2_INTEGER_DATA) {
+     return def;
+   }
+   return l2data->_value.integer;
+ }
+ /**
+  * @param l2data layer-2 data object
+  * @param def default value to return
+  * @return value of data object, default value if not net
+  */
+ static INLINE bool
+ oonf_layer2_data_get_boolean(const struct oonf_layer2_data *l2data, bool def) {
+   if (l2data->_type != OONF_LAYER2_BOOLEAN_DATA) {
+     return def;
+   }
+   return l2data->_value.boolean;
+ }
+ /**
+  * @param buffer pointer to int64 data storage
+  * @param l2data layer-2 data object
+  * @return 0 if value was read, -1 if it was the wrong type
+  */
+ static INLINE int
+ oonf_layer2_data_read_int64(int64_t *buffer, const struct oonf_layer2_data *l2data) {
+   if (l2data->_type != OONF_LAYER2_INTEGER_DATA) {
+     return -1;
+   }
+   *buffer = l2data->_value.integer;
+   return 0;
+ }
+ /**
+  * @param buffer pointer to boolean data storage
+  * @param l2data layer-2 data object
+  * @return 0 if value was read, -1 if it was the wrong type
+  */
+ static INLINE int
+ oonf_layer2_data_read_boolean(bool *buffer, const struct oonf_layer2_data *l2data) {
+   if (l2data->_type != OONF_LAYER2_BOOLEAN_DATA) {
+     return -1;
+   }
+   *buffer = l2data->_value.boolean;
+   return 0;
+ }
+ /**
+  * @param l2data layer-2 data object
+  * @return originator of data value
+  */
+ static INLINE const struct oonf_layer2_origin *
+ oonf_layer2_data_get_origin(const struct oonf_layer2_data *l2data) {
+   return l2data->_origin;
+ }
+ /**
+  * Sets the originator of a layer-2 data object
+  * @param l2data layer-2 data object
+  * @param origin originator of data value
+  */
+ static INLINE void
+ oonf_layer2_data_set_origin(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin) {
+   l2data->_origin = origin;
+ }
+ static INLINE bool
+ oonf_layer2_data_from_string(struct oonf_layer2_data *data, const struct oonf_layer2_origin *origin,
+   const struct oonf_layer2_metadata *meta, const char *input) {
+   union oonf_layer2_value value;
+   if (oonf_layer2_data_parse_string(&value, meta, input)) {
+     return false;
+   }
+   return oonf_layer2_data_set(data, origin, meta->type, &value);
+ }
+ static INLINE const char *
+ oonf_layer2_net_data_to_string(
+   char *buffer, size_t length, const struct oonf_layer2_data *data, enum oonf_layer2_network_index idx, bool raw) {
+   return oonf_layer2_data_to_string(buffer, length, data, oonf_layer2_net_metadata_get(idx), raw);
+ }
+ static INLINE const char *
+ oonf_layer2_neigh_data_to_string(
+   char *buffer, size_t length, const struct oonf_layer2_data *data, enum oonf_layer2_neighbor_index idx, bool raw) {
+   return oonf_layer2_data_to_string(buffer, length, data, oonf_layer2_neigh_metadata_get(idx), raw);
+ }
+ /**
+  * Set the value of a layer-2 data object
+  * @param l2data layer-2 data object
+  * @param origin originator of value
+  * @param integer new value for data object
+  * @return true if value was overwrite, false otherwise
+  */
+ static INLINE bool
+ oonf_layer2_data_set_int64(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, int64_t integer) {
+   union oonf_layer2_value value = { 0 };
+   value.integer = integer;
+   return oonf_layer2_data_set(l2data, origin, OONF_LAYER2_INTEGER_DATA, &value);
+ }
+ /**
+  * Set the value of a layer-2 data object
+  * @param l2data layer-2 data object
+  * @param origin originator of value
+  * @param boolean new value for data object
+  * @return true if value was overwrite, false otherwise
+  */
+ static INLINE bool
+ oonf_layer2_data_set_bool(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin, bool boolean) {
+   union oonf_layer2_value value = { 0 };
+   value.boolean = boolean;
+   return oonf_layer2_data_set(l2data, origin, OONF_LAYER2_BOOLEAN_DATA, &value);
+ }
+ static INLINE int
+ oonf_layer2_net_data_from_string(struct oonf_layer2_data *data, enum oonf_layer2_network_index idx,
+   struct oonf_layer2_origin *origin, const char *input) {
+   return oonf_layer2_data_from_string(data, origin, oonf_layer2_net_metadata_get(idx), input);
+ }
+ static INLINE int
+ oonf_layer2_neigh_data_from_string(struct oonf_layer2_data *data, enum oonf_layer2_neighbor_index idx,
+   struct oonf_layer2_origin *origin, const char *input) {
+   return oonf_layer2_data_from_string(data, origin, oonf_layer2_neigh_metadata_get(idx), input);
+ }
+ /**
+  * Removes the value of a layer-2 data object
+  * @param l2data layer-2 data object
+  */
+ static INLINE void
+ oonf_layer2_data_reset(struct oonf_layer2_data *l2data) {
+   l2data->_type = OONF_LAYER2_NO_DATA;
+   l2data->_origin = NULL;
+ }
+ #endif /* OONF_LAYER2_H_ */
index 0000000,c704f25..b647329
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,438 +1,449 @@@
 -  /*! tree of modifications which should be put into the next peer update */
 -  struct avl_tree _ip_prefix_modification;
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #ifndef _DLEP_SESSION_H_
+ #define _DLEP_SESSION_H_
+ struct dlep_session;
+ struct dlep_writer;
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_stream_socket.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/base/os_interface.h>
+ #include <oonf/generic/dlep/dlep_extension.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ /**
+  * Return codes for DLEP parser
+  */
+ enum dlep_parser_error
+ {
+   /*! parsing successful */
+   DLEP_NEW_PARSER_OKAY = 0,
+   /*! Signal terminates session, session is now invalid! */
+   DLEP_NEW_PARSER_TERMINDATED = -1,
+   /*! signal too short, incomplete TLV header */
+   DLEP_NEW_PARSER_INCOMPLETE_TLV_HEADER = -2,
+   /*! signal too short, incomplete TLV */
+   DLEP_NEW_PARSER_INCOMPLETE_TLV = -3,
+   /*! TLV type is not supported by session */
+   DLEP_NEW_PARSER_UNSUPPORTED_TLV = -4,
+   /*! TLV length is not supported */
+   DLEP_NEW_PARSER_ILLEGAL_TLV_LENGTH = -5,
+   /*! mandatory TLV is missing */
+   DLEP_NEW_PARSER_MISSING_MANDATORY_TLV = -6,
+   /*! this TLV must not be used more than once */
+   DLEP_NEW_PARSER_DUPLICATE_TLV = -7,
+   /*! out of memory error */
+   DLEP_NEW_PARSER_OUT_OF_MEMORY = -8,
+   /*! internal parser error, inconsistent data structures */
+   DLEP_NEW_PARSER_INTERNAL_ERROR = -9,
+ };
+ /**
+  * Definition of a TLV that has been parsed by DLEP
+  */
+ struct dlep_parser_tlv {
+   /*! tlv id */
+   uint16_t id;
+   /*! index of first session value for tlv, -1 if none */
+   int32_t tlv_first;
+   /*! index of last session value for tlv, -1 if none */
+   int32_t tlv_last;
+   /*! minimal length of tlv */
+   uint16_t length_min;
+   /*! maximal length of tlv */
+   uint16_t length_max;
+   /*! used to remove unsupported TLVs */
+   bool remove;
+   /*! node for session tlv tree */
+   struct avl_node _node;
+ };
+ /**
+  * header for binary data gathered for a TLV of a certain type
+  */
+ struct dlep_parser_value {
+   /*! index of next session value */
+   int32_t tlv_next;
+   /*! index of value within signal buffer */
+   uint16_t index;
+   /*! length of tlv in bytes */
+   uint16_t length;
+ };
+ /**
+  * Session for the DLEP tlv parser
+  */
+ struct dlep_session_parser {
+   /*! tree of allowed TLVs for this session */
+   struct avl_tree allowed_tlvs;
+   /*! array of TLV values */
+   struct dlep_parser_value *values;
+   /*! size of array for TLV value headers */
+   size_t value_max_count;
+   /*! array of active dlep extensions */
+   struct dlep_extension *extensions[DLEP_EXTENSION_BASE_COUNT + DLEP_EXTENSION_COUNT];
+   /*! number of active dlep extensions */
+   size_t extension_count;
+   /*! start of signal tlvs that is has been parsed */
+   const uint8_t *tlv_ptr;
+   /*! neighbor MAC a signal is referring to */
+   struct netaddr signal_neighbor_mac;
+ };
+ /**
+  * DLEP writer for TLV data
+  */
+ struct dlep_writer {
+   /*! output buffer for binary data */
+   struct autobuf *out;
+   /*! type of signal */
+   uint16_t signal_type;
+   /*! index of first byte of signal */
+   size_t signal_start;
+ };
+ /**
+  * Status of a DLEP neighbor
+  */
+ enum dlep_neighbor_state
+ {
+   /*! neighbor has not yet been used in session */
+   DLEP_NEIGHBOR_IDLE = 0,
+   /*! a destination up has been sent */
+   DLEP_NEIGHBOR_UP_SENT = 1,
+   /*! a destination up has been sent and acked */
+   DLEP_NEIGHBOR_UP_ACKED = 2,
+   /*! a destination down has been sent */
+   DLEP_NEIGHBOR_DOWN_SENT = 3,
+   /*! a destination down has been sent and acked*/
+   DLEP_NEIGHBOR_DOWN_ACKED = 4,
+ };
+ /**
+  * Neighbor that has been used in a DLEP session
+  */
+ struct dlep_local_neighbor {
+   /**
+    * mac address of the endpoint of the neighbor
+    * (might be proxied ethernet)
+    * it might also have a link ID
+    */
+   struct oonf_layer2_neigh_key key;
+   /*! state of neighbor */
+   enum dlep_neighbor_state state;
+   /*! true if the neighbor changes since the last update */
+   bool changed;
+   /*! true if iterative updates are be used for destination IPs */
+   bool destination_ip_iterative;
+   /*! mac address (plus link ID) of the neighbors wireless interface */
+   struct oonf_layer2_neigh_key neigh_key;
+   /*! back-pointer to dlep session */
+   struct dlep_session *session;
+   /*! timeout for acknowledgement signal */
+   struct oonf_timer_instance _ack_timeout;
+   /*! tree of modifications which should be put into the next destination update */
+   struct avl_tree _ip_prefix_modification;
+   /*! hook into the sessions tree of neighbors */
+   struct avl_node _node;
+ };
+ /**
+  * Configuration of a dlep session
+  */
+ struct dlep_session_config {
+   /*! peer type of local session */
+   char *peer_type;
+   /*! discovery interval */
+   uint64_t discovery_interval;
+   /*! heartbeat settings for our heartbeats */
+   uint64_t heartbeat_interval;
+   /*! true if normal neighbors should be sent with DLEP */
+   bool send_neighbors;
+   /*! true if proxied neighbors should be sent with DLEP */
+   bool send_proxied;
+   /*! length of LIDs used to communicate with router */
+   int32_t lid_length;
+ };
+ /**
+  * Records the state of the peer regarding PEER UPDATE messages
+  */
+ enum dlep_peer_state
+ {
+   DLEP_PEER_NOT_CONNECTED,
+   DLEP_PEER_WAIT_FOR_INIT,
+   DLEP_PEER_WAIT_FOR_UPDATE_ACK,
+   DLEP_PEER_SEND_UPDATE,
+   DLEP_PEER_IDLE,
+   DLEP_PEER_TERMINATED,
+ };
++struct dlep_session_ext_ip {
++  /*! tree of modifications which should be put into the next peer update */
++  struct avl_tree prefix_modification;
++
++  /*! last transmitted IPv4 DLEP interface IP */
++  struct netaddr if_ip_v4;
++
++  /*! last transmitted IPv6 DLEP interface IP */
++  struct netaddr if_ip_v6;
++};
++
+ /**
+  * Generic DLEP session, might be radio or router
+  */
+ struct dlep_session {
+   /*! copy of local configuration */
+   struct dlep_session_config cfg;
+   /*! restrict incoming signals to a special signal */
+   enum dlep_signals restrict_signal;
+   /*! initialize restrict signal with this variable after processing if not 0 */
+   enum dlep_signals next_restrict_signal;
+   /*! true if this is a radio session */
+   bool radio;
+   /*! parser for this dlep session */
+   struct dlep_session_parser parser;
+   /*! signal writer*/
+   struct dlep_writer writer;
+   /*! tree of local neighbors being processed by DLEP */
+   struct avl_tree local_neighbor_tree;
+   /*! oonf layer2 origin for dlep session */
+   const struct oonf_layer2_origin *l2_origin;
+   /*! oonf layer2 origin for dlep session defaults */
+   const struct oonf_layer2_origin *l2_default_origin;
+   /*! send content of output buffer */
+   void (*cb_send_buffer)(struct dlep_session *, int af_family);
+   /*! terminate the current session */
+   void (*cb_end_session)(struct dlep_session *);
+   /*! handle timeout for destination */
+   void (*cb_destination_timeout)(struct dlep_session *, struct dlep_local_neighbor *);
+   /*! log source for usage of this session */
+   enum oonf_log_source log_source;
+   /*! local layer2 data interface */
+   struct os_interface_listener l2_listener;
+   /*! timer to generate discovery/heartbeats */
+   struct oonf_timer_instance local_event_timer;
+   /*! keep track of remote heartbeats */
+   struct oonf_timer_instance remote_heartbeat_timeout;
+   /*! rate of remote heartbeats */
+   uint64_t remote_heartbeat_interval;
+   /*! remote endpoint of current communication */
+   union netaddr_socket remote_socket;
+   /*! timeout for acknowledgement signal */
+   struct oonf_timer_instance _ack_timeout;
+   /*! true if we cannot send a peer update at the moment */
+   enum dlep_peer_state _peer_state;
++  /*! session data for IP extension */
++  struct dlep_session_ext_ip _ext_ip;
+ };
+ void dlep_session_init(void);
+ int dlep_session_add(struct dlep_session *session, const char *l2_ifname, const struct oonf_layer2_origin *l2_origin,
+   const struct oonf_layer2_origin *l2_default_origin, struct autobuf *out, bool radio,
+   int (*if_changed)(struct os_interface_listener *), enum oonf_log_source);
+ void dlep_session_remove(struct dlep_session *session);
+ void dlep_session_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text);
+ int dlep_session_update_extensions(struct dlep_session *session, const uint8_t *extvalues, size_t extcount, bool radio);
+ enum oonf_stream_session_state dlep_session_process_tcp(
+   struct oonf_stream_session *tcp_session, struct dlep_session *session);
+ ssize_t dlep_session_process_buffer(struct dlep_session *session, const void *buffer, size_t length, bool is_udp);
+ ssize_t dlep_session_process_signal(struct dlep_session *session, const void *buffer, size_t length, bool is_udp);
+ int dlep_session_generate_signal(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor);
+ int dlep_session_generate_signal_status(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor,
+   enum dlep_status status, const char *msg);
+ struct dlep_parser_value *dlep_session_get_tlv_value(struct dlep_session *session, uint16_t tlvtype);
+ struct dlep_local_neighbor *dlep_session_add_local_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key);
+ void dlep_session_remove_local_neighbor(struct dlep_session *session, struct dlep_local_neighbor *local);
+ struct oonf_layer2_neigh *dlep_session_get_local_l2_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key);
+ struct oonf_layer2_neigh *dlep_session_get_l2_from_neighbor(struct dlep_local_neighbor *dlep_neigh);
+ /**
+  * get the dlep session tlv
+  * @param parser dlep session parser
+  * @param tlvtype tlvtype
+  * @return tlv parser information, NULL if not available
+  */
+ static INLINE struct dlep_parser_tlv *
+ dlep_parser_get_tlv(struct dlep_session_parser *parser, uint16_t tlvtype) {
+   struct dlep_parser_tlv *tlv;
+   return avl_find_element(&parser->allowed_tlvs, &tlvtype, tlv, _node);
+ }
+ /**
+  * Get value of first appearance of a TLV
+  * @param session dlep session parser
+  * @param tlv dlep session tlv
+  * @return dlep tlv value, NULL if no value available
+  */
+ static INLINE struct dlep_parser_value *
+ dlep_session_get_tlv_first_value(struct dlep_session *session, struct dlep_parser_tlv *tlv) {
+   if (tlv->tlv_first == -1) {
+     return NULL;
+   }
+   return &session->parser.values[tlv->tlv_first];
+ }
+ /**
+  * Get the next value of a TLV
+  * @param session dlep session parser
+  * @param value current dlep parser value
+  * @return next dlep tlv value, NULL if no further values
+  */
+ static INLINE struct dlep_parser_value *
+ dlep_session_get_next_tlv_value(struct dlep_session *session, struct dlep_parser_value *value) {
+   if (value->tlv_next == -1) {
+     return NULL;
+   }
+   return &session->parser.values[value->tlv_next];
+ }
+ /**
+  * Get the binary data of a tlv
+  * @param parser dlep session parser
+  * @param value dlep tlv value
+  * @return binary data pointer
+  */
+ static INLINE const uint8_t *
+ dlep_parser_get_tlv_binary(struct dlep_session_parser *parser, struct dlep_parser_value *value) {
+   return &parser->tlv_ptr[value->index];
+ }
+ /**
+  * Shortcut for getting the binary data of a TLV for a session
+  * @param session dlep session
+  * @param value dlep tlv value
+  * @return binary data pointer
+  */
+ static INLINE const uint8_t *
+ dlep_session_get_tlv_binary(struct dlep_session *session, struct dlep_parser_value *value) {
+   return &session->parser.tlv_ptr[value->index];
+ }
+ /**
+  * Get a DLEP neighbor
+  * @param session dlep session
+  * @param key neighbor MAC address plus link ID
+  * @return DLEP neighbor, NULL if not found
+  */
+ static INLINE struct dlep_local_neighbor *
+ dlep_session_get_local_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key) {
+   struct dlep_local_neighbor *local;
+   return avl_find_element(&session->local_neighbor_tree, key, local, _node);
+ }
+ #endif /* _DLEP_SESSION_H_ */
index 0000000,eaab1b0..34bb214
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,82 +1,90 @@@
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #ifndef DLEP_RADIO_SESSION_H_
+ #define DLEP_RADIO_SESSION_H_
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_packet_socket.h>
+ #include <oonf/base/oonf_stream_socket.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_session.h>
+ #include <oonf/generic/dlep/radio/dlep_radio_session.h>
+ /**
+  * DLEP radio session state
+  */
+ struct dlep_radio_session {
+   /*! basic struct for tcp stream, must be first in struct! */
+   struct oonf_stream_session stream;
+   /*! generic DLEP session */
+   struct dlep_session session;
+   /*! back pointer to interface session */
+   struct dlep_radio_if *interface;
+   /*! node for session tree of interface */
+   struct avl_node _node;
+ };
+ void dlep_radio_session_init(void);
+ void dlep_radio_session_cleanup(void);
+ void dlep_radio_remove_session(struct dlep_radio_session *router_session);
+ void dlep_radio_session_initialize_tcp_callbacks(struct oonf_stream_config *config);
++static INLINE struct dlep_radio_session *
++dlep_radio_get_session(struct dlep_session *session) {
++  if (!session->radio) {
++    return NULL;
++  }
++  return container_of(session, struct dlep_radio_session, session);
++}
++
+ #endif /* DLEP_RADIO_SESSION_H_ */
index 0000000,2fb598e..47cbdcf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,436 +1,436 @@@
 - * @param addr netaddr object
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #ifndef NETADDR_H_
+ #define NETADDR_H_
+ #ifndef _WIN32
+ #include <arpa/inet.h>
+ #include <net/if.h>
+ #include <netinet/if_ether.h>
+ #include <netinet/ip.h>
+ #else
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #define IF_NAMESIZE 16
+ #endif
+ #include <string.h>
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/oonf.h>
+ enum
+ {
+   /*! address family for 48-bit mac address */
+   AF_MAC48 = AF_MAX + 1,
+   /*! address family for 64-bit EUI-64 address */
+   AF_EUI64 = AF_MAX + 2,
+   /*! universal unique identifier (UUID) */
+   AF_UUID = AF_MAX + 3,
+ };
+ enum
+ {
+   /*! maximum length of a network address in octets */
+   NETADDR_MAX_LENGTH = 16
+ };
+ /*! text name for IPv4_ANY prefix */
+ #define NETADDR_STR_ANY4 "any4"
+ /*! text name for IPv6 ANY prefix */
+ #define NETADDR_STR_ANY6 "any6"
+ /*! text name for IPv4 linklocal prefix */
+ #define NETADDR_STR_LINKLOCAL4 "linklocal4"
+ /*! text name for IPv6 linklocal prefix */
+ #define NETADDR_STR_LINKLOCAL6 "linklocal6"
+ /*! text name for IPv6 unique local prefix */
+ #define NETADDR_STR_ULA "ula"
+ /**
+  * Representation of an address including address type
+  * At the moment we support AF_INET, AF_INET6 and AF_MAC48
+  */
+ struct netaddr {
+   /*! 16 bytes of memory for address */
+   uint8_t _addr[NETADDR_MAX_LENGTH];
+   /*! address type */
+   uint8_t _type;
+   /*! address prefix length */
+   uint8_t _prefix_len;
+ };
+ /**
+  * Representation of a sockaddr object. Allows access
+  * to all variants without casting and compiler warnings.
+  */
+ union netaddr_socket {
+   /*! IPv4 sockaddr */
+   struct sockaddr_in v4;
+   /*! IPv6 sockaddr */
+   struct sockaddr_in6 v6;
+   /*! generic sockaddr */
+   struct sockaddr std;
+   /*! storage object for sockaddr */
+   struct sockaddr_storage storage;
+ };
+ /**
+  * Maximum text length of netaddr_to_string
+  *
+  * INET_ADDRSTRLEN and INET6_ADDRSTRLEN have been defined
+  * in netinet/in.h, which has been included by this file
+  */
+ enum
+ {
+   MAC48_ADDRSTRLEN = 18,
+   MAC48_PREFIXSTRLEN = MAC48_ADDRSTRLEN + 3,
+   EUI64_ADDRSTRLEN = 24,
+   EUI64_PREFIXSTRLEN = EUI64_ADDRSTRLEN + 3,
+   INET_PREFIXSTRLEN = INET_ADDRSTRLEN + 3,
+   INET6_PREFIXSTRLEN = INET6_ADDRSTRLEN + 4,
+ };
+ /**
+  * Buffer for writing string representation of netaddr
+  * and netaddr_socket objects
+  */
+ struct netaddr_str {
+   /*! buffer for maximum length netaddr string representation */
+   char buf[INET6_ADDRSTRLEN + 16];
+ };
+ EXPORT extern const struct netaddr NETADDR_UNSPEC;
+ EXPORT extern const struct netaddr NETADDR_IPV4_ANY;
+ EXPORT extern const struct netaddr NETADDR_IPV4_BINDTO_ANY;
+ EXPORT extern const struct netaddr NETADDR_IPV4_MULTICAST;
+ EXPORT extern const struct netaddr NETADDR_IPV4_LINKLOCAL;
+ EXPORT extern const struct netaddr NETADDR_IPV4_LOOPBACK_NET;
+ EXPORT extern const struct netaddr NETADDR_IPV6_ANY;
+ EXPORT extern const struct netaddr NETADDR_IPV6_BINDTO_ANY;
+ EXPORT extern const struct netaddr NETADDR_IPV6_MULTICAST;
+ EXPORT extern const struct netaddr NETADDR_IPV6_LINKLOCAL;
+ EXPORT extern const struct netaddr NETADDR_IPV6_ULA;
+ EXPORT extern const struct netaddr NETADDR_IPV6_GLOBAL;
+ EXPORT extern const struct netaddr NETADDR_IPV6_IPV4COMPATIBLE;
+ EXPORT extern const struct netaddr NETADDR_IPV6_IPV4MAPPED;
+ EXPORT extern const struct netaddr NETADDR_IPV6_LOOPBACK;
+ EXPORT extern const struct netaddr NETADDR_MAC48_BROADCAST;
+ EXPORT const struct netaddr NETADDR_MAC48_IPV4_MULTICAST;
+ EXPORT const struct netaddr NETADDR_MAC48_IPV6_MULTICAST;
+ EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV4_ANY;
+ EXPORT extern const union netaddr_socket NETADDR_SOCKET_IPV6_ANY;
+ EXPORT int netaddr_from_binary_prefix(
+   struct netaddr *dst, const void *binary, size_t len, uint8_t addr_type, uint8_t prefix_len);
+ EXPORT int netaddr_to_binary(void *dst, const struct netaddr *src, size_t len);
+ EXPORT int netaddr_from_socket(struct netaddr *dst, const union netaddr_socket *src);
+ EXPORT int netaddr_to_socket(union netaddr_socket *dst, const struct netaddr *src);
+ EXPORT int netaddr_to_autobuf(struct autobuf *, const struct netaddr *src);
+ EXPORT int netaddr_create_host_bin(
+   struct netaddr *host, const struct netaddr *netmask, const void *number, size_t num_length);
+ EXPORT int netaddr_create_prefix(
+   struct netaddr *prefix, const struct netaddr *host, const struct netaddr *netmask, bool truncate);
+ EXPORT void netaddr_truncate(struct netaddr *dst, const struct netaddr *src);
+ EXPORT int netaddr_socket_init(
+   union netaddr_socket *combined, const struct netaddr *addr, uint16_t port, unsigned if_index);
+ EXPORT uint16_t netaddr_socket_get_port(const union netaddr_socket *sock);
+ EXPORT const char *netaddr_to_prefixstring(struct netaddr_str *dst, const struct netaddr *src, bool forceprefix);
+ EXPORT int netaddr_from_string(struct netaddr *, const char *) __attribute__((warn_unused_result));
+ EXPORT const char *netaddr_socket_to_string(struct netaddr_str *, const union netaddr_socket *);
+ EXPORT int netaddr_cmp_to_socket(const struct netaddr *, const union netaddr_socket *);
+ EXPORT bool netaddr_isequal_binary(
+   const struct netaddr *addr, const void *bin, size_t len, uint16_t af, uint8_t prefix_len);
+ EXPORT bool netaddr_is_in_subnet(const struct netaddr *subnet, const struct netaddr *addr);
+ EXPORT bool netaddr_binary_is_in_subnet(const struct netaddr *subnet, const void *bin, size_t len, uint8_t af_family);
+ EXPORT uint8_t netaddr_get_af_maxprefix(const uint32_t);
+ EXPORT int netaddr_avlcmp(const void *, const void *);
+ EXPORT int netaddr_socket_avlcmp(const void *, const void *);
+ #ifdef WIN32
+ EXPORT const char *inet_ntop(int af, const void *src, char *dst, int cnt);
+ EXPORT int inet_pton(int af, const char *cp, void *buf);
+ #endif
+ /**
+  * Sets the address type of a netaddr object to AF_UNSPEC
+  * @param addr netaddr object
+  */
+ static INLINE void
+ netaddr_invalidate(struct netaddr *addr) {
+   memset(addr, 0, sizeof(*addr));
+ }
+ /**
+  * Sets the address type of a netaddr object to AF_UNSPEC
+  * @param sock netaddr socket object
+  */
+ static INLINE void
+ netaddr_socket_invalidate(union netaddr_socket *sock) {
+   memset(sock, 0, sizeof(*sock));
+ }
+ /**
 -  return addr->_type == AF_UNSPEC;
++ * @param addr netaddr object (might be NULL)
+  * @return true if address is AF_UNSPEC, false otherwise
+  */
+ static INLINE bool
+ netaddr_is_unspec(const struct netaddr *addr) {
++  return addr == NULL || addr->_type == AF_UNSPEC;
+ }
+ /**
+  * @param sock netaddr socket object
+  * @return true if address is AF_UNSPEC, false otherwise
+  */
+ static INLINE bool
+ netaddr_socket_is_unspec(const union netaddr_socket *sock) {
+   return sock->std.sa_family == AF_UNSPEC;
+ }
+ /**
+  * Calculates the maximum prefix length of an address type
+  * @param addr netaddr object
+  * @return prefix length, 0 if unknown address family
+  */
+ static INLINE uint8_t
+ netaddr_get_maxprefix(const struct netaddr *addr) {
+   return netaddr_get_af_maxprefix(addr->_type);
+ }
+ /**
+  * Check if an address has the maximum prefix length
+  * @param addr netaddr object
+  * @return true if prefix length is maximum, false otherwise
+  */
+ static INLINE bool
+ netaddr_is_host(const struct netaddr *addr) {
+   return netaddr_get_maxprefix(addr) == addr->_prefix_len;
+ }
+ /**
+  * Converts a netaddr object into a string.
+  * Prefix will be added if necessary.
+  * @param dst target buffer
+  * @param src netaddr source
+  * @return pointer to target buffer, NULL if an error happened
+  */
+ static INLINE const char *
+ netaddr_to_string(struct netaddr_str *dst, const struct netaddr *src) {
+   return netaddr_to_prefixstring(dst, src, false);
+ }
+ /**
+  * Creates a host address from a netmask and a host number part. This function
+  * will copy the netmask and then overwrite the bits after the prefix length
+  * with the one from the host number.
+  * @param host target buffer
+  * @param netmask prefix of result
+  * @param host_number postfix of result
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static INLINE int
+ netaddr_create_host(struct netaddr *host, const struct netaddr *netmask, const struct netaddr *host_number) {
+   return netaddr_create_host_bin(host, netmask, host_number->_addr, netaddr_get_maxprefix(host_number) / 8);
+ }
+ /**
+  * Embed an IPv4 address into an IPv6 IPv4-compatible address
+  * @param dst target IPv6 address
+  * @param src source IPv4 address
+  */
+ static INLINE void
+ netaddr_embed_ipv4_compatible(struct netaddr *dst, const struct netaddr *src) {
+   memcpy(&dst->_addr[0], &NETADDR_IPV6_IPV4COMPATIBLE._addr[0], 12);
+   memcpy(&dst->_addr[12], &src->_addr[0], 4);
+   dst->_type = AF_INET6;
+   dst->_prefix_len = src->_prefix_len + 96;
+ }
+ /**
+  * Extract an IPv4 address from an IPv6 IPv4-compatible address
+  * @param dst target IPv4 address
+  * @param src source IPv6 address
+  */
+ static INLINE void
+ netaddr_extract_ipv4_compatible(struct netaddr *dst, const struct netaddr *src) {
+   memcpy(&dst->_addr[0], &src->_addr[12], 4);
+   memset(&dst->_addr[4], 0, 12);
+   dst->_type = AF_INET;
+   dst->_prefix_len = src->_prefix_len - 96;
+ }
+ /**
+  * Generate a stateless autoconfigured IPv6 address from an IPv6 template and a MAC address.
+  * @param ipv6 generated IPv6 address
+  * @param template original IPv6 address or prefix, lower 8 bytes will be overwritten
+  * @param mac MAC address to generate host part of address
+  */
+ static INLINE void
+ netaddr_get_stateless_ipv6(struct netaddr *ipv6, const struct netaddr *template, const struct netaddr *mac) {
+   memcpy(&ipv6->_addr[0], &template->_addr[0], 8);
+   ipv6->_addr[ 8] = mac->_addr[0] ^ 0x02;
+   ipv6->_addr[ 9] = mac->_addr[1];
+   ipv6->_addr[10] = mac->_addr[2];
+   ipv6->_addr[11] = 0xff;
+   ipv6->_addr[12] = 0xfe;
+   ipv6->_addr[13] = mac->_addr[3];
+   ipv6->_addr[14] = mac->_addr[4];
+   ipv6->_addr[15] = mac->_addr[5];
+   ipv6->_type = AF_INET6;
+   ipv6->_prefix_len = 128;
+ }
+ /**
+  * Read the binary representation of an address into a netaddr object
+  * @param dst pointer to netaddr object
+  * @param binary source pointer
+  * @param len length of source buffer
+  * @param addr_type address type of source,
+  *   0 for autodetection based on length
+  * @return 0 if successful read binary data, -1 otherwise
+  */
+ static INLINE int
+ netaddr_from_binary(struct netaddr *dst, const void *binary, size_t len, uint8_t addr_type) {
+   return netaddr_from_binary_prefix(dst, binary, len, addr_type, 255);
+ }
+ /**
+  * Compares two addresses.
+  * Address type will be compared last.
+  * @param a1 address 1
+  * @param a2 address 2
+  * @return >0 if a1>a2, <0 if a1<a2, 0 otherwise
+  */
+ static INLINE int
+ netaddr_cmp(const struct netaddr *a1, const struct netaddr *a2) {
+   return memcmp(a1, a2, sizeof(*a1));
+ }
+ /**
+  * Compares two sockets.
+  * @param s1 socket address port 1
+  * @param s2 socket address/port 2
+  * @return >0 if s1>s2, <0 if s1<s2, 0 otherwise
+  */
+ static INLINE int
+ netaddr_socket_cmp(const union netaddr_socket *s1, const union netaddr_socket *s2) {
+   return memcmp(s1, s2, sizeof(*s1));
+ }
+ /**
+  * @param n pointer to netaddr
+  * @return pointer to start of binary address
+  */
+ static INLINE const void *
+ netaddr_get_binptr(const struct netaddr *n) {
+   return &n->_addr[0];
+ }
+ /**
+  * @param n pointer to netaddr
+  * @return number of bytes of binary address
+  */
+ static INLINE size_t
+ netaddr_get_binlength(const struct netaddr *n) {
+   return netaddr_get_maxprefix(n) >> 3;
+ }
+ /**
+  * @param n pointer to netaddr
+  * @return address family
+  */
+ static INLINE uint8_t
+ netaddr_get_address_family(const struct netaddr *n) {
+   return n->_type;
+ }
+ /**
+  * @param n pointer to netaddr
+  * @return prefix length
+  */
+ static INLINE uint8_t
+ netaddr_get_prefix_length(const struct netaddr *n) {
+   return n->_prefix_len;
+ }
+ /**
+  * @param n pointer to netaddr
+  * @param prefix_len new prefix length
+  */
+ static INLINE void
+ netaddr_set_prefix_length(struct netaddr *n, uint8_t prefix_len) {
+   n->_prefix_len = prefix_len;
+ }
+ /**
+  * @param s pointer to netaddr socket
+  * @return address family of socket
+  */
+ static INLINE sa_family_t
+ netaddr_socket_get_addressfamily(const union netaddr_socket *s) {
+   return s->std.sa_family;
+ }
+ #endif /* NETADDR_H_ */
index 0000000,d27f3ce..082b4cf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,399 +1,423 @@@
 -struct nhdp_domain;
 -
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #ifndef NHDP_DOMAIN_H_
+ #define NHDP_DOMAIN_H_
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/list.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_rfc5444.h>
+ #include <oonf/nhdp/nhdp/nhdp_db.h>
+ #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
+ /*! memory class for nhdp domain */
+ #define NHDP_CLASS_DOMAIN "nhdp_domain"
++struct nhdp_domain;
++
+ /**
+  * NHDP domain constants
+  */
+ enum
+ {
+   /*! maximum length of metric name */
+   NHDP_DOMAIN_METRIC_MAXLEN = 16,
+   /*! maximum length of mpr name */
+   NHDP_DOMAIN_MPR_MAXLEN = 16,
+ };
+ /**
++ * Result of a metric calculation based on a layer2 neighbor
++ */
++enum nhdp_metric_result {
++  /*! metric has been calculated normally */
++  NHDP_METRIC_OKAY,
++
++  /*! metric has been calculated, but some data has been missing */
++  NHDP_METRIC_PARTIAL_DATA,
++
++  /*! metric could not be calculated */
++  NHDP_METRIC_NOT_AVAILABLE,
++};
++
++/**
+  * Buffer for string representation of a linkmetric value
+  */
+ struct nhdp_metric_str {
+   /*! buffer for maximum sized text link metric */
+   char buf[128];
+ };
+ /**
+  *  Metric handler for a NHDP domain.
+  */
+ struct nhdp_domain_metric {
+   /*! name of linkmetric */
+   const char *name;
+   /*! minimum metric value*/
+   uint32_t metric_minimum;
+   /*! maximum metric value */
+   uint32_t metric_maximum;
+   /*! default incoming link metric for "no default handling" metrics */
+   uint32_t incoming_link_start;
+   /*! default outgoing link metric for "no default handling" metrics */
+   uint32_t outgoing_link_start;
+   /*! default incoming 2-hop link metric for "no default handling" metrics */
+   uint32_t incoming_2hop_start;
+   /*! default outgoing 2-hop link metric for "no default handling" metrics */
+   uint32_t outgoing_2hop_start;
+   /*! true if metrics should not be handled by nhdp reader/writer */
+   bool no_default_handling;
+   /*! array of layer2 interface values this metric requires */
+   const enum oonf_layer2_network_index *required_l2net_data;
+   /*! number of required layer2 interface values */
+   size_t required_l2net_count;
+   /*! array of layer2 neighbor values this metric requires */
+   const enum oonf_layer2_neighbor_index *required_l2neigh_data;
+   /*! number of required layer2 neighbor values */
+   size_t required_l2neigh_count;
+   /**
+    * callback to convert link metric into string representation
+    * @param buf buffer to put string into
+    * @param cost link metric
+    * @return pointer to buffer
+    */
+   const char *(*link_to_string)(struct nhdp_metric_str *buf, uint32_t cost);
+   /**
+    * callback to convert path metric into string representation
+    * @param buf buffer to put string into
+    * @param cost path metric
+    * @param hopcount hopcount of path
+    * @return pointer to buffer
+    */
+   const char *(*path_to_string)(struct nhdp_metric_str *buf, uint32_t cost, uint8_t hopcount);
+   /*! conversion of internal metric data into string */
+   /**
+    * callback to convert internal metric state into string
+    * @param buf buffer to put internal state into
+    * @param lnk nhdp link
+    * @return pointer to buffer
+    */
+   const char *(*internal_link_to_string)(struct nhdp_metric_str *buf, struct nhdp_link *lnk);
+   /**
+    * callback to enable metric
+    */
+   void (*enable)(void);
+   /**
+    * callback to disable metric
+    */
+   void (*disable)(void);
++  /**
++   * Calculate the metric cost of a link defined by a layer2 neighbor
++   * @param domain nhdp domain the metric calculation should be based upon
++   * @param metric pointer to target buffer for metric result
++   * @param neigh layer2 neighbor
++   * @return status of metric calculation
++   */
++  enum nhdp_metric_result (*cb_get_metric)(struct nhdp_domain *domain, uint32_t *metric, struct oonf_layer2_neigh *neigh);
++
+   /*! reference count */
+   int _refcount;
+   /*! node for tree of metrics */
+   struct avl_node _node;
+ };
+ /**
+  * MPR handler for a NHDP domain
+  */
+ struct nhdp_domain_mpr {
+   /*! name of handler */
+   const char *name;
+   /**
+    * callback to calculate routing MPR set
+    * @param domain NHDP domain to update MPR set
+    */
+   void (*update_routing_mpr)(struct nhdp_domain *domain);
+   /**
+    * callback to calculate flooding MPR set
+    * @param domain NHDP domain used to update flooding MPR set
+    */
+   void (*update_flooding_mpr)(struct nhdp_domain *domain);
+   /**
+    * callback to enable mpr
+    */
+   void (*enable)(void);
+   /**
+    * callback to disable mpr
+    */
+   void (*disable)(void);
+   /*! reference count */
+   int _refcount;
+   /*! node for tree of MPR algorithms */
+   struct avl_node _node;
+ };
+ /**
+  * NHDP domain
+  *
+  * A domain is a topology on the mesh, including its own
+  * metric and routing MPR set. Both is transmitted over a
+  * specified TLV extension value on MPR and LQ TLVs.
+  */
+ struct nhdp_domain {
+   /*! name of metric */
+   char metric_name[NHDP_DOMAIN_METRIC_MAXLEN];
+   /*! name of MPR algorithm */
+   char mpr_name[NHDP_DOMAIN_MPR_MAXLEN];
+   /*! pointer to metric definition */
+   struct nhdp_domain_metric *metric;
+   /*! pointer to mpr definition */
+   struct nhdp_domain_mpr *mpr;
+   /*! flooding willingness */
+   uint8_t local_willingness;
+   /*! metric tlv extension */
+   uint8_t ext;
+   /*! index in the domain array */
+   int index;
+   /*! true if MPR should be recalculated */
+   bool _mpr_outdated;
+   /*! temporary storage for willingness processing */
+   uint8_t _tmp_willingness;
+   /*! storage for the up to four additional link metrics */
+   struct rfc5444_writer_tlvtype _metric_addrtlvs[4];
+   /*! list of nhdp domains */
+   struct list_entity _node;
+ };
+ /**
+  * listener for NHDP domain updates
+  */
+ struct nhdp_domain_listener {
+   /**
+    * Callback to inform about a NHDP neighbor MPR update
+    * @param domain NHDP domain of which the MPR set changed
+    */
+   void (*mpr_update)(struct nhdp_domain *domain);
+   /**
+    * Callback to inform about a NHDP neighbor metric update
+    * @param domain NHDP domain of which the metric changed
+    */
+   void (*metric_update)(struct nhdp_domain *domain);
+   /*! hook into global domain updater list */
+   struct list_entity _node;
+ };
+ /**
+  * Postprocessor for NHDP metric changes
+  */
+ struct nhdp_domain_metric_postprocessor {
+   /**
+    * Callback getting informed about a incoming metric change.
+    * @param domain domain the metric changed happened
+    * @param lnk nhdp link the metric change happened
+    * @param new_metric new metric value
+    * @return processed new metric value
+    */
+   uint32_t (*process_in_metric)(struct nhdp_domain *domain, struct nhdp_link *lnk, uint32_t new_metric);
+   /*! hook into global domain metric postprocessor list */
+   struct list_entity _node;
+ };
+ void nhdp_domain_init(struct oonf_rfc5444_protocol *);
+ void nhdp_domain_cleanup(void);
+ EXPORT size_t nhdp_domain_get_count(void);
+ EXPORT struct nhdp_domain *nhdp_domain_add(uint8_t ext);
+ EXPORT struct nhdp_domain *nhdp_domain_configure(
+   uint8_t ext, const char *metric_name, const char *mpr_name, uint8_t willingness);
+ EXPORT int nhdp_domain_metric_add(struct nhdp_domain_metric *);
+ EXPORT void nhdp_domain_metric_remove(struct nhdp_domain_metric *);
+ EXPORT int nhdp_domain_mpr_add(struct nhdp_domain_mpr *);
+ EXPORT void nhdp_domain_mpr_remove(struct nhdp_domain_mpr *);
+ EXPORT void nhdp_domain_listener_add(struct nhdp_domain_listener *);
+ EXPORT void nhdp_domain_listener_remove(struct nhdp_domain_listener *);
+ EXPORT void nhdp_domain_metric_postprocessor_add(struct nhdp_domain_metric_postprocessor *);
+ EXPORT void nhdp_domain_metric_postprocessor_remove(struct nhdp_domain_metric_postprocessor *);
+ EXPORT struct nhdp_domain *nhdp_domain_get_by_ext(uint8_t);
+ EXPORT void nhdp_domain_init_link(struct nhdp_link *);
+ EXPORT void nhdp_domain_init_l2hop(struct nhdp_l2hop *);
+ EXPORT void nhdp_domain_init_neighbor(struct nhdp_neighbor *);
+ EXPORT void nhdp_domain_process_metric_linktlv(struct nhdp_domain *, struct nhdp_link *lnk, const uint8_t *value);
+ EXPORT void nhdp_domain_process_metric_2hoptlv(struct nhdp_domain *d, struct nhdp_l2hop *l2hop, const uint8_t *value);
+ EXPORT size_t nhdp_domain_process_mprtypes_tlv(
+   uint8_t *mprtypes, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv);
+ EXPORT void nhdp_domain_process_mpr_tlv(
+   uint8_t *mprtypes, size_t mprtypes_size, struct nhdp_link *, struct rfc5444_reader_tlvblock_entry *tlv);
+ EXPORT void nhdp_domain_process_willingness_tlv(
+   uint8_t *mpr_types, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv);
+ EXPORT void nhdp_domain_store_willingness(struct nhdp_link *);
+ EXPORT size_t nhdp_domain_encode_mprtypes_tlvvalue(uint8_t *mprtypes, size_t mprtypes_size);
+ EXPORT size_t nhdp_domain_encode_mpr_tlvvalue(uint8_t *tlvvalue, size_t tlvsize, struct nhdp_link *);
+ EXPORT size_t nhdp_domain_encode_willingness_tlvvalue(uint8_t *tlvvalue, size_t tlvsize);
+ EXPORT bool nhdp_domain_set_incoming_metric(
+   struct nhdp_domain_metric *metric, struct nhdp_link *lnk, uint32_t metric_in);
+ EXPORT bool nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh);
++EXPORT enum nhdp_metric_result nhdp_domain_get_metric(struct nhdp_domain *domain, uint32_t *metric, struct oonf_layer2_neigh *neigh);
+ EXPORT bool nhdp_domain_node_is_mpr(void);
+ EXPORT void nhdp_domain_delayed_mpr_recalculation(struct nhdp_domain *domain, struct nhdp_neighbor *neigh);
+ EXPORT void nhdp_domain_recalculate_mpr(void);
+ EXPORT struct list_entity *nhdp_domain_get_list(void);
+ EXPORT struct list_entity *nhdp_domain_get_listener_list(void);
+ EXPORT const struct nhdp_domain *nhdp_domain_get_flooding_domain(void);
+ EXPORT void nhdp_domain_set_flooding_mpr(const char *mpr_name, uint8_t willingness);
+ EXPORT const struct nhdp_domain *nhdp_domain_get_flooding_domain(void);
+ /**
+  * @param domain NHDP domain
+  * @param lnk NHDP link
+  * @return domain data of specified link
+  */
+ static INLINE struct nhdp_link_domaindata *
+ nhdp_domain_get_linkdata(const struct nhdp_domain *domain, struct nhdp_link *lnk) {
+   return &lnk->_domaindata[domain->index];
+ }
+ /**
+  * @param domain NHDP domain
+  * @param neigh NHDP neighbor
+  * @return domain data of specified neighbor
+  */
+ static INLINE struct nhdp_neighbor_domaindata *
+ nhdp_domain_get_neighbordata(const struct nhdp_domain *domain, struct nhdp_neighbor *neigh) {
+   return &neigh->_domaindata[domain->index];
+ }
+ /**
+  * @param domain NHDP domain
+  * @param l2hop NHDP twohop neighbor
+  * @return domain data of specified twohop neighbor
+  */
+ static INLINE struct nhdp_l2hop_domaindata *
+ nhdp_domain_get_l2hopdata(const struct nhdp_domain *domain, struct nhdp_l2hop *l2hop) {
+   return &l2hop->_domaindata[domain->index];
+ }
+ /**
+  * @param buf pointer to metric output buffer
+  * @param domain pointer to metric domain
+  * @param metric raw metric value
+  * @return pointer to string representation of metric
+  */
+ static INLINE const char *
+ nhdp_domain_get_link_metric_value(struct nhdp_metric_str *buf, const struct nhdp_domain *domain, uint32_t metric) {
+   return domain->metric->link_to_string(buf, metric);
+ }
+ /**
+  * @param buf pointer to metric output buffer
+  * @param domain pointer to metric domain
+  * @param metric raw path metric value
+  * @param hopcount path hopcount
+  * @return pointer to string representation of metric
+  */
+ static INLINE const char *
+ nhdp_domain_get_path_metric_value(
+   struct nhdp_metric_str *buf, const struct nhdp_domain *domain, uint32_t metric, uint8_t hopcount) {
+   return domain->metric->path_to_string(buf, metric, hopcount);
+ }
+ /**
+  * @param buf pointer to metric output buffer
+  * @param metric pointer to metric
+  * @param lnk nhdp link
+  * @return pointer to string internal representation of metric
+  */
+ static INLINE const char *
+ nhdp_domain_get_internal_link_metric_value(
+   struct nhdp_metric_str *buf, const struct nhdp_domain_metric *metric, struct nhdp_link *lnk) {
+   return metric->internal_link_to_string(buf, lnk);
+ }
+ #endif /* NHDP_DOMAIN_H_ */
index 0000000,f4903b8..d9e2976
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1342 +1,1379 @@@
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/libcommon/avl_comp.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/json.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libconfig/cfg_schema.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_class.h>
+ #include <oonf/base/os_interface.h>
+ #include <oonf/base/oonf_layer2.h>
+ /* Definitions */
+ #define LOG_LAYER2 _oonf_layer2_subsystem.logging
+ /* prototypes */
+ static int _init(void);
+ static void _cleanup(void);
+ static void _net_remove(struct oonf_layer2_net *l2net);
+ static void _neigh_remove(struct oonf_layer2_neigh *l2neigh);
+ /* subsystem definition */
+ static const char *_dependencies[] = {
+   OONF_CLASS_SUBSYSTEM,
+   OONF_OS_INTERFACE_SUBSYSTEM,
+ };
+ static struct oonf_subsystem _oonf_layer2_subsystem = {
+   .name = OONF_LAYER2_SUBSYSTEM,
+   .dependencies = _dependencies,
+   .dependencies_count = ARRAYSIZE(_dependencies),
+   .init = _init,
+   .cleanup = _cleanup,
+ };
+ DECLARE_OONF_PLUGIN(_oonf_layer2_subsystem);
+ /* layer2 neighbor metadata */
+ static const struct oonf_layer2_metadata _metadata_neigh[OONF_LAYER2_NEIGH_COUNT] = {
+   [OONF_LAYER2_NEIGH_TX_SIGNAL] = { .key = "tx_signal",
+     .type = OONF_LAYER2_INTEGER_DATA,
+     .unit = "dBm",
+     .fraction = 3 },
+   [OONF_LAYER2_NEIGH_RX_SIGNAL] = { .key = "rx_signal",
+     .type = OONF_LAYER2_INTEGER_DATA,
+     .unit = "dBm",
+     .fraction = 3 },
+   [OONF_LAYER2_NEIGH_TX_BITRATE] = { .key = "tx_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_RX_BITRATE] = { .key = "rx_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_TX_MAX_BITRATE] = { .key = "tx_max_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_RX_MAX_BITRATE] = { .key = "rx_max_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_TX_BYTES] = { .key = "tx_bytes", .type = OONF_LAYER2_INTEGER_DATA, .unit = "byte" },
+   [OONF_LAYER2_NEIGH_RX_BYTES] = { .key = "rx_bytes", .type = OONF_LAYER2_INTEGER_DATA, .unit = "byte" },
+   [OONF_LAYER2_NEIGH_TX_FRAMES] = { .key = "tx_frames", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RX_FRAMES] = { .key = "rx_frames", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_TX_THROUGHPUT] = { .key = "tx_throughput", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_RX_THROUGHPUT] = { .key = "rx_throughput", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_TX_RETRIES] = { .key = "tx_retries", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RX_RETRIES] = { .key = "rx_retries", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_TX_FAILED] = { .key = "tx_failed", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RX_FAILED] = { .key = "rx_failed", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_TX_RLQ] = { .key = "tx_rlq", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RX_RLQ] = { .key = "rx_rlq", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RX_BC_BITRATE] = { .key = "rx_bc_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NEIGH_RX_BC_LOSS] = { .key = "rx_bc_loss", .type = OONF_LAYER2_INTEGER_DATA, .fraction = 3 },
+   [OONF_LAYER2_NEIGH_LATENCY] = { .key = "latency", .type = OONF_LAYER2_INTEGER_DATA, .unit = "s", .fraction = 6 },
+   [OONF_LAYER2_NEIGH_RESOURCES] = { .key = "resources", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_RADIO_HOPCOUNT] = { .key = "radio_hopcount", .type = OONF_LAYER2_INTEGER_DATA },
+   [OONF_LAYER2_NEIGH_IP_HOPCOUNT] = { .key = "ip_hopcount", .type = OONF_LAYER2_INTEGER_DATA },
+ };
+ /* layer2 network metadata */
+ static const struct oonf_layer2_metadata _metadata_net[OONF_LAYER2_NET_COUNT] = {
+   [OONF_LAYER2_NET_FREQUENCY_1] = { .key = "frequency1", .type = OONF_LAYER2_INTEGER_DATA, .unit = "Hz" },
+   [OONF_LAYER2_NET_FREQUENCY_2] = { .key = "frequency2", .type = OONF_LAYER2_INTEGER_DATA, .unit = "Hz" },
+   [OONF_LAYER2_NET_BANDWIDTH_1] = { .key = "bandwidth1", .type = OONF_LAYER2_INTEGER_DATA, .unit = "Hz" },
+   [OONF_LAYER2_NET_BANDWIDTH_2] = { .key = "bandwidth2", .type = OONF_LAYER2_INTEGER_DATA, .unit = "Hz" },
+   [OONF_LAYER2_NET_NOISE] = { .key = "noise", .type = OONF_LAYER2_INTEGER_DATA, .unit = "dBm", .fraction = 3 },
+   [OONF_LAYER2_NET_CHANNEL_ACTIVE] = { .key = "ch_active",
+     .type = OONF_LAYER2_INTEGER_DATA,
+     .unit = "s",
+     .fraction = 9 },
+   [OONF_LAYER2_NET_CHANNEL_BUSY] = { .key = "ch_busy", .type = OONF_LAYER2_INTEGER_DATA, .unit = "s", .fraction = 9 },
+   [OONF_LAYER2_NET_CHANNEL_RX] = { .key = "ch_rx", .type = OONF_LAYER2_INTEGER_DATA, .unit = "s", .fraction = 9 },
+   [OONF_LAYER2_NET_CHANNEL_TX] = { .key = "ch_tx", .type = OONF_LAYER2_INTEGER_DATA, .unit = "s", .fraction = 9 },
+   [OONF_LAYER2_NET_TX_BC_BITRATE] = { .key = "tx_bc_bitrate", .type = OONF_LAYER2_INTEGER_DATA, .unit = "bit/s" },
+   [OONF_LAYER2_NET_MTU] = { .key = "mtu", .type = OONF_LAYER2_INTEGER_DATA, .unit = "byte" },
+   [OONF_LAYER2_NET_MCS_BY_PROBING] = { .key = "mcs_by_probing", .type = OONF_LAYER2_BOOLEAN_DATA },
+   [OONF_LAYER2_NET_RX_ONLY_UNICAST] = { .key = "rx_only_unicast", .type = OONF_LAYER2_BOOLEAN_DATA },
+   [OONF_LAYER2_NET_TX_ONLY_UNICAST] = { .key = "tx_only_unicast", .type = OONF_LAYER2_BOOLEAN_DATA },
+   [OONF_LAYER2_NET_RADIO_MULTIHOP] = { .key = "radio_multihop", .type = OONF_LAYER2_BOOLEAN_DATA },
+   [OONF_LAYER2_NET_BAND_UP_DOWN] = { .key = "band_updown", .type = OONF_LAYER2_BOOLEAN_DATA },
+ };
+ static const char *_network_type[OONF_LAYER2_TYPE_COUNT] = {
+   [OONF_LAYER2_TYPE_UNDEFINED] = "undefined",
+   [OONF_LAYER2_TYPE_WIRELESS] = "wireless",
+   [OONF_LAYER2_TYPE_ETHERNET] = "ethernet",
+   [OONF_LAYER2_TYPE_TUNNEL] = "tunnel",
+ };
+ static const char *_data_comparators[OONF_LAYER2_DATA_CMP_COUNT] = {
+   [OONF_LAYER2_DATA_CMP_EQUALS] = "==",
+   [OONF_LAYER2_DATA_CMP_NOT_EQUALS] = "!=",
+   [OONF_LAYER2_DATA_CMP_LESSER] = "<",
+   [OONF_LAYER2_DATA_CMP_LESSER_OR_EQUALS] = "<=",
+   [OONF_LAYER2_DATA_CMP_GREATER] = ">",
+   [OONF_LAYER2_DATA_CMP_GREATER_OR_EQUALS] = ">=",
+ };
+ static const char *_data_types[OONF_LAYER2_DATA_TYPE_COUNT] = {
+   [OONF_LAYER2_NO_DATA] = "none",
+   [OONF_LAYER2_INTEGER_DATA] = "integer",
+   [OONF_LAYER2_BOOLEAN_DATA] = "boolean",
+   [OONF_LAYER2_NETWORK_DATA] = "network",
+ };
+ /* infrastructure for l2net/l2neigh tree */
+ static struct oonf_class _l2network_class = {
+   .name = LAYER2_CLASS_NETWORK,
+   .size = sizeof(struct oonf_layer2_net),
+ };
+ static struct oonf_class _l2neighbor_class = {
+   .name = LAYER2_CLASS_NEIGHBOR,
+   .size = sizeof(struct oonf_layer2_neigh),
+ };
+ static struct oonf_class _l2dst_class = {
+   .name = LAYER2_CLASS_DESTINATION,
+   .size = sizeof(struct oonf_layer2_destination),
+ };
+ static struct oonf_class _l2net_addr_class = {
+   .name = LAYER2_CLASS_NETWORK_ADDRESS,
+   .size = sizeof(struct oonf_layer2_peer_address),
+ };
+ static struct oonf_class _l2neigh_addr_class = {
+   .name = LAYER2_CLASS_NEIGHBOR_ADDRESS,
+   .size = sizeof(struct oonf_layer2_neighbor_address),
+ };
+ static struct oonf_class _lid_class = {
+   .name = LAYER2_CLASS_LID,
+   .size = sizeof(struct oonf_layer2_lid),
+ };
+ static struct avl_tree _oonf_layer2_net_tree;
+ static struct avl_tree _oonf_originator_tree;
+ static struct avl_tree _local_peer_ips_tree;
+ static struct avl_tree _lid_tree;
+ static uint32_t _lid_originator_count;
+ /**
+  * Subsystem constructor
+  * @return always returns 0
+  */
+ static int
+ _init(void) {
+   oonf_class_add(&_l2network_class);
+   oonf_class_add(&_l2neighbor_class);
+   oonf_class_add(&_l2dst_class);
+   oonf_class_add(&_l2net_addr_class);
+   oonf_class_add(&_l2neigh_addr_class);
+   oonf_class_add(&_lid_class);
+   avl_init(&_oonf_layer2_net_tree, avl_comp_strcasecmp, false);
+   avl_init(&_oonf_originator_tree, avl_comp_strcasecmp, false);
+   avl_init(&_local_peer_ips_tree, avl_comp_netaddr, true);
+   avl_init(&_lid_tree, avl_comp_netaddr, false);
+   _lid_originator_count = 0;
+   return 0;
+ }
+ /**
+  * Subsystem destructor
+  */
+ static void
+ _cleanup(void) {
+   struct oonf_layer2_net *l2net, *l2n_it;
+   struct oonf_layer2_lid *lid, *lid_it;
+   avl_for_each_element_safe(&_oonf_layer2_net_tree, l2net, _node, l2n_it) {
+     _net_remove(l2net);
+   }
+   avl_for_each_element_safe(&_lid_tree, lid, _node, lid_it) {
+     avl_remove(&_lid_tree, &lid->_node);
+     oonf_class_free(&_lid_class, lid);
+   }
+   oonf_class_remove(&_lid_class);
+   oonf_class_remove(&_l2neigh_addr_class);
+   oonf_class_remove(&_l2net_addr_class);
+   oonf_class_remove(&_l2dst_class);
+   oonf_class_remove(&_l2neighbor_class);
+   oonf_class_remove(&_l2network_class);
+ }
+ /**
+  * Register a new data originator number for layer2 data
+  * @param origin layer2 originator
+  */
+ void
+ oonf_layer2_origin_add(struct oonf_layer2_origin *origin) {
+   origin->_node.key = origin->name;
+   avl_insert(&_oonf_originator_tree, &origin->_node);
+   if (origin->lid) {
+     origin->lid_index = _lid_originator_count;
+     _lid_originator_count++;
+   }
+ }
+ /**
+  * Removes all layer2 data associated with this data originator
+  * @param origin originator
+  */
+ void
+ oonf_layer2_origin_remove(struct oonf_layer2_origin *origin) {
+   struct oonf_layer2_net *l2net, *l2net_it;
+   if (!avl_is_node_added(&origin->_node)) {
+     return;
+   }
+   avl_for_each_element_safe(&_oonf_layer2_net_tree, l2net, _node, l2net_it) {
+     oonf_layer2_net_remove(l2net, origin);
+   }
+   avl_remove(&_oonf_originator_tree, &origin->_node);
+ }
+ /**
+  * Parse a string into a layer2 data object
+  * @param value target buffer for layer2 data
+  * @param meta metadata for layer2 data
+  * @param input input string
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ oonf_layer2_data_parse_string(
+   union oonf_layer2_value *value, const struct oonf_layer2_metadata *meta, const char *input) {
+   memset(value, 0, sizeof(*value));
+   switch (meta->type) {
+     case OONF_LAYER2_INTEGER_DATA:
+       return isonumber_to_s64(&value->integer, input, meta->fraction);
+     case OONF_LAYER2_BOOLEAN_DATA:
+       if (!cfg_is_bool(input)) {
+         return -1;
+       }
+       value->boolean = cfg_get_bool(input);
+       return 0;
+     default:
+       return -1;
+   }
+ }
+ /**
+  * Convert a layer2 data object into a string representation
+  * @param buffer destination string buffer
+  * @param length length of string buffer
+  * @param data layer2 data
+  * @param meta layer2 metadata
+  * @param raw true for raw conversion (switch of isoprefix conversion)
+  * @return pointer to output buffer, NULL if an error happened
+  */
+ const char *
+ oonf_layer2_data_to_string(
+   char *buffer, size_t length, const struct oonf_layer2_data *data, const struct oonf_layer2_metadata *meta, bool raw) {
+   struct isonumber_str iso_str;
+   switch (meta->type) {
+     case OONF_LAYER2_INTEGER_DATA:
+       if (!isonumber_from_s64(&iso_str, data->_value.integer, meta->unit, meta->fraction, raw)) {
+         return NULL;
+       }
+       return strscpy(buffer, iso_str.buf, length);
+     case OONF_LAYER2_BOOLEAN_DATA:
+       return strscpy(buffer, json_getbool(data->_value.boolean), length);
+     default:
+       return NULL;
+   }
+ }
+ /**
+  * (Over)write the value of a layer2 data object
+  * @param l2data layer2 data object
+  * @param origin origin of new data
+  * @param type type of new data
+  * @param input new data value
+  * @return true if data changed, false otherwise
+  */
+ bool
+ oonf_layer2_data_set(struct oonf_layer2_data *l2data, const struct oonf_layer2_origin *origin,
+   enum oonf_layer2_data_type type, const union oonf_layer2_value *input) {
+   bool changed = false;
+   if (l2data->_type == OONF_LAYER2_NO_DATA || l2data->_origin == NULL || l2data->_origin == origin ||
+       l2data->_origin->priority < origin->priority) {
+     changed = l2data->_type != type || memcmp(&l2data->_value, input, sizeof(*input)) != 0;
+     memcpy(&l2data->_value, input, sizeof(*input));
+     l2data->_type = type;
+     l2data->_origin = origin;
+   }
+   return changed;
+ }
+ /**
+  * Compare two layer2 data objects
+  * @param left left parameter for comparator
+  * @param right right parameter for comparator
+  * @param comparator comparator type
+  * @param data_type data type for comparison
+  * @return comparator result, false if not valid
+  *   (e.g. comparing different types of data)
+  */
+ bool
+ oonf_layer2_data_compare(const union oonf_layer2_value *left, const union oonf_layer2_value *right,
+   enum oonf_layer2_data_comparator_type comparator, enum oonf_layer2_data_type data_type) {
+   int result;
+   switch (data_type) {
+     case OONF_LAYER2_INTEGER_DATA:
+       if (left->integer > right->integer) {
+         result = 1;
+       }
+       else if (left->integer < right->integer) {
+         result = -1;
+       }
+       else {
+         result = 0;
+       }
+       break;
+     case OONF_LAYER2_BOOLEAN_DATA:
+       result = memcmp(&left->boolean, &right->boolean, sizeof(left->boolean));
+       break;
+     case OONF_LAYER2_NETWORK_DATA:
+       result = memcmp(&left->addr, &right->addr, sizeof(left->addr));
+       break;
+     default:
+       return false;
+   }
+   switch (comparator) {
+     case OONF_LAYER2_DATA_CMP_EQUALS:
+       return result == 0;
+     case OONF_LAYER2_DATA_CMP_NOT_EQUALS:
+       return result != 0;
+     case OONF_LAYER2_DATA_CMP_LESSER:
+       return result < 0;
+     case OONF_LAYER2_DATA_CMP_LESSER_OR_EQUALS:
+       return result <= 0;
+     case OONF_LAYER2_DATA_CMP_GREATER:
+       return result > 0;
+     case OONF_LAYER2_DATA_CMP_GREATER_OR_EQUALS:
+       return result >= 0;
+     default:
+       return false;
+   }
+ }
+ /**
+  * Get comparator type from string
+  * @param string string (C) representation of comparator
+  * @return comparator type
+  */
+ enum oonf_layer2_data_comparator_type
+ oonf_layer2_data_get_comparator(const char *string)
+ {
+   enum oonf_layer2_data_comparator_type i;
+   for (i = 0; i < OONF_LAYER2_DATA_CMP_COUNT; i++) {
+     if (strcmp(string, _data_comparators[i]) == 0) {
+       return i;
+     }
+   }
+   return OONF_LAYER2_DATA_CMP_ILLEGAL;
+ }
+ /**
+  * @param type layer2 comparator type
+  * @return string representation of comparator
+  */
+ const char *
+ oonf_layer2_data_get_comparator_string(enum oonf_layer2_data_comparator_type type) {
+   return _data_comparators[type];
+ }
+ /**
+  * @param type type index of layer2 data
+  * @return the string name of a layer2 data type
+  */
+ const char *
+ oonf_layer2_data_get_type_string(enum oonf_layer2_data_type type) {
+   return _data_types[type];
+ }
+ /**
+  * Add a layer-2 network to the database
+  * @param ifname name of interface
+  * @return layer-2 network object
+  */
+ struct oonf_layer2_net *
+ oonf_layer2_net_add(const char *ifname) {
+   struct oonf_layer2_net *l2net;
+   if (!ifname) {
+     return NULL;
+   }
+   l2net = avl_find_element(&_oonf_layer2_net_tree, ifname, l2net, _node);
+   if (l2net) {
+     return l2net;
+   }
+   l2net = oonf_class_malloc(&_l2network_class);
+   if (!l2net) {
+     return NULL;
+   }
+   /* initialize key */
+   strscpy(l2net->name, ifname, sizeof(l2net->name));
+   /* add to global l2net tree */
+   l2net->_node.key = l2net->name;
+   avl_insert(&_oonf_layer2_net_tree, &l2net->_node);
+   /* initialize tree of neighbors, ips and proxies */
+   avl_init(&l2net->neighbors, oonf_layer2_avlcmp_neigh_key, false);
+   avl_init(&l2net->local_peer_ips, avl_comp_netaddr, false);
+   avl_init(&l2net->remote_neighbor_ips, avl_comp_netaddr, true);
+   /* initialize interface listener */
+   l2net->if_listener.name = l2net->name;
+   os_interface_add(&l2net->if_listener);
+   oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_ADDED);
+   return l2net;
+ }
+ /**
+  * Remove all data objects of a certain originator from a layer-2 network
+  * object.
+  * @param l2net layer-2 addr object
+  * @param origin originator number
+  * @param cleanup_neigh true to cleanup neighbor data too
+  * @return true if a value was removed, false otherwise
+  */
+ bool
+ oonf_layer2_net_cleanup(struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin, bool cleanup_neigh) {
+   struct oonf_layer2_neigh *l2neigh;
+   bool changed = false;
+   int i;
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     if (l2net->data[i]._origin == origin) {
+       oonf_layer2_data_reset(&l2net->data[i]);
+       changed = true;
+     }
+   }
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (l2net->neighdata[i]._origin == origin) {
+       oonf_layer2_data_reset(&l2net->neighdata[i]);
+       changed = true;
+     }
+   }
+   if (cleanup_neigh) {
+     avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
+       changed |= oonf_layer2_neigh_cleanup(l2neigh, origin);
+     }
+   }
+   return changed;
+ }
+ /**
+  * Remove all information of a certain originator from a layer-2 addr
+  * object. Remove the object if its empty and has no neighbors anymore.
+  * @param l2net layer-2 addr object
+  * @param origin originator identifier
+  * @return true if something changed, false otherwise
+  */
+ bool
+ oonf_layer2_net_remove(struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin) {
+   struct oonf_layer2_neigh *l2neigh, *l2neigh_it;
+   bool changed = false;
+   if (!avl_is_node_added(&l2net->_node)) {
+     return false;
+   }
+   avl_for_each_element_safe(&l2net->neighbors, l2neigh, _node, l2neigh_it) {
+     if (oonf_layer2_neigh_remove(l2neigh, origin)) {
+       changed = true;
+     }
+   }
+   if (oonf_layer2_net_cleanup(l2net, origin, false)) {
+     changed = true;
+   }
+   if (changed) {
+     oonf_layer2_net_commit(l2net);
+   }
+   return changed;
+ }
+ /**
+  * Commit all changes to a layer-2 addr object. This might remove the
+  * object from the database if all data has been removed from the object.
+  * @param l2net layer-2 addr object
+  * @return true if the object has been removed, false otherwise
+  */
+ bool
+ oonf_layer2_net_commit(struct oonf_layer2_net *l2net) {
+   size_t i;
+   if (l2net->neighbors.count > 0) {
+     oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_CHANGED);
+     return false;
+   }
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     if (oonf_layer2_data_has_value(&l2net->data[i])) {
+       oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_CHANGED);
+       return false;
+     }
+   }
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (oonf_layer2_data_has_value(&l2net->neighdata[i])) {
+       oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_CHANGED);
+       return false;
+     }
+   }
+   _net_remove(l2net);
+   return true;
+ }
+ /**
+  * Relabel all network data (including neighbor data)
+  * of one origin to another one
+  * @param l2net layer2 network object
+  * @param new_origin new origin
+  * @param old_origin old origin to overwrite
+  */
+ void
+ oonf_layer2_net_relabel(struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *new_origin,
+   const struct oonf_layer2_origin *old_origin) {
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_peer_address *peer_ip;
+   size_t i;
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     if (oonf_layer2_data_get_origin(&l2net->data[i]) == old_origin) {
+       oonf_layer2_data_set_origin(&l2net->data[i], new_origin);
+     }
+   }
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (oonf_layer2_data_get_origin(&l2net->neighdata[i]) == old_origin) {
+       oonf_layer2_data_set_origin(&l2net->neighdata[i], new_origin);
+     }
+   }
+   avl_for_each_element(&l2net->local_peer_ips, peer_ip, _net_node) {
+     if (peer_ip->origin == old_origin) {
+       peer_ip->origin = new_origin;
+     }
+   }
+   avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
+     oonf_layer2_neigh_relabel(l2neigh, new_origin, old_origin);
+   }
+ }
+ /**
+  * Add an IP address or prefix to a layer-2 interface. This represents
+  * an address of the local radio or modem.
+  * @param l2net layer-2 network object
+  * @param ip ip address or prefix
+  * @return layer2 ip address object, NULL if out of memory
+  */
+ struct oonf_layer2_peer_address *
+ oonf_layer2_net_add_ip(
+   struct oonf_layer2_net *l2net, const struct oonf_layer2_origin *origin, const struct netaddr *ip) {
+   struct oonf_layer2_peer_address *l2addr;
+   l2addr = oonf_layer2_net_get_local_ip(l2net, ip);
+   if (!l2addr) {
+     l2addr = oonf_class_malloc(&_l2net_addr_class);
+     if (!l2addr) {
+       return NULL;
+     }
+     /* copy data */
+     memcpy(&l2addr->ip, ip, sizeof(*ip));
+     /* set back reference */
+     l2addr->l2net = l2net;
+     /* add to tree */
+     l2addr->_net_node.key = &l2addr->ip;
+     avl_insert(&l2net->local_peer_ips, &l2addr->_net_node);
+     l2addr->_global_node.key = &l2addr->ip;
+     avl_insert(&_local_peer_ips_tree, &l2addr->_global_node);
+     oonf_class_event(&_l2net_addr_class, l2addr, OONF_OBJECT_ADDED);
+   }
+   l2addr->origin = origin;
+   return l2addr;
+ }
+ /**
+  * Remove a peer IP address from a layer2 network
+  * @param ip ip address or prefix
+  * @param origin origin of IP address
+  * @return 0 if IP was removed, -1 if it was registered to a different origin
+  */
+ int
+ oonf_layer2_net_remove_ip(struct oonf_layer2_peer_address *ip, const struct oonf_layer2_origin *origin) {
+   if (ip->origin != origin) {
+     return -1;
+   }
+   oonf_class_event(&_l2net_addr_class, ip, OONF_OBJECT_REMOVED);
+   avl_remove(&ip->l2net->local_peer_ips, &ip->_net_node);
+   avl_remove(&_local_peer_ips_tree, &ip->_global_node);
+   oonf_class_free(&_l2net_addr_class, ip);
+   return 0;
+ }
+ /**
+  * Look for the best matching prefix in all layer2 neighbor addresses
+  * that contains a specific address
+  * @param addr ip address to look for
+  * @return layer2 neighbor address object, NULL if no match was found
+  */
+ struct oonf_layer2_neighbor_address *
+ oonf_layer2_net_get_best_neighbor_match(const struct netaddr *addr) {
+   struct oonf_layer2_neighbor_address *best_match, *l2addr;
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_net *l2net;
+   int prefix_length;
+   prefix_length = 256;
+   best_match = NULL;
+   avl_for_each_element(&_oonf_layer2_net_tree, l2net, _node) {
+     avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
+       avl_for_each_element(&l2neigh->remote_neighbor_ips, l2addr, _neigh_node) {
+         if (netaddr_is_in_subnet(&l2addr->ip, addr) && netaddr_get_prefix_length(&l2addr->ip) < prefix_length) {
+           best_match = l2addr;
+           prefix_length = netaddr_get_prefix_length(&l2addr->ip);
+         }
+       }
+     }
+   }
+   return best_match;
+ }
+ /**
+  * Generate a layer2 key based on an originator and a MAC. Enumerate
+  * new keys without explicitly storing them.
+  * @param key destination buffer for key
+  * @param origin originator for link-id creation
+  * @param mac mac address part of key
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ oonf_layer2_neigh_generate_lid(struct oonf_layer2_neigh_key *key,
+     struct oonf_layer2_origin *origin, const struct netaddr *mac) {
+   struct oonf_layer2_lid *lid;
+   uint32_t u32;
+   if (!origin->lid) {
+     return -1;
+   }
+   if (netaddr_get_address_family(mac) != AF_MAC48 && netaddr_get_address_family(mac) != AF_EUI64) {
+     return -1;
+   }
+   if (!(lid = avl_find_element(&_lid_tree, mac, lid, _node))) {
+     lid = oonf_class_malloc(&_lid_class);
+     if (!lid) {
+       return -1;
+     }
+     memcpy(&lid->mac, mac, sizeof(*mac));
+     lid->_node.key = &lid->mac;
+     avl_insert(&_lid_tree, &lid->_node);
+     lid->next_id = 1;
+   }
+   /* copy mac */
+   memcpy(&key->addr, mac, sizeof(*mac));
+   /* TODO: make LID length configurable */
+   /* generate new link-id */
+   u32 = htonl(lid->next_id);
+   memcpy(&key->link_id[0], &u32, 4);
+   key->link_id_length = 4;
+   /* keep track which originator orderer this LID */
+   key->link_id[0] = origin->lid_index & 0xff;
+   lid->next_id++;
+   return 0;
+ }
+ /**
+  * Add a layer-2 neighbor to a addr.
+  * @param l2net layer-2 addr object
+  * @param key unique key for neighbor
+  * @return layer-2 neighbor object
+  */
+ struct oonf_layer2_neigh *
+ oonf_layer2_neigh_add_lid(struct oonf_layer2_net *l2net, const struct oonf_layer2_neigh_key *key) {
+   struct oonf_layer2_neigh *l2neigh;
+   if (netaddr_get_address_family(&key->addr) != AF_MAC48 && netaddr_get_address_family(&key->addr) != AF_EUI64) {
+     return NULL;
+   }
+   l2neigh = oonf_layer2_neigh_get_lid(l2net, key);
+   if (l2neigh) {
+     return l2neigh;
+   }
+   l2neigh = oonf_class_malloc(&_l2neighbor_class);
+   if (!l2neigh) {
+     return NULL;
+   }
+   memcpy(&l2neigh->key, key, sizeof(*key));
+   l2neigh->_node.key = &l2neigh->key;
+   l2neigh->network = l2net;
+   avl_insert(&l2net->neighbors, &l2neigh->_node);
+   avl_init(&l2neigh->destinations, avl_comp_netaddr, false);
+   avl_init(&l2neigh->remote_neighbor_ips, avl_comp_netaddr, false);
+   oonf_class_event(&_l2neighbor_class, l2neigh, OONF_OBJECT_ADDED);
+   return l2neigh;
+ }
+ /**
+  * Remove all data objects of a certain originator from a layer-2 neighbor
+  * object.
+  * @param l2neigh layer-2 neighbor
+  * @param origin originator number
+  * @return true if a value was resetted, false otherwise
+  */
+ bool
+ oonf_layer2_neigh_cleanup(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin) {
+   bool changed = false;
+   int i;
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (l2neigh->data[i]._origin == origin) {
+       oonf_layer2_data_reset(&l2neigh->data[i]);
+       changed = true;
+     }
+   }
+   return changed;
+ }
+ /**
+  * Remove all information of a certain originator from a layer-2 neighbor
+  * object. Remove the object if its empty.
+  * @param l2neigh layer-2 neighbor object
+  * @param origin originator number
+  * @return true if something was change, false otherwise
+  */
+ bool
+ oonf_layer2_neigh_remove(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin) {
+   struct oonf_layer2_destination *l2dst, *l2dst_it;
+   struct oonf_layer2_neighbor_address *l2ip, *l2ip_it;
+   bool changed = false;
+   if (!avl_is_node_added(&l2neigh->_node)) {
+     return false;
+   }
+   avl_for_each_element_safe(&l2neigh->destinations, l2dst, _node, l2dst_it) {
+     if (l2dst->origin == origin) {
+       oonf_layer2_destination_remove(l2dst);
+       changed = true;
+     }
+   }
+   avl_for_each_element_safe(&l2neigh->remote_neighbor_ips, l2ip, _neigh_node, l2ip_it) {
+     if (oonf_layer2_neigh_remove_ip(l2ip, origin) == 0) {
+       changed = true;
+     }
+   }
+   if (oonf_layer2_neigh_cleanup(l2neigh, origin)) {
+     changed = true;
+   }
+   if (changed) {
+     oonf_layer2_neigh_commit(l2neigh);
+   }
+   return changed;
+ }
+ /**
+  * Commit all changes to a layer-2 neighbor object. This might remove the
+  * object from the database if all data has been removed from the object.
+  * @param l2neigh layer-2 neighbor object
+  * @return true if the object has been removed, false otherwise
+  */
+ bool
+ oonf_layer2_neigh_commit(struct oonf_layer2_neigh *l2neigh) {
+   size_t i;
+   if (l2neigh->destinations.count > 0 || l2neigh->remote_neighbor_ips.count > 0) {
+     oonf_class_event(&_l2neighbor_class, l2neigh, OONF_OBJECT_CHANGED);
++    l2neigh->modified = OONF_LAYER2_NEIGH_MODIFY_NONE;
+     return false;
+   }
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (oonf_layer2_data_has_value(&l2neigh->data[i])) {
+       oonf_class_event(&_l2neighbor_class, l2neigh, OONF_OBJECT_CHANGED);
++      l2neigh->modified = OONF_LAYER2_NEIGH_MODIFY_NONE;
+       return false;
+     }
+   }
+   _neigh_remove(l2neigh);
+   return true;
+ }
+ /**
+  * Relabel all neighbor data of one origin to another one
+  * @param l2neigh layer2 neighbor object
+  * @param new_origin new origin
+  * @param old_origin old origin to overwrite
+  */
+ void
+ oonf_layer2_neigh_relabel(struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *new_origin,
+   const struct oonf_layer2_origin *old_origin) {
+   struct oonf_layer2_neighbor_address *neigh_ip;
+   struct oonf_layer2_destination *l2dst;
+   size_t i;
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (oonf_layer2_data_get_origin(&l2neigh->data[i]) == old_origin) {
+       oonf_layer2_data_set_origin(&l2neigh->data[i], new_origin);
+     }
+   }
+   avl_for_each_element(&l2neigh->remote_neighbor_ips, neigh_ip, _neigh_node) {
+     if (neigh_ip->origin == old_origin) {
+       neigh_ip->origin = new_origin;
+     }
+   }
+   avl_for_each_element(&l2neigh->destinations, l2dst, _node) {
+     if (l2dst->origin == old_origin) {
+       l2dst->origin = new_origin;
+     }
+   }
+ }
+ /**
++* Sets the (ip) next hop of a neighbor, you should call oonf_layer2_neigh_commit after a
++* successful change.
++* @param neigh layer2 neighbor
++* @param nexthop next hop, should be IPv4 or IPv6
++* @return -1 if nothing was changed, 0 if the next hop was updated.
++*/
++int
++oonf_layer2_neigh_set_nexthop(struct oonf_layer2_neigh *neigh, const struct netaddr *nexthop) {
++  enum oonf_layer2_neigh_mods mod;
++  struct netaddr *nh;
++
++  switch (netaddr_get_address_family(nexthop)) {
++    case AF_INET:
++      nh = &neigh->_next_hop_v4;
++      mod = OONF_LAYER2_NEIGH_MODIFY_NEXTHOP_V4;
++      break;
++    case AF_INET6:
++      nh = &neigh->_next_hop_v6;
++      mod = OONF_LAYER2_NEIGH_MODIFY_NEXTHOP_V6;
++      break;
++    default:
++      return -1;
++  }
++
++  if (memcmp(nh, nexthop, sizeof(*nexthop)) == 0) {
++    return -1;
++  }
++
++  memcpy(nh, nexthop, sizeof(*nexthop));
++  neigh->modified |= mod;
++  return 0;
++}
++
++
++/**
+  * Add an IP address or prefix to a layer-2 interface. This represents
+  * an address of the local radio or modem.
+  * @param l2neigh layer-2 neighbor object
+  * @param origin layer2 data origin
+  * @param ip ip address or prefix
+  * @return layer2 ip address object, NULL if out of memory
+  */
+ struct oonf_layer2_neighbor_address *
+ oonf_layer2_neigh_add_ip(
+   struct oonf_layer2_neigh *l2neigh, const struct oonf_layer2_origin *origin, const struct netaddr *ip) {
+   struct oonf_layer2_neighbor_address *l2addr;
+   l2addr = oonf_layer2_neigh_get_remote_ip(l2neigh, ip);
+   if (l2addr) {
+     l2addr->origin = origin;
+     return l2addr;
+   }
+   l2addr = oonf_class_malloc(&_l2neigh_addr_class);
+   if (!l2addr) {
+     return NULL;
+   }
+   /* copy data */
+   memcpy(&l2addr->ip, ip, sizeof(*ip));
+   /* set back reference */
+   l2addr->l2neigh = l2neigh;
+   /* add to tree */
+   l2addr->_neigh_node.key = &l2addr->ip;
+   avl_insert(&l2neigh->remote_neighbor_ips, &l2addr->_neigh_node);
+   l2addr->_net_node.key = &l2addr->ip;
+   avl_insert(&l2neigh->network->remote_neighbor_ips, &l2addr->_net_node);
+   /* remember originator */
+   l2addr->origin = origin;
+   oonf_class_event(&_l2neigh_addr_class, l2addr, OONF_OBJECT_ADDED);
+   return l2addr;
+ }
+ /**
+  * Remove a neighbor IP address from a layer2 neighbor
+  * @param ip ip address or prefix
+  * @param origin origin of IP address
+  * @return 0 if IP was removed, -1 if it was registered to a different origin
+  */
+ int
+ oonf_layer2_neigh_remove_ip(struct oonf_layer2_neighbor_address *ip, const struct oonf_layer2_origin *origin) {
+   if (ip->origin != origin) {
+     return -1;
+   }
+   oonf_class_event(&_l2neigh_addr_class, ip, OONF_OBJECT_REMOVED);
+   avl_remove(&ip->l2neigh->remote_neighbor_ips, &ip->_neigh_node);
+   avl_remove(&ip->l2neigh->network->remote_neighbor_ips, &ip->_net_node);
+   oonf_class_free(&_l2neigh_addr_class, ip);
+   return 0;
+ }
+ /**
+  * add a layer2 destination (a MAC address behind a neighbor) to
+  * the layer2 database
+  * @param l2neigh layer2 neighbor of the destination
+  * @param destination destination address
+  * @param origin layer2 origin
+  * @return layer2 destination, NULL if out of memory
+  */
+ struct oonf_layer2_destination *
+ oonf_layer2_destination_add(
+   struct oonf_layer2_neigh *l2neigh, const struct netaddr *destination, const struct oonf_layer2_origin *origin) {
+   struct oonf_layer2_destination *l2dst;
+   l2dst = oonf_layer2_destination_get(l2neigh, destination);
+   if (l2dst) {
+     return l2dst;
+   }
+   l2dst = oonf_class_malloc(&_l2dst_class);
+   if (!l2dst) {
+     return NULL;
+   }
+   /* copy data into destination storage */
+   memcpy(&l2dst->destination, destination, sizeof(*destination));
+   l2dst->origin = origin;
+   /* add back-pointer */
+   l2dst->neighbor = l2neigh;
+   /* add to neighbor tree */
+   l2dst->_node.key = &l2dst->destination;
+   avl_insert(&l2neigh->destinations, &l2dst->_node);
+   oonf_class_event(&_l2dst_class, l2dst, OONF_OBJECT_ADDED);
+   return l2dst;
+ }
+ /**
+  * Remove a layer2 destination
+  * @param l2dst layer2 destination
+  */
+ void
+ oonf_layer2_destination_remove(struct oonf_layer2_destination *l2dst) {
+   if (!avl_is_node_added(&l2dst->_node)) {
+     return;
+   }
+   oonf_class_event(&_l2dst_class, l2dst, OONF_OBJECT_REMOVED);
+   avl_remove(&l2dst->neighbor->destinations, &l2dst->_node);
+   oonf_class_free(&_l2dst_class, l2dst);
+ }
+ /**
+  * Get neighbor specific data, either from neighbor or from the networks default
+  * @param ifname name of interface
+  * @param l2neigh_addr neighbor mac address
+  * @param idx data index
+  * @param get_default true to return default (net) data if no neighbor data available
+  * @return pointer to linklayer data, NULL if no value available
+  */
+ struct oonf_layer2_data *
+ oonf_layer2_neigh_query(const char *ifname, const struct netaddr *l2neigh_addr,
+       enum oonf_layer2_neighbor_index idx, bool get_default) {
+   struct oonf_layer2_net *l2net;
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_data *data;
+   /* query layer2 database about neighbor */
+   l2net = oonf_layer2_net_get(ifname);
+   if (l2net == NULL) {
+     return NULL;
+   }
+   /* look for neighbor specific data */
+   l2neigh = oonf_layer2_neigh_get(l2net, l2neigh_addr);
+   if (l2neigh != NULL) {
+     data = &l2neigh->data[idx];
+     if (oonf_layer2_data_has_value(data)) {
+       return data;
+     }
+   }
+   if (!get_default) {
+     return NULL;
+   }
+   /* look for network specific default */
+   data = &l2net->neighdata[idx];
+   if (oonf_layer2_data_has_value(data)) {
+     return data;
+   }
+   return NULL;
+ }
+ /**
+  * Get neighbor specific data, add interface and neighbor if necessary
+  * @param ifname name of interface
+  * @param l2neigh_addr neighbor mac address
+  * @param idx data index
+  * @return pointer to linklayer data, NULL if no value available
+  */
+ struct oonf_layer2_data *
+ oonf_layer2_neigh_add_path(const char *ifname, const struct netaddr *l2neigh_addr, enum oonf_layer2_neighbor_index idx) {
+     struct oonf_layer2_net *l2net;
+   struct oonf_layer2_neigh *l2neigh;
+   /* query layer2 database about neighbor */
+   l2net = oonf_layer2_net_add(ifname);
+   if (l2net == NULL) {
+     return NULL;
+   }
+   /* look for neighbor specific data */
+   l2neigh = oonf_layer2_neigh_add(l2net, l2neigh_addr);
+   if (l2neigh == NULL) {
+     return NULL;
+   }
+   return &l2neigh->data[idx];
+ }
+ /**
+  * Get neighbor specific data, either from neighbor or from the networks default
+  * @param l2neigh pointer to layer2 neighbor
+  * @param idx data index
+  * @return pointer to linklayer data, NULL if no value available
+  */
+ struct oonf_layer2_data *
+ oonf_layer2_neigh_get_data(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx) {
+   struct oonf_layer2_data *data;
+   data = &l2neigh->data[idx];
+   if (oonf_layer2_data_has_value(data)) {
+     return data;
+   }
+   /* look for network specific default */
+   data = &l2neigh->network->neighdata[idx];
+   if (oonf_layer2_data_has_value(data)) {
+     return data;
+   }
+   return NULL;
+ }
+ /**
+  * get neighbor metric metadata
+  * @param idx neighbor metric index
+  * @return metadata object
+  */
+ const struct oonf_layer2_metadata *
+ oonf_layer2_neigh_metadata_get(enum oonf_layer2_neighbor_index idx) {
+   return &_metadata_neigh[idx];
+ }
+ /**
+  * get network metric metadata
+  * @param idx network metric index
+  * @return metadata object
+  */
+ const struct oonf_layer2_metadata *
+ oonf_layer2_net_metadata_get(enum oonf_layer2_network_index idx) {
+   return &_metadata_net[idx];
+ }
+ /**
+  * Callback for configuration choice of layer2 network key
+  * @param idx index
+  * @param unused not used
+  * @return pointer to network key
+  */
+ const char *
+ oonf_layer2_cfg_get_l2net_key(size_t idx, const void *unused __attribute__((unused))) {
+   return _metadata_net[idx].key;
+ }
+ /**
+  * Callback for configuration choice of layer2 neighbor key
+  * @param idx index
+  * @param unused not used
+  * @return pointer to neighbor key
+  */
+ const char *
+ oonf_layer2_cfg_get_l2neigh_key(size_t idx, const void *unused __attribute__((unused))) {
+   return _metadata_neigh[idx].key;
+ }
+ /**
+  * Callback for configuration choice of layer2 neighbor key
+  * @param idx index
+  * @param unused not used
+  * @return pointer to neighbor key
+  */
+ const char *
+ oonf_layer2_cfg_get_l2comp(size_t idx, const void *unused __attribute__((unused))) {
+   return _data_comparators[idx];
+ }
+ /**
+  * get text representation of network type
+  * @param type network type
+  * @return text representation
+  */
+ const char *
+ oonf_layer2_net_get_type_name(enum oonf_layer2_network_type type) {
+   return _network_type[type];
+ }
+ /**
+  * get tree of layer2 networks
+  * @return network tree
+  */
+ struct avl_tree *
+ oonf_layer2_get_net_tree(void) {
+   return &_oonf_layer2_net_tree;
+ }
+ /**
+  * get tree of layer2 originators
+  * @return originator tree
+  */
+ struct avl_tree *
+ oonf_layer2_get_origin_tree(void) {
+   return &_oonf_originator_tree;
+ }
+ /**
+  * Compares two layer2 neighbor keys
+  * @param p1 pointer to first neighbor key
+  * @param p2 pointer to second neighbor key
+  * @return result of memcmp comparison
+  */
+ int
+ oonf_layer2_avlcmp_neigh_key(const void *p1, const void *p2) {
+   const struct oonf_layer2_neigh_key *k1 = p1;
+   const struct oonf_layer2_neigh_key *k2 = p2;
+   return memcmp(k1, k2, sizeof(*k1));
+ }
+ /**
+  * Converts a layer2 neighbor key into a string representation
+  * @param buf buffer for output string
+  * @param key layer2 neighbor key
+  * @param show_mac true to show MAC and Link ID, false to only show LID
+  * @return pointer to output string
+  */
+ const char *
+ oonf_layer2_neigh_key_to_string(union oonf_layer2_neigh_key_str *buf,
+     const struct oonf_layer2_neigh_key *key, bool show_mac) {
+   static const char HEX[17] = "0123456789ABCDEF";
+   static const char NONE[] = "-";
+   size_t str_idx, lid_idx;
+   uint8_t af;
+   if (key == NULL) {
+     return NONE;
+   }
+   af = netaddr_get_address_family(&key->addr);
+   if (af != AF_MAC48 && af != AF_EUI64) {
+     return NONE;
+   }
+   if (show_mac) {
+     netaddr_to_string(&buf->nbuf, &key->addr);
+   }
+   else {
+     buf->buf[0] = 0;
+   }
+   if (key->link_id_length == 0) {
+     return buf->buf;
+   }
+   str_idx = strlen(buf->buf);
+   lid_idx = 0;
+   if (show_mac) {
+     buf->buf[str_idx++] = ' ';
+     buf->buf[str_idx++] = '[';
+     buf->buf[str_idx++] = '0';
+     buf->buf[str_idx++] = 'x';
+   }
+   while (str_idx + 4 < sizeof(*buf) && lid_idx < key->link_id_length) {
+     buf->buf[str_idx++] = HEX[key->link_id[lid_idx] >> 4];
+     buf->buf[str_idx++] = HEX[key->link_id[lid_idx] & 15];
+     lid_idx++;
+   }
+   if (show_mac) {
+     buf->buf[str_idx++] = ']';
+     buf->buf[str_idx++] = 0;
+   }
+   return buf->buf;
+ }
+ /**
+  * Removes a layer-2 addr object from the database.
+  * @param l2net layer-2 addr object
+  */
+ static void
+ _net_remove(struct oonf_layer2_net *l2net) {
+   struct oonf_layer2_neigh *l2neigh, *l2n_it;
+   struct oonf_layer2_peer_address *l2peer, *l2peer_it;
+   /* free all embedded neighbors */
+   avl_for_each_element_safe(&l2net->neighbors, l2neigh, _node, l2n_it) {
+     _neigh_remove(l2neigh);
+   }
+   /* free all attached peer addresses */
+   avl_for_each_element_safe(&l2net->local_peer_ips, l2peer, _net_node, l2peer_it) {
+     oonf_layer2_net_remove_ip(l2peer, l2peer->origin);
+   }
+   oonf_class_event(&_l2network_class, l2net, OONF_OBJECT_REMOVED);
+   /* remove interface listener */
+   os_interface_remove(&l2net->if_listener);
+   /* free addr */
+   avl_remove(&_oonf_layer2_net_tree, &l2net->_node);
+   oonf_class_free(&_l2network_class, l2net);
+ }
+ /**
+  * Removes a layer-2 neighbor object from the database
+  * @param l2neigh layer-2 neighbor object
+  */
+ static void
+ _neigh_remove(struct oonf_layer2_neigh *l2neigh) {
+   struct oonf_layer2_destination *l2dst, *l2dst_it;
+   struct oonf_layer2_neighbor_address *l2addr, *l2addr_it;
+   /* free all embedded destinations */
+   avl_for_each_element_safe(&l2neigh->destinations, l2dst, _node, l2dst_it) {
+     oonf_layer2_destination_remove(l2dst);
+   }
+   /* free all attached neighbor addresses */
+   avl_for_each_element_safe(&l2neigh->remote_neighbor_ips, l2addr, _neigh_node, l2addr_it) {
+     oonf_layer2_neigh_remove_ip(l2addr, l2addr->origin);
+   }
+   /* inform user that mac entry will be removed */
+   oonf_class_event(&_l2neighbor_class, l2neigh, OONF_OBJECT_REMOVED);
+   /* free resources for mac entry */
+   avl_remove(&l2neigh->network->neighbors, &l2neigh->_node);
+   oonf_class_free(&_l2neighbor_class, l2neigh);
+ }
Simple merge
index 0000000,fa1adff..87f898b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1112 +1,1112 @@@
 -  avl_init(&session->_ip_prefix_modification, avl_comp_netaddr, false);
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/libcommon/avl_comp.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcore/oonf_logging.h>
+ #include <oonf/base/oonf_class.h>
+ #include <oonf/base/oonf_stream_socket.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_extension.h>
+ #include <oonf/generic/dlep/dlep_session.h>
+ #include <oonf/generic/dlep/dlep_writer.h>
+ /**
+  * internal constants of DLEP session
+  */
+ enum
+ {
+   /*! size increase step for DLEP value storage */
+   SESSION_VALUE_STEP = 128,
+ };
+ static int _update_allowed_tlvs(struct dlep_session *session);
+ static enum dlep_parser_error _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t length);
+ static enum dlep_parser_error _check_mandatory(
+   struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type);
+ static enum dlep_parser_error _check_duplicate(
+   struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type);
+ static enum dlep_parser_error _call_extension_processing(
+   struct dlep_session *parser, struct dlep_extension *ext, int32_t signal_type);
+ static struct dlep_parser_tlv *_add_session_tlv(struct dlep_session_parser *parser, uint16_t id);
+ static enum dlep_parser_error _handle_extension(
+   struct dlep_session *session, struct dlep_extension *ext, uint32_t signal_type);
+ static enum dlep_parser_error _process_tlvs(
+   struct dlep_session *, int32_t signal_type, uint16_t signal_length, const uint8_t *tlvs);
+ static void _send_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text);
+ static void _cb_destination_timeout(struct oonf_timer_instance *);
+ static struct oonf_class _tlv_class = {
+   .name = "dlep reader tlv",
+   .size = sizeof(struct dlep_parser_tlv),
+ };
+ static struct oonf_class _local_neighbor_class = {
+   .name = "dlep neighbor",
+   .size = sizeof(struct dlep_local_neighbor),
+ };
+ static struct oonf_timer_class _destination_ack_class = {
+   .name = "dlep destination ack",
+   .callback = _cb_destination_timeout,
+ };
+ /**
+  * Initialize DLEP session system
+  */
+ void
+ dlep_session_init(void) {
+   oonf_class_add(&_tlv_class);
+   oonf_class_add(&_local_neighbor_class);
+   oonf_timer_add(&_destination_ack_class);
+ }
+ /**
+  * Initialize a session, will hook in the base extension
+  * @param session session to initialize
+  * @param l2_ifname name of layer2 interface for dlep session
+  * @param l2_origin layer2 db origin id for session
+  * @param l2_default_origin layer2 originator that shall be used for setting defaults
+  * @param out output buffer for session
+  * @param radio true if this is a radio session,
+  *   false for a router session
+  * @param if_changed interface listener for session, can be NULL
+  * @param log_source logging source for session
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_session_add(struct dlep_session *session, const char *l2_ifname, const struct oonf_layer2_origin *l2_origin,
+   const struct oonf_layer2_origin *l2_default_origin, struct autobuf *out, bool radio,
+   int (*if_changed)(struct os_interface_listener *), enum oonf_log_source log_source) {
+   struct dlep_session_parser *parser;
+   struct dlep_extension *ext;
+   parser = &session->parser;
+   avl_init(&parser->allowed_tlvs, avl_comp_uint16, false);
+   avl_init(&session->local_neighbor_tree, avl_comp_netaddr, false);
+   session->log_source = log_source;
+   session->l2_origin = l2_origin;
+   session->l2_default_origin = l2_default_origin;
+   session->radio = radio;
+   session->writer.out = out;
+   session->_peer_state = DLEP_PEER_WAIT_FOR_INIT;
+   /* remember interface name */
+   session->l2_listener.name = l2_ifname;
+   session->l2_listener.if_changed = if_changed;
+   /* get interface listener to lock interface */
+   if (!os_interface_add(&session->l2_listener)) {
+     OONF_WARN(session->log_source, "Cannot activate interface listener for %s", l2_ifname);
+     dlep_session_remove(session);
+     return -1;
+   }
+   parser->values = calloc(SESSION_VALUE_STEP, sizeof(struct dlep_parser_value));
+   if (!parser->values) {
+     OONF_WARN(session->log_source, "Cannot allocate values buffer for %s", l2_ifname);
+     dlep_session_remove(session);
+     return -1;
+   }
+   /* generate full list of extensions */
+   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
+     OONF_DEBUG(session->log_source, "Add extension %d to session", ext->id);
+     parser->extensions[parser->extension_count] = ext;
+     parser->extension_count++;
+   }
+   if (_update_allowed_tlvs(session)) {
+     OONF_WARN(session->log_source, "Could not update allowed TLVs for %s", l2_ifname);
+     dlep_session_remove(session);
+     return -1;
+   }
++  avl_init(&session->_ext_ip.prefix_modification, avl_comp_netaddr, false);
+   OONF_INFO(session->log_source, "Add session on %s", session->l2_listener.name);
+   return 0;
+ }
+ /**
+  * Remove a DLEP session
+  * @param session dlep session
+  */
+ void
+ dlep_session_remove(struct dlep_session *session) {
+   struct dlep_parser_tlv *tlv, *tlv_it;
+   struct dlep_session_parser *parser;
+ #ifdef OONF_LOG_DEBUG_INFO
+   struct netaddr_str nbuf;
+ #endif
+   OONF_DEBUG(session->log_source, "Remove session if %s to %s", session->l2_listener.name,
+     netaddr_socket_to_string(&nbuf, &session->remote_socket));
+   os_interface_remove(&session->l2_listener);
+   parser = &session->parser;
+   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
+     avl_remove(&parser->allowed_tlvs, &tlv->_node);
+     oonf_class_free(&_tlv_class, tlv);
+   }
+   oonf_timer_stop(&session->local_event_timer);
+   oonf_timer_stop(&session->remote_heartbeat_timeout);
+   parser->extension_count = 0;
+   free(parser->values);
+   parser->values = NULL;
+   session->_peer_state = DLEP_PEER_NOT_CONNECTED;
+ }
+ /**
+  * Send peer termination
+  * @param session dlep session
+  * @param status DLEP status code for termination
+  * @param status_text text message for termination
+  */
+ void
+ dlep_session_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text) {
+   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
+     dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, status, status_text);
+     session->cb_send_buffer(session, 0);
+   }
+   session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
+ }
+ /**
+  * Update the list of active dlep extensions for a session
+  * @param session dlep session
+  * @param extvalues array with allowed DLEP sessions
+  * @param extcount number of bytes in array
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_session_update_extensions(struct dlep_session *session, const uint8_t *extvalues, size_t extcount, bool radio) {
+   struct dlep_extension *ext;
+   size_t i, j;
+   bool deactivate;
+   uint16_t extid;
+   OONF_INFO(session->log_source, "Update session extension list");
+   /* deactivate all extensions not present anymore  */
+   for (j = DLEP_EXTENSION_BASE_COUNT; j < session->parser.extension_count; j++) {
+     deactivate = true;
+     for (i = 0; i < extcount; i++) {
+       memcpy(&extid, &extvalues[i * 2], sizeof(extid));
+       if (ntohs(extid) == session->parser.extensions[j]->id) {
+         deactivate = false;
+         break;
+       }
+     }
+     if (deactivate) {
+       if (radio) {
+         session->parser.extensions[j]->cb_session_deactivate_radio(session);
+       }
+       else {
+         session->parser.extensions[j]->cb_session_deactivate_router(session);
+       }
+     }
+   }
+   /* generate new session extension list */
+   session->parser.extension_count = DLEP_EXTENSION_BASE_COUNT;
+   for (i = 0; i < extcount; i++) {
+     memcpy(&extid, &extvalues[i * 2], sizeof(extid));
+     ext = dlep_extension_get(ntohs(extid));
+     if (ext) {
+       OONF_INFO(session->log_source, "Add extension: %d", ntohs(extid));
+       session->parser.extensions[session->parser.extension_count] = ext;
+       session->parser.extension_count++;
+     }
+   }
+   return _update_allowed_tlvs(session);
+ }
+ /**
+  * Process data in DLEP session TCP input buffer
+  * @param tcp_session TCP session
+  * @param session DLEP session
+  * @return new TCP session state
+  */
+ enum oonf_stream_session_state
+ dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_session *session)
+ {
+   ssize_t processed;
+   OONF_DEBUG(
+     session->log_source, "Process TCP buffer of %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(&tcp_session->in));
+   processed = dlep_session_process_buffer(session, abuf_getptr(&tcp_session->in), abuf_getlen(&tcp_session->in), false);
+   if (processed < 0) {
+     /* session is most likely invalid now */
+     return STREAM_SESSION_CLEANUP;
+   }
+   if (session->restrict_signal == DLEP_KILL_SESSION) {
+     return STREAM_SESSION_CLEANUP;
+   }
+   OONF_DEBUG(session->log_source, "Processed %" PRINTF_SSIZE_T_SPECIFIER " bytes", processed);
+   abuf_pull(&tcp_session->in, processed);
+   if (abuf_getlen(session->writer.out) > 0) {
+     OONF_DEBUG(
+       session->log_source, "Trigger sending %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out));
+     /* send answer */
+     oonf_stream_flush(tcp_session);
+   }
+   if (session->restrict_signal == DLEP_KILL_SESSION) {
+     return STREAM_SESSION_CLEANUP;
+   }
+   return STREAM_SESSION_ACTIVE;
+ }
+ /**
+  * Process the content of a buffer as DLEP signal(s)
+  * @param session dlep session
+  * @param buffer pointer to buffer
+  * @param length length of buffer
+  * @return number of bytes of buffer which were parsed and
+  *   can be removed, -1 if an error happened
+  */
+ ssize_t
+ dlep_session_process_buffer(struct dlep_session *session, const void *buffer, size_t length, bool is_udp) {
+   ssize_t result, offset;
+   const char *ptr;
+   offset = 0;
+   ptr = buffer;
+   OONF_DEBUG(session->log_source,
+     "Processing buffer of"
+     " %" PRINTF_SIZE_T_SPECIFIER " bytes",
+     length);
+   while (length > 0) {
+     OONF_DEBUG(session->log_source,
+       "Processing message at offset"
+       " %" PRINTF_SSIZE_T_SPECIFIER,
+       offset);
+     if ((result = dlep_session_process_signal(session, &ptr[offset], length, is_udp)) <= 0) {
+       if (result < 0) {
+         return result;
+       }
+       break;
+     }
+     if (session->restrict_signal == DLEP_KILL_SESSION) {
+       return offset;
+     }
+     length -= result;
+     offset += result;
+   }
+   return offset;
+ }
+ /**
+  * Process a DLEP signal/message
+  * @param session dlep session
+  * @param ptr pointer to buffer with DLEP signal/message
+  * @param length length of buffer
+  * @return number of bytes parsed, 0 if a generic error happened,
+  *   negative to return a parser result enum
+  */
+ ssize_t
+ dlep_session_process_signal(struct dlep_session *session, const void *ptr, size_t length, bool is_udp) {
+   enum dlep_parser_error result;
+   uint16_t original_signal_type;
+   int32_t signal_type;
+   uint16_t signal_length;
+   const uint8_t *buffer;
+ #ifdef OONF_LOG_DEBUG_INFO
+   struct netaddr_str nbuf;
+ #endif
+   session->next_restrict_signal = DLEP_KEEP_RESTRICTION;
+   if (length < 4) {
+     /* not enough data for a signal type */
+     OONF_DEBUG(session->log_source,
+       "Not enough data to process"
+       " signal from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
+       netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
+     return 0;
+   }
+   buffer = ptr;
+   /* copy data */
+   memcpy(&original_signal_type, &buffer[0], sizeof(original_signal_type));
+   memcpy(&signal_length, &buffer[2], sizeof(signal_length));
+   signal_type = ntohs(original_signal_type);
+   signal_length = ntohs(signal_length);
+   if (is_udp) {
+     signal_type += DLEP_IS_UDP_SIGNAL;
+   }
+   if (length < (size_t)signal_length + 4u) {
+     /* not enough data for signal */
+     OONF_DEBUG(session->log_source,
+       "Not enough data to process"
+       " signal %u (length %u) from %s"
+       " (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
+       signal_type, signal_length, netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
+     return 0;
+   }
+   OONF_DEBUG_HEX(session->log_source, buffer, signal_length + 4,
+     "Process signal %d from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)", signal_type,
+     netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
+   if (session->restrict_signal != DLEP_ALL_SIGNALS && session->restrict_signal != signal_type) {
+     OONF_DEBUG(session->log_source,
+       "Signal should have been %d,"
+       " drop session",
+       session->restrict_signal);
+     /* we only accept a single type and we got the wrong one */
+     return -1;
+   }
+   result = _process_tlvs(session, signal_type, signal_length, &buffer[4]);
+   if (result == DLEP_NEW_PARSER_TERMINDATED) {
+     /* session is now invalid, end parser */
+     return result;
+   }
+   if (result != DLEP_NEW_PARSER_OKAY) {
+     OONF_WARN(session->log_source, "Parser error: %d", result);
+     _send_terminate(session, DLEP_STATUS_INVALID_DATA, "Incoming signal could not be parsed");
+   }
+   else if (session->next_restrict_signal != DLEP_KEEP_RESTRICTION) {
+     session->restrict_signal = session->next_restrict_signal;
+   }
+   /* skip forward */
+   return signal_length + 4;
+ }
+ /**
+  * Add a neighbor to the local DLEP storage
+  * @param session dlep session
+  * @param key neighbor key (MAC plus link id)
+  * @return pointer to dlep neighbor, NULL if out of memory
+  */
+ struct dlep_local_neighbor *
+ dlep_session_add_local_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key) {
+   struct dlep_local_neighbor *local;
+   if ((local = dlep_session_get_local_neighbor(session, key))) {
+     return local;
+   }
+   if (key->link_id_length != 0 && key->link_id_length != session->cfg.lid_length) {
+     /* LIDs not allowed */
+     return NULL;
+   }
+   local = oonf_class_malloc(&_local_neighbor_class);
+   if (!local) {
+     return NULL;
+   }
+   /* hook into tree */
+   memcpy(&local->key, key, sizeof(*key));
+   local->_node.key = &local->key;
+   avl_insert(&session->local_neighbor_tree, &local->_node);
+   /* initialize timer */
+   local->_ack_timeout.class = &_destination_ack_class;
+   /* initialize backpointer */
+   local->session = session;
+   avl_init(&local->_ip_prefix_modification, avl_comp_netaddr, false);
+   return local;
+ }
+ /**
+  * Remove a neighbor from the DLEP storage
+  * @param session dlep session
+  * @param local DLEP neighbor
+  */
+ void
+ dlep_session_remove_local_neighbor(struct dlep_session *session, struct dlep_local_neighbor *local) {
+   avl_remove(&session->local_neighbor_tree, &local->_node);
+   oonf_timer_stop(&local->_ack_timeout);
+   oonf_class_free(&_local_neighbor_class, local);
+ }
+ /**
+  * Get the layer2 neigbor for a DLEP session MAC address
+  * @param session dlep session
+  * @param key neighbor key (MAC address plus link id)
+  * @return layer2 neighbor, NULL if not found
+  */
+ struct oonf_layer2_neigh *
+ dlep_session_get_local_l2_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key) {
+   struct dlep_local_neighbor *dlep_neigh;
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_net *l2net;
+ #ifdef OONF_LOG_INFO
+   union oonf_layer2_neigh_key_str nbuf1, nbuf2;
+ #endif
+   dlep_neigh = dlep_session_get_local_neighbor(session, key);
+   if (!dlep_neigh) {
+     OONF_INFO(session->log_source, "Could not find local neighbor for %s",
+               oonf_layer2_neigh_key_to_string(&nbuf1, key, true));
+     return NULL;
+   }
+   l2net = oonf_layer2_net_get(session->l2_listener.name);
+   if (!l2net) {
+     OONF_DEBUG(session->log_source, "Could not find l2net %s for new neighbor", session->l2_listener.name);
+     return NULL;
+   }
+   l2neigh = oonf_layer2_neigh_get_lid(l2net, &dlep_neigh->neigh_key);
+   if (!l2neigh) {
+     OONF_INFO(session->log_source,
+       "Could not find l2neigh for neighbor %s (%s)",
+       oonf_layer2_neigh_key_to_string(&nbuf1, key, true),
+       oonf_layer2_neigh_key_to_string(&nbuf2, &dlep_neigh->neigh_key, true));
+     return NULL;
+   }
+   return l2neigh;
+ }
+ struct oonf_layer2_neigh *
+ dlep_session_get_l2_from_neighbor(struct dlep_local_neighbor *dlep_neigh) {
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_net *l2net;
+ #ifdef OONF_LOG_INFO
+   union oonf_layer2_neigh_key_str nbuf;
+ #endif
+   l2net = oonf_layer2_net_get(dlep_neigh->session->l2_listener.name);
+   if (!l2net) {
+     OONF_DEBUG(dlep_neigh->session->log_source, "Could not find l2net %s for new neighbor",
+       dlep_neigh->session->l2_listener.name);
+     return NULL;
+   }
+   l2neigh = oonf_layer2_neigh_get_lid(l2net, &dlep_neigh->neigh_key);
+   if (!l2neigh) {
+     OONF_INFO(dlep_neigh->session->log_source,
+       "Could not find l2neigh for neighbor %s",
+       oonf_layer2_neigh_key_to_string(&nbuf, &dlep_neigh->neigh_key, true));
+     return NULL;
+   }
+   return l2neigh;
+ }
+ /**
+  * Generate a DLEP signal/message
+  * @param session dlep session
+  * @param signal signal id
+  * @param neighbor neighbor MAC address the signal should refer to,
+  *   might be NULL
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _generate_signal(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor) {
+   struct dlep_extension *ext;
+   size_t e, s;
+   size_t len;
+ #ifdef OONF_LOG_DEBUG_INFO
+   union oonf_layer2_neigh_key_str nkbuf;
+   struct netaddr_str nbuf2;
+ #endif
+   OONF_DEBUG(session->log_source, "Generate signal %u for %s on %s (%s)", signal,
+              oonf_layer2_neigh_key_to_string(&nkbuf, neighbor, true),
+              session->l2_listener.name, netaddr_socket_to_string(&nbuf2, &session->remote_socket));
+   len = abuf_getlen(session->writer.out);
+   /* generate signal, mask out UDP/TCP difference */
+   dlep_writer_start_signal(&session->writer, signal & 65535);
+   for (e = 0; e < session->parser.extension_count; e++) {
+     ext = session->parser.extensions[e];
+     for (s = 0; s < ext->signal_count; s++) {
+       if (ext->signals[s].id != signal) {
+         continue;
+       }
+       if (session->radio && ext->signals[s].add_radio_tlvs) {
+         OONF_DEBUG(session->log_source, "Add tlvs for radio extension %d", ext->id);
+         if (ext->signals[s].add_radio_tlvs(ext, session, neighbor)) {
+           abuf_setlen(session->writer.out, len);
+           return -1;
+         }
+       }
+       else if (!session->radio && ext->signals[s].add_router_tlvs) {
+         OONF_DEBUG(session->log_source, "Add tlvs for router extension %d", ext->id);
+         if (ext->signals[s].add_router_tlvs(ext, session, neighbor)) {
+           abuf_setlen(session->writer.out, len);
+           return -1;
+         }
+       }
+       break;
+     }
+   }
+   OONF_DEBUG(
+     session->log_source, "generated %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out) - len);
+   return 0;
+ }
+ /**
+  * Generate a DLEP signal/message
+  * @param session dlep session
+  * @param signal signal id
+  * @param neighbor neighbor MAC address (plus link id) the signal should refer to,
+  *   might be NULL
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_session_generate_signal(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor) {
+   if (_generate_signal(session, signal, neighbor)) {
+     OONF_WARN(session->log_source, "Could not generate signal %u", signal);
+     return -1;
+   }
+   return dlep_writer_finish_signal(&session->writer, session->log_source);
+ }
+ /**
+  * Generate a DLEP signal/message with a DLEP status TLV
+  * @param session dlep session
+  * @param signal signal id
+  * @param neighbor neighbor MAC address (plus link id) the signal should refer to,
+  *   might be NULL
+  * @param status DLEP status code
+  * @param msg ZERO terminated DLEP status text
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_session_generate_signal_status(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor,
+   enum dlep_status status, const char *msg) {
+   if (_generate_signal(session, signal, neighbor)) {
+     OONF_WARN(session->log_source, "Could not generate signal %u", signal);
+     return -1;
+   }
+   if (dlep_writer_add_status(&session->writer, status, msg)) {
+     OONF_WARN(session->log_source, "Could not add status TLV");
+     return -1;
+   }
+   return dlep_writer_finish_signal(&session->writer, session->log_source);
+ }
+ /**
+  * Get the value of the first DLEP TLV of a specific type
+  * @param session dlep session
+  * @param tlvtype DLEP TLV type
+  * @return DLEP value, NULL if not found
+  */
+ struct dlep_parser_value *
+ dlep_session_get_tlv_value(struct dlep_session *session, uint16_t tlvtype) {
+   struct dlep_parser_tlv *tlv;
+   struct dlep_parser_value *value;
+   tlv = dlep_parser_get_tlv(&session->parser, tlvtype);
+   if (!tlv) {
+     OONF_INFO(session->log_source, "Could not find TLV type %u", tlvtype);
+     return NULL;
+   }
+   value = dlep_session_get_tlv_first_value(session, tlv);
+   if (!value) {
+     OONF_INFO(session->log_source, "Could not find value of TLV type %u", tlvtype);
+     return NULL;
+   }
+   else {
+     OONF_DEBUG(session->log_source, "TLV %u has value", tlvtype);
+   }
+   return value;
+ }
+ /**
+  * Update the list of allowed TLVs based on the list of allowed
+  * extensions
+  * @param session dlep session
+  * @return -1 if extensions are inconsistent, 0 otherwise
+  */
+ static int
+ _update_allowed_tlvs(struct dlep_session *session) {
+   struct dlep_session_parser *parser;
+   struct dlep_parser_tlv *tlv, *tlv_it;
+   struct dlep_extension *ext;
+   size_t e, t;
+   uint16_t id;
+   parser = &session->parser;
+   /* mark all existing allowed tlvs */
+   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
+     tlv->remove = true;
+   }
+   /* allocate new allowed tlvs structures */
+   for (e = 0; e < parser->extension_count; e++) {
+     ext = parser->extensions[e];
+     /* for all extensions */
+     for (t = 0; t < ext->tlv_count; t++) {
+       /* for all tlvs */
+       id = ext->tlvs[t].id;
+       tlv = dlep_parser_get_tlv(parser, id);
+       if (!tlv) {
+         /* new tlv found! */
+         if (!(tlv = _add_session_tlv(parser, id))) {
+           return -1;
+         }
+         tlv->length_min = ext->tlvs[t].length_min;
+         tlv->length_max = ext->tlvs[t].length_max;
+       }
+       else if (tlv->length_min != ext->tlvs[t].length_min || tlv->length_max != ext->tlvs[t].length_max) {
+         OONF_WARN(session->log_source,
+           "Two extensions conflict about"
+           " tlv %u minimal/maximum length",
+           id);
+         return -1;
+       }
+       tlv->remove = false;
+     }
+   }
+   /* remove all existing allowed tlvs that are not supported anymore */
+   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
+     if (tlv->remove) {
+       avl_remove(&parser->allowed_tlvs, &tlv->_node);
+       oonf_class_free(&_tlv_class, tlv);
+     }
+   }
+   return 0;
+ }
+ /**
+  * Check constraints of extensions and call the relevant callbacks
+  * @param session dlep session
+  * @param ext dlep extension
+  * @param signal_type signal type
+  * @return dlep parser error, 0 of everything is fine
+  */
+ static enum dlep_parser_error
+ _handle_extension(struct dlep_session *session, struct dlep_extension *ext, uint32_t signal_type) {
+   enum dlep_parser_error result;
+   bool active;
+   size_t e;
+   active = false;
+   /* only handle active extensions */
+   for (e = 0; e < session->parser.extension_count; e++) {
+     if (session->parser.extensions[e] == ext) {
+       active = true;
+       break;
+     }
+   }
+   if (!active) {
+     /* not active at the moment */
+     return DLEP_NEW_PARSER_OKAY;
+   }
+   if ((result = _check_mandatory(session, ext, signal_type))) {
+     OONF_DEBUG(session->log_source, "check_mandatory result: %d", result);
+     return result;
+   }
+   if ((result = _check_duplicate(session, ext, signal_type))) {
+     OONF_DEBUG(session->log_source, "check_duplicate result: %d", result);
+     return result;
+   }
+   if ((result = _call_extension_processing(session, ext, signal_type))) {
+     OONF_DEBUG(session->log_source, "extension processing failed: %d", result);
+     return result;
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * Parse a DLEP tlv
+  * @param session dlep session
+  * @param signal_type dlep signal/message type
+  * @param signal_length signal/message length
+  * @param tlvs pointer to bytearray with TLVs
+  * @return dlep parser status
+  */
+ static enum dlep_parser_error
+ _process_tlvs(struct dlep_session *session, int32_t signal_type, uint16_t signal_length, const uint8_t *tlvs) {
+   enum dlep_parser_error result;
+   struct dlep_extension *ext;
+   /* start at the beginning of the tlvs */
+   if ((result = _parse_tlvstream(session, tlvs, signal_length))) {
+     OONF_DEBUG(session->log_source, "parse_tlvstream result: %d", result);
+     return result;
+   }
+   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
+     if ((result = _handle_extension(session, ext, signal_type))) {
+       return result;
+     }
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * terminate a DLEP session
+  * @param session dlep session
+  * @param status DLEP status code for termination
+  * @param status_text text message for termination
+  */
+ static void
+ _send_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text) {
+   if (session->restrict_signal != DLEP_UDP_PEER_DISCOVERY && session->restrict_signal != DLEP_UDP_PEER_OFFER) {
+     dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, status, status_text);
+     session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
+     session->next_restrict_signal = DLEP_SESSION_TERMINATION_ACK;
+   }
+ }
+ /**
+  * Callback when a destination up/down signal times out
+  * @param ptr timer instance that fired
+  */
+ static void
+ _cb_destination_timeout(struct oonf_timer_instance *ptr) {
+   struct dlep_local_neighbor *local;
+   local = container_of(ptr, struct dlep_local_neighbor, _ack_timeout);
+   if (local->session->cb_destination_timeout) {
+     local->session->cb_destination_timeout(local->session, local);
+   }
+ }
+ /**
+  * parse a stream of DLEP tlvs
+  * @param session dlep session
+  * @param buffer TLV buffer
+  * @param length buffer size
+  * @return DLEP parser status
+  */
+ static enum dlep_parser_error
+ _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t length) {
+   struct dlep_session_parser *parser;
+   struct dlep_parser_tlv *tlv;
+   struct dlep_parser_value *value;
+   uint16_t tlv_type;
+   uint16_t tlv_length;
+   size_t tlv_count, idx;
+   parser = &session->parser;
+   parser->tlv_ptr = buffer;
+   tlv_count = 0;
+   idx = 0;
+   avl_for_each_element(&parser->allowed_tlvs, tlv, _node) {
+     tlv->tlv_first = -1;
+     tlv->tlv_last = -1;
+   }
+   while (idx < length) {
+     if (length - idx < 4) {
+       /* too short for a TLV, end parsing */
+       return DLEP_NEW_PARSER_INCOMPLETE_TLV_HEADER;
+     }
+     /* copy header */
+     memcpy(&tlv_type, &buffer[idx], sizeof(tlv_type));
+     idx += sizeof(tlv_type);
+     tlv_type = ntohs(tlv_type);
+     memcpy(&tlv_length, &buffer[idx], sizeof(tlv_length));
+     idx += sizeof(tlv_length);
+     tlv_length = ntohs(tlv_length);
+     if (idx + tlv_length > length) {
+       OONF_WARN(session->log_source,
+         "TLV %u incomplete: "
+         "%" PRINTF_SIZE_T_SPECIFIER " > %" PRINTF_SIZE_T_SPECIFIER,
+         tlv_type, idx + tlv_length, length);
+       return DLEP_NEW_PARSER_INCOMPLETE_TLV;
+     }
+     /* check if tlv is supported */
+     tlv = dlep_parser_get_tlv(parser, tlv_type);
+     if (!tlv) {
+       OONF_INFO(session->log_source, "Unsupported TLV %u", tlv_type);
+       return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
+     }
+     /* check length */
+     if (tlv->length_max < tlv_length || tlv->length_min > tlv_length) {
+       OONF_WARN(session->log_source,
+         "TLV %u has wrong size,"
+         " %d is not between %u and %u",
+         tlv_type, tlv_length, tlv->length_min, tlv->length_max);
+       return DLEP_NEW_PARSER_ILLEGAL_TLV_LENGTH;
+     }
+     /* check if we need to allocate more space for value pointers */
+     if (parser->value_max_count == tlv_count) {
+       /* allocate more */
+       value = realloc(parser->values, sizeof(*value) * (tlv_count + SESSION_VALUE_STEP));
+       if (!value) {
+         return DLEP_NEW_PARSER_OUT_OF_MEMORY;
+       }
+       parser->value_max_count += SESSION_VALUE_STEP;
+       parser->values = value;
+     }
+     OONF_DEBUG_HEX(session->log_source, &buffer[idx], tlv_length, "Received TLV %u", tlv_type);
+     /* remember tlv value */
+     value = &parser->values[tlv_count];
+     value->tlv_next = -1;
+     value->index = idx;
+     value->length = tlv_length;
+     if (tlv->tlv_last == -1) {
+       /* first tlv */
+       tlv->tlv_first = tlv_count;
+     }
+     else {
+       /* one more */
+       value = &parser->values[tlv->tlv_last];
+       value->tlv_next = tlv_count;
+     }
+     tlv->tlv_last = tlv_count;
+     tlv_count++;
+     idx += tlv_length;
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * Check if all mandatory TLVs were found
+  * @param session dlep session
+  * @param ext dlep extension
+  * @param signal_type dlep signal/message type
+  * @return dlep parser status
+  */
+ static enum dlep_parser_error
+ _check_mandatory(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
+   struct dlep_session_parser *parser;
+   struct dlep_parser_tlv *tlv;
+   struct dlep_extension_signal *extsig;
+   size_t s, t;
+   parser = &session->parser;
+   extsig = NULL;
+   for (s = 0; s < ext->signal_count; s++) {
+     if (ext->signals[s].id == signal_type) {
+       extsig = &ext->signals[s];
+       break;
+     }
+   }
+   if (!extsig) {
+     return DLEP_NEW_PARSER_OKAY;
+   }
+   for (t = 0; t < extsig->mandatory_tlv_count; t++) {
+     tlv = dlep_parser_get_tlv(parser, extsig->mandatory_tlvs[t]);
+     if (!tlv) {
+       OONF_WARN(session->log_source,
+         "Could not find tlv data for"
+         " mandatory TLV %u in extension %d",
+         extsig->mandatory_tlvs[t], ext->id);
+       return DLEP_NEW_PARSER_INTERNAL_ERROR;
+     }
+     if (tlv->tlv_first == -1) {
+       OONF_WARN(session->log_source,
+         "Missing mandatory TLV"
+         " %u in extension %d",
+         extsig->mandatory_tlvs[t], ext->id);
+       return DLEP_NEW_PARSER_MISSING_MANDATORY_TLV;
+     }
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * Check if all duplicate TLVs were allowed to be duplicates
+  * @param session dlep session
+  * @param ext dlep extension
+  * @param signal_type dlep signal/message type
+  * @return dlep parser status
+  */
+ static enum dlep_parser_error
+ _check_duplicate(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
+   struct dlep_session_parser *parser;
+   struct dlep_parser_tlv *tlv;
+   struct dlep_extension_signal *extsig;
+   size_t s, t, dt;
+   bool okay;
+   parser = &session->parser;
+   extsig = NULL;
+   for (s = 0; s < ext->signal_count; s++) {
+     extsig = &ext->signals[s];
+     if (ext->signals[s].id == signal_type) {
+       extsig = &ext->signals[s];
+       break;
+     }
+   }
+   if (!extsig) {
+     return DLEP_NEW_PARSER_OKAY;
+   }
+   for (t = 0; t < extsig->supported_tlv_count; t++) {
+     tlv = avl_find_element(&parser->allowed_tlvs, &extsig->supported_tlvs[t], tlv, _node);
+     if (tlv == NULL || tlv->tlv_first == tlv->tlv_last) {
+       continue;
+     }
+     okay = false;
+     for (dt = 0; dt < extsig->duplicate_tlv_count; dt++) {
+       if (extsig->duplicate_tlvs[dt] == tlv->id) {
+         okay = true;
+         break;
+       }
+     }
+     if (!okay) {
+       OONF_WARN(session->log_source,
+         "Duplicate not allowed"
+         " for TLV %u in extension %d",
+         tlv->id, ext->id);
+       return DLEP_NEW_PARSER_DUPLICATE_TLV;
+     }
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * Call extension processing hooks for parsed signal/message
+  * @param session dlep session
+  * @param ext dlep_extension
+  * @param signal_type dlep signal/message type
+  * @return dlep parser status
+  */
+ static enum dlep_parser_error
+ _call_extension_processing(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
+   size_t s;
+   for (s = 0; s < ext->signal_count; s++) {
+     if (ext->signals[s].id != signal_type) {
+       continue;
+     }
+     if (session->radio) {
+       if (ext->signals[s].process_radio && ext->signals[s].process_radio(ext, session)) {
+         OONF_DEBUG(session->log_source, "Error in radio signal processing of extension '%s'", ext->name);
+         return -1;
+       }
+     }
+     else {
+       if (ext->signals[s].process_router && ext->signals[s].process_router(ext, session)) {
+         OONF_DEBUG(session->log_source, "Error in router signal processing of extension '%s'", ext->name);
+         return -1;
+       }
+     }
+     break;
+   }
+   return DLEP_NEW_PARSER_OKAY;
+ }
+ /**
+  * Add a TLV to the allowed TLVs of a DLEP session
+  * @param parser dlep session parser
+  * @param id DLEP TLV id
+  * @return dlep parser TLV, NULL if out of memory
+  */
+ static struct dlep_parser_tlv *
+ _add_session_tlv(struct dlep_session_parser *parser, uint16_t id) {
+   struct dlep_parser_tlv *tlv;
+   tlv = oonf_class_malloc(&_tlv_class);
+   if (!tlv) {
+     return NULL;
+   }
+   tlv->id = id;
+   tlv->_node.key = &tlv->id;
+   tlv->tlv_first = -1;
+   tlv->tlv_last = -1;
+   avl_insert(&parser->allowed_tlvs, &tlv->_node);
+   return tlv;
+ }
index 0000000,8b7bb93..b88260b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,495 +1,497 @@@
 - * Write a DLEP Link-ID TLV
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <arpa/inet.h>
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcore/oonf_logging.h>
+ #include <oonf/generic/dlep/dlep_extension.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ #include <oonf/generic/dlep/dlep_writer.h>
+ #ifndef _BSD_SOURCE
+ #define _BSD_SOURCE
+ #endif
+ #include <endian.h> /* htobe64 */
+ /**
+  * Start to write a new DLEP signal/message into a buffer
+  * @param writer dlep writer
+  * @param signal_type signal/message type
+  */
+ void
+ dlep_writer_start_signal(struct dlep_writer *writer, uint16_t signal_type) {
+   writer->signal_type = signal_type;
+   writer->signal_start = abuf_getlen(writer->out);
+   abuf_append_uint16(writer->out, htons(signal_type));
+   abuf_append_uint16(writer->out, 0);
+ }
+ /**
+  * Add a TLV to a DLEP writer buffer
+  * @param writer dlep writer
+  * @param type TLV type
+  * @param data pointer to TLV value
+  * @param len length of value, can be 0
+  */
+ void
+ dlep_writer_add_tlv(struct dlep_writer *writer, uint16_t type, const void *data, uint16_t len) {
+   abuf_append_uint16(writer->out, htons(type));
+   abuf_append_uint16(writer->out, htons(len));
+   abuf_memcpy(writer->out, data, len);
+ }
+ /**
+  * Add a TLV to a DLEP writer buffer
+  * @param writer dlep writer
+  * @param type TLV type
+  * @param data1 first part of TLV value
+  * @param len1 length of first value
+  * @param data2 second part of TLV value
+  * @param len2 length of second value
+  */
+ void
+ dlep_writer_add_tlv2(
+   struct dlep_writer *writer, uint16_t type, const void *data1, uint16_t len1, const void *data2, uint16_t len2) {
+   abuf_append_uint16(writer->out, htons(type));
+   abuf_append_uint16(writer->out, htons(len1 + len2));
+   abuf_memcpy(writer->out, data1, len1);
+   abuf_memcpy(writer->out, data2, len2);
+ }
+ /**
+  * Finish a DLEP signal/message
+  * @param writer dlep writer
+  * @param source logging source for error messages
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_writer_finish_signal(struct dlep_writer *writer, enum oonf_log_source source) {
+   size_t length;
+   uint16_t tmp16;
+   char *dst;
+   if (abuf_has_failed(writer->out)) {
+     OONF_WARN(source, "Could not build signal: %u", writer->signal_type);
+     return -1;
+   }
+   length = abuf_getlen(writer->out) - writer->signal_start;
+   if (length > 65535 + 4) {
+     OONF_WARN(
+       source, "Signal %u became too long: %" PRINTF_SIZE_T_SPECIFIER, writer->signal_type, abuf_getlen(writer->out));
+     return -1;
+   }
+   /* calculate network ordered size */
+   tmp16 = htons(length - 4);
+   /* put it into the signal */
+   dst = abuf_getptr(writer->out);
+   memcpy(&dst[writer->signal_start + 2], &tmp16, sizeof(tmp16));
+   OONF_DEBUG_HEX(source, &dst[writer->signal_start], length, "Finished signal %u:", writer->signal_type);
+   return 0;
+ }
+ /**
+  * Write a DLEP heartbeat TLV
+  * @param writer dlep writer
+  * @param interval interval length in milliseconds
+  */
+ void
+ dlep_writer_add_heartbeat_tlv(struct dlep_writer *writer, uint64_t interval) {
+   uint32_t value;
+   value = htonl(interval);
+   dlep_writer_add_tlv(writer, DLEP_HEARTBEAT_INTERVAL_TLV, &value, sizeof(value));
+ }
+ /**
+  * Write a DLEP peer type TLV
+  * @param writer dlep writer
+  * @param peer_type ZERO terminated peer type
+  * @param access_control true if radio implements access control, false otherwise
+  */
+ void
+ dlep_writer_add_peer_type_tlv(struct dlep_writer *writer, const char *peer_type, bool access_control) {
+   char flags;
+   flags = access_control ? DLEP_PEER_TYPE_SECURED : DLEP_PEER_TYPE_OPEN;
+   dlep_writer_add_tlv2(writer, DLEP_PEER_TYPE_TLV, &flags, sizeof(flags), peer_type, strlen(peer_type));
+ }
+ /**
+  * Write a DLEP MAC address TLV
+  * @param writer dlep writer
+  * @param mac_lid mac address/LID
+  * @return -1 if address was wrong type, 0 otherwise
+  */
+ int
+ dlep_writer_add_mac_tlv(struct dlep_writer *writer, const struct oonf_layer2_neigh_key *mac_lid) {
+   uint8_t value[8];
+   switch (netaddr_get_address_family(&mac_lid->addr)) {
+     case AF_MAC48:
+     case AF_EUI64:
+       break;
+     default:
+       return -1;
+   }
+   netaddr_to_binary(value, &mac_lid->addr, 8);
+   dlep_writer_add_tlv(writer, DLEP_MAC_ADDRESS_TLV, value, netaddr_get_binlength(&mac_lid->addr));
+   return 0;
+ }
+ /**
 -  dlep_writer_add_tlv(writer, DLEP_LID_TLV, mac_lid->link_id, mac_lid->link_id_length);
++ * Write a DLEP Link-ID TLV if length is greater than zero
+  * @param writer dlep writer
+  * @param mac_lid mac address/LID
+  * @return -1 if address was wrong type, 0 otherwise
+  */
+ int
+ dlep_writer_add_lid_tlv(struct dlep_writer *writer, const struct oonf_layer2_neigh_key *mac_lid) {
++  if (mac_lid->link_id_length > 0) {
++    dlep_writer_add_tlv(writer, DLEP_LID_TLV, mac_lid->link_id, mac_lid->link_id_length);
++  }
+   return 0;
+ }
+ /**
+  * Write a DLEP Link-ID length TLV
+  * @param writer dlep writer
+  * @param link_id_length length of link-id
+  * @return -1 if address was wrong type, 0 otherwise
+  */
+ int
+ dlep_writer_add_lid_length_tlv(struct dlep_writer *writer, uint16_t link_id_length) {
+   uint16_t value;
+   value = htons(link_id_length);
+   dlep_writer_add_tlv(writer, DLEP_LID_LENGTH_TLV, &value, sizeof(value));
+   return 0;
+ }
+ /**
+  * Write a DLEP IPv4/IPv6 address/subnet TLV
+  * @param writer dlep writer
+  * @param ip IPv4 address
+  * @param add true if address should be added, false to remove it
+  */
+ int
+ dlep_writer_add_ip_tlv(struct dlep_writer *writer, const struct netaddr *ip, bool add) {
+   uint8_t value[18];
+   value[0] = add ? DLEP_IP_ADD : DLEP_IP_REMOVE;
+   netaddr_to_binary(&value[1], ip, 16);
+   switch (netaddr_get_address_family(ip)) {
+     case AF_INET:
+       value[5] = netaddr_get_prefix_length(ip);
+       if (value[5] != 32) {
+         dlep_writer_add_tlv(writer, DLEP_IPV4_SUBNET_TLV, value, 6);
+       }
+       else {
+         dlep_writer_add_tlv(writer, DLEP_IPV4_ADDRESS_TLV, value, 5);
+       }
+       break;
+     case AF_INET6:
+       value[17] = netaddr_get_prefix_length(ip);
+       if (value[17] != 128) {
+         dlep_writer_add_tlv(writer, DLEP_IPV6_SUBNET_TLV, value, 18);
+       }
+       else {
+         dlep_writer_add_tlv(writer, DLEP_IPV6_ADDRESS_TLV, value, 17);
+       }
+       break;
+     default:
+       return -1;
+   }
+   return 0;
+ }
+ /**
+  * Write a DLEP IPv4 conpoint TLV
+  * @param writer dlep writer
+  * @param addr IPv4 address
+  * @param port port number
+  * @param tls TLS capability flag
+  */
+ void
+ dlep_writer_add_ipv4_conpoint_tlv(struct dlep_writer *writer, const struct netaddr *addr, uint16_t port, bool tls) {
+   uint8_t value[7];
+   if (netaddr_get_address_family(addr) != AF_INET) {
+     return;
+   }
+   /* convert port to network byte order */
+   port = htons(port);
+   /* copy data into value buffer */
+   value[0] = tls ? DLEP_CONNECTION_TLS : DLEP_CONNECTION_PLAIN;
+   netaddr_to_binary(&value[1], addr, sizeof(value));
+   memcpy(&value[5], &port, sizeof(port));
+   dlep_writer_add_tlv(writer, DLEP_IPV4_CONPOINT_TLV, &value, sizeof(value));
+ }
+ /**
+  * Write a DLEP IPv6 conpoint TLV
+  * @param writer dlep writer
+  * @param addr IPv6 address
+  * @param port port number
+  * @param tls TLS capability flag
+  */
+ void
+ dlep_writer_add_ipv6_conpoint_tlv(struct dlep_writer *writer, const struct netaddr *addr, uint16_t port, bool tls) {
+   uint8_t value[19];
+   if (netaddr_get_address_family(addr) != AF_INET6) {
+     return;
+   }
+   /* convert port to network byte order */
+   port = htons(port);
+   /* copy data into value buffer */
+   value[0] = tls ? DLEP_CONNECTION_TLS : DLEP_CONNECTION_PLAIN;
+   netaddr_to_binary(&value[1], addr, sizeof(value));
+   memcpy(&value[17], &port, sizeof(port));
+   dlep_writer_add_tlv(writer, DLEP_IPV6_CONPOINT_TLV, &value, sizeof(value));
+ }
+ /**
+  * Add a DLEP tlv with uint64 value
+  * @param writer dlep writer
+  * @param number value
+  * @param tlv tlv id
+  */
+ void
+ dlep_writer_add_uint64(struct dlep_writer *writer, uint64_t number, enum dlep_tlvs tlv) {
+   uint64_t value;
+   value = be64toh(number);
+   dlep_writer_add_tlv(writer, tlv, &value, sizeof(value));
+ }
+ /**
+  * Add a DLEP tlv with int64 value
+  * @param writer dlep writer
+  * @param number value
+  * @param tlv tlv id
+  */
+ void
+ dlep_writer_add_int64(struct dlep_writer *writer, int64_t number, enum dlep_tlvs tlv) {
+   uint64_t *value = (uint64_t *)(&number);
+   *value = htonl(*value);
+   dlep_writer_add_tlv(writer, tlv, value, sizeof(*value));
+ }
+ /**
+  * Write a DLEP status TLV
+  * @param writer dlep writer
+  * @param status dlep status code
+  * @param text ZERO terminated DLEP status text
+  * @return -1 if status text was too long, 0 otherwise
+  */
+ int
+ dlep_writer_add_status(struct dlep_writer *writer, enum dlep_status status, const char *text) {
+   uint8_t value;
+   size_t txtlen;
+   value = status;
+   txtlen = strlen(text);
+   if (txtlen > 65534) {
+     return -1;
+   }
+   dlep_writer_add_tlv2(writer, DLEP_STATUS_TLV, &value, sizeof(value), text, txtlen);
+   return 0;
+ }
+ /**
+  * Write the supported DLEP extensions TLV
+  * @param writer dlep writer
+  * @param extensions array of supported extensions
+  * @param ext_count number of supported extensions
+  */
+ void
+ dlep_writer_add_supported_extensions(struct dlep_writer *writer, const uint16_t *extensions, uint16_t ext_count) {
+   dlep_writer_add_tlv(writer, DLEP_EXTENSIONS_SUPPORTED_TLV, extensions, ext_count * 2);
+ }
+ /**
+  * Write a layer2 data object into a DLEP TLV
+  * @param writer dlep writer
+  * @param data layer2 data
+  * @param tlv tlv id
+  * @param length tlv value length (1,2,4 or 8 bytes)
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ dlep_writer_map_identity(struct dlep_writer *writer, struct oonf_layer2_data *data,
+   const struct oonf_layer2_metadata *meta, uint16_t tlv, uint16_t length) {
+   int64_t l2value64;
+   uint64_t tmp64;
+   uint32_t tmp32;
+   uint16_t tmp16;
+   uint8_t tmp8;
+   void *value;
+   if (!oonf_layer2_data_has_value(data)) {
+     /* no data available */
+     return 0;
+   }
+   if (meta->type != oonf_layer2_data_get_type(data)) {
+     /* bad data type */
+     return -1;
+   }
+   switch (oonf_layer2_data_get_type(data)) {
+     case OONF_LAYER2_INTEGER_DATA:
+       l2value64 = oonf_layer2_data_get_int64(data, 0);
+       break;
+     case OONF_LAYER2_BOOLEAN_DATA:
+       l2value64 = oonf_layer2_data_get_boolean(data, false) ? 1 : 0;
+       break;
+     default:
+       return -1;
+   }
+   switch (length) {
+     case 8:
+       tmp64 = htobe64((uint64_t)l2value64);
+       value = &tmp64;
+       break;
+     case 4:
+       tmp32 = htonl((uint32_t)((int32_t)l2value64));
+       value = &tmp32;
+       break;
+     case 2:
+       tmp16 = htons((uint16_t)((int16_t)l2value64));
+       value = &tmp16;
+       break;
+     case 1:
+       tmp8 = (uint8_t)((int8_t)l2value64);
+       value = &tmp8;
+       break;
+     default:
+       return -1;
+   }
+   dlep_writer_add_tlv(writer, tlv, value, length);
+   return 0;
+ }
+ /**
+  * Automatically map all predefined metric values of an
+  * extension for layer2 neighbor data from the layer2
+  * database to DLEP TLVs
+  * @param writer dlep writer
+  * @param ext dlep extension
+  * @param data layer2 neighbor data array
+  * @param def layer2 neighbor defaults data array
+  * @return 0 if everything worked fine, negative index
+  *   (minus 1) of the conversion that failed.
+  */
+ int
+ dlep_writer_map_l2neigh_data(
+   struct dlep_writer *writer, struct dlep_extension *ext, struct oonf_layer2_data *data, struct oonf_layer2_data *def) {
+   struct dlep_neighbor_mapping *map;
+   struct oonf_layer2_data *ptr;
+   size_t i;
+   for (i = 0; i < ext->neigh_mapping_count; i++) {
+     map = &ext->neigh_mapping[i];
+     ptr = &data[map->layer2];
+     if (!oonf_layer2_data_has_value(ptr) && def) {
+       ptr = &def[map->layer2];
+     }
+     if (map->to_tlv(writer, ptr, oonf_layer2_neigh_metadata_get(map->layer2), map->dlep, map->length)) {
+       return -(i + 1);
+     }
+   }
+   return 0;
+ }
+ /**
+  * Automatically map all predefined metric values of an
+  * extension for layer2 network data from the layer2
+  * database to DLEP TLVs
+  * @param writer dlep writer
+  * @param ext dlep extension
+  * @param data layer2 network data array
+  * @return 0 if everything worked fine, negative index
+  *   (minus 1) of the conversion that failed.
+  */
+ int
+ dlep_writer_map_l2net_data(struct dlep_writer *writer, struct dlep_extension *ext, struct oonf_layer2_data *data) {
+   struct dlep_network_mapping *map;
+   size_t i;
+   for (i = 0; i < ext->if_mapping_count; i++) {
+     map = &ext->if_mapping[i];
+     if (map->to_tlv(writer, &data[map->layer2], oonf_layer2_net_metadata_get(map->layer2), map->dlep, map->length)) {
+       return -(i + 1);
+     }
+   }
+   return 0;
+ }
index 0000000,f3ea6ff..07d70c0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,515 +1,557 @@@
 -    _add_prefix(&session->_ip_prefix_modification, &l2net_ip->ip, true);
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/oonf.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_extension.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ #include <oonf/generic/dlep/dlep_interface.h>
+ #include <oonf/generic/dlep/dlep_reader.h>
+ #include <oonf/generic/dlep/dlep_writer.h>
+ #include <oonf/generic/dlep/radio/dlep_radio_interface.h>
+ #include <oonf/generic/dlep/radio/dlep_radio_session.h>
+ #include <oonf/generic/dlep/ext_base_ip/ip.h>
+ struct _prefix_storage {
+   struct netaddr prefix;
+   bool add;
+   struct avl_node _node;
+ };
+ static void _cb_session_init(struct dlep_session *session);
+ static void _cb_session_cleanup(struct dlep_session *session);
+ static int _radio_write_session_update(
+   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh);
+ static int _radio_write_destination_update(
+   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh);
+ static enum dlep_parser_error _router_process_session_update(struct dlep_extension *ext, struct dlep_session *session);
+ static enum dlep_parser_error _router_process_destination_update(
+   struct dlep_extension *ext, struct dlep_session *session);
+ static void _add_prefix(struct avl_tree *tree, struct netaddr *addr, bool add);
+ static void _cb_add_if_ip(void *ptr);
+ static void _cb_remove_if_ip(void *ptr);
+ static void _cb_add_neigh_ip(void *ptr);
+ static void _cb_remove_neigh_ip(void *ptr);
+ /* peer initialization ack/peer update/destination update */
+ static const uint16_t _ip_tlvs[] = {
+   DLEP_IPV4_ADDRESS_TLV,
+   DLEP_IPV4_SUBNET_TLV,
+   DLEP_IPV6_ADDRESS_TLV,
+   DLEP_IPV6_SUBNET_TLV,
+ };
+ /* supported signals of this extension */
+ static struct dlep_extension_signal _signals[] = {
+   {
+     .id = DLEP_SESSION_INITIALIZATION_ACK,
+     .supported_tlvs = _ip_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_ip_tlvs),
+     .add_radio_tlvs = _radio_write_session_update,
+     .process_router = _router_process_session_update,
+   },
+   {
+     .id = DLEP_SESSION_UPDATE,
+     .supported_tlvs = _ip_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_ip_tlvs),
+     .add_radio_tlvs = _radio_write_session_update,
+     .process_router = _router_process_session_update,
+   },
+   {
+     .id = DLEP_DESTINATION_UP,
+     .supported_tlvs = _ip_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_ip_tlvs),
+     .add_radio_tlvs = _radio_write_destination_update,
+     .process_router = _router_process_destination_update,
+   },
+   {
+     .id = DLEP_DESTINATION_UPDATE,
+     .supported_tlvs = _ip_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_ip_tlvs),
+     .add_radio_tlvs = _radio_write_destination_update,
+     .process_router = _router_process_destination_update,
+   },
+ };
+ /* supported TLVs of this extension */
+ static struct dlep_extension_tlv _tlvs[] = {
+   { DLEP_MAC_ADDRESS_TLV, 6, 8 },
+   { DLEP_IPV4_ADDRESS_TLV, 5, 5 },
+   { DLEP_IPV4_SUBNET_TLV, 6, 6 },
+   { DLEP_IPV6_ADDRESS_TLV, 17, 17 },
+   { DLEP_IPV6_SUBNET_TLV, 18, 18 },
+ };
+ /* DLEP base extension, radio side */
+ static struct dlep_extension _base_ip = {
+   .id = DLEP_EXTENSION_BASE_IP,
+   .name = "base metric",
+   .signals = _signals,
+   .signal_count = ARRAYSIZE(_signals),
+   .tlvs = _tlvs,
+   .tlv_count = ARRAYSIZE(_tlvs),
+   .cb_session_init_radio = _cb_session_init,
+   .cb_session_init_router = _cb_session_init,
+   .cb_session_cleanup_radio = _cb_session_cleanup,
+   .cb_session_cleanup_router = _cb_session_cleanup,
+ };
+ static struct oonf_class _prefix_class = {
+   .name = "dlep ip prefix",
+   .size = sizeof(struct _prefix_storage),
+ };
+ static struct oonf_class_extension _l2_interface_ip_listener = {
+   .ext_name = "dlep l2 if-ip",
+   .class_name = LAYER2_CLASS_NETWORK_ADDRESS,
+   .cb_add = _cb_add_if_ip,
+   .cb_remove = _cb_remove_if_ip,
+ };
+ static struct oonf_class_extension _l2_neighbor_ip_listener = {
+   .ext_name = "dlep l2 neigh-ip",
+   .class_name = LAYER2_CLASS_NEIGHBOR_ADDRESS,
+   .cb_add = _cb_add_neigh_ip,
+   .cb_remove = _cb_remove_neigh_ip,
+ };
+ /**
+  * Initialize the base metric DLEP extension
+  * @return this extension
+  */
+ struct dlep_extension *
+ dlep_base_ip_init(void) {
+   dlep_extension_add(&_base_ip);
+   oonf_class_add(&_prefix_class);
+   oonf_class_extension_add(&_l2_interface_ip_listener);
+   oonf_class_extension_add(&_l2_neighbor_ip_listener);
+   return &_base_ip;
+ }
+ void
+ dlep_base_ip_cleanup(void) {
+   oonf_class_extension_remove(&_l2_neighbor_ip_listener);
+   oonf_class_extension_remove(&_l2_interface_ip_listener);
+   oonf_class_remove(&_prefix_class);
+ }
+ static void
+ _cb_session_init(struct dlep_session *session) {
+   struct oonf_layer2_neighbor_address *l2neigh_ip;
+   struct oonf_layer2_neigh *l2neigh;
+   struct oonf_layer2_peer_address *l2net_ip;
+   struct oonf_layer2_net *l2net;
+   struct dlep_local_neighbor *dlep_neighbor;
+   l2net = oonf_layer2_net_get(session->l2_listener.name);
+   if (!l2net) {
+     return;
+   }
+   avl_for_each_element(&l2net->local_peer_ips, l2net_ip, _net_node) {
 -  avl_for_each_element_safe(&session->_ip_prefix_modification, storage, _node, storage_it) {
 -    avl_remove(&session->_ip_prefix_modification, &storage->_node);
++    _add_prefix(&session->_ext_ip.prefix_modification, &l2net_ip->ip, true);
+   }
+   avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
+     dlep_neighbor = dlep_session_add_local_neighbor(session, &l2neigh->key);
+     if (dlep_neighbor) {
+       avl_for_each_element(&l2neigh->remote_neighbor_ips, l2neigh_ip, _neigh_node) {
+         _add_prefix(&dlep_neighbor->_ip_prefix_modification, &l2neigh_ip->ip, true);
+       }
+     }
+   }
+ }
+ static void
+ _cb_session_cleanup(struct dlep_session *session) {
+   struct dlep_local_neighbor *l2neigh;
+   struct _prefix_storage *storage, *storage_it;
+   /* remove all stored changes for neighbors */
+   avl_for_each_element(&session->local_neighbor_tree, l2neigh, _node) {
+     avl_for_each_element_safe(&l2neigh->_ip_prefix_modification, storage, _node, storage_it) {
+       avl_remove(&l2neigh->_ip_prefix_modification, &storage->_node);
+       oonf_class_free(&_prefix_class, storage);
+     }
+   }
+   /* remove all stored changes for the local peer */
 -
++  avl_for_each_element_safe(&session->_ext_ip.prefix_modification, storage, _node, storage_it) {
++    avl_remove(&session->_ext_ip.prefix_modification, &storage->_node);
+     oonf_class_free(&_prefix_class, storage);
+   }
+ }
++static void
++_handle_if_ip(struct dlep_session *session, struct netaddr *last_session_if_ip,
++    const struct netaddr *first_if_ip, const struct netaddr *second_if_ip) {
++  const struct netaddr *if_ip;
++
++  if_ip = netaddr_is_unspec(first_if_ip) ? second_if_ip : first_if_ip;
++
++  if (netaddr_cmp(last_session_if_ip, if_ip) == 0) {
++    return;
++  }
++
++  if (!netaddr_is_unspec(last_session_if_ip)) {
++    dlep_writer_add_ip_tlv(&session->writer, last_session_if_ip, false);
++  }
++  if (!netaddr_is_unspec(if_ip)) {
++    dlep_writer_add_ip_tlv(&session->writer, if_ip, true);
++  }
++  memcpy(last_session_if_ip, if_ip, sizeof(*if_ip));
++}
++
+ static int
+ _radio_write_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
+   const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
+   struct _prefix_storage *storage, *storage_it;
 -  avl_for_each_element(&session->_ip_prefix_modification, storage, _node) {
++  struct dlep_radio_session *radio_session;
++  struct os_interface *os_if;
+   struct netaddr_str nbuf;
 -  avl_for_each_element_safe(&session->_ip_prefix_modification, storage, _node, storage_it) {
 -    avl_remove(&session->_ip_prefix_modification, &storage->_node);
++  /* transmit modified IP network prefixes */
++  avl_for_each_element(&session->_ext_ip.prefix_modification, storage, _node) {
+     OONF_INFO(session->log_source, "Add '%s' (%s) to session update", netaddr_to_string(&nbuf, &storage->prefix),
+       storage->add ? "add" : "remove");
+     if (dlep_writer_add_ip_tlv(&session->writer, &storage->prefix, storage->add)) {
+       OONF_WARN(session->log_source, "Cannot add '%s' (%s) to session update",
+         netaddr_to_string(&nbuf, &storage->prefix), storage->add ? "add" : "remove");
+       return -1;
+     }
+   }
++  /* also transmit IP interface addresses */
++  radio_session = dlep_radio_get_session(session);
++  if (radio_session) {
++    os_if = radio_session->interface->interf.udp._if_listener.data;
++    _handle_if_ip(session, &session->_ext_ip.if_ip_v4, os_if->if_linklocal_v4, os_if->if_v4);
++    _handle_if_ip(session, &session->_ext_ip.if_ip_v6, os_if->if_linklocal_v6, os_if->if_v6);
++  }
++
+   /* no error, now remove elements from temporary storage */
 -        _add_prefix(&radio_session->session._ip_prefix_modification, prefix, add);
++  avl_for_each_element_safe(&session->_ext_ip.prefix_modification, storage, _node, storage_it) {
++    avl_remove(&session->_ext_ip.prefix_modification, &storage->_node);
+     oonf_class_free(&_prefix_class, storage);
+   }
+   return 0;
+ }
+ static int
+ _radio_write_destination_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
+     const struct oonf_layer2_neigh_key *neigh) {
+   struct dlep_local_neighbor *dlep_neigh;
+   struct _prefix_storage *storage, *storage_it;
+   union oonf_layer2_neigh_key_str nkbuf;
+   struct netaddr_str nbuf1;
+   dlep_neigh = dlep_session_get_local_neighbor(session, neigh);
+   if (!dlep_neigh) {
+     OONF_WARN(session->log_source,
+       "Could not find dlep_neighbor for neighbor %s",
+       oonf_layer2_neigh_key_to_string(&nkbuf, neigh, true));
+     return -1;
+   }
+   /* send every attached IP towards the router */
+   avl_for_each_element(&dlep_neigh->_ip_prefix_modification, storage, _node) {
+     OONF_INFO(session->log_source, "add '%s' (%s) to destination update %s",
+       netaddr_to_string(&nbuf1, &storage->prefix), storage->add ? "add" : "remove",
+       oonf_layer2_neigh_key_to_string(&nkbuf, neigh, true));
+     if (dlep_writer_add_ip_tlv(&session->writer, &storage->prefix, storage->add)) {
+       OONF_WARN(session->log_source, "Cannot add '%s' (%s) to destination update %s",
+         netaddr_to_string(&nbuf1, &storage->prefix), storage->add ? "add" : "remove",
+         oonf_layer2_neigh_key_to_string(&nkbuf, neigh, true));
+       return -1;
+     }
+   }
+   /* no error, now remove elements from temporary storage */
+   avl_for_each_element_safe(&dlep_neigh->_ip_prefix_modification, storage, _node, storage_it) {
+     avl_remove(&dlep_neigh->_ip_prefix_modification, &storage->_node);
+     oonf_class_free(&_prefix_class, storage);
+   }
+   return 0;
+ }
+ static void
+ _process_session_ip_tlvs(
+   const struct oonf_layer2_origin *origin, struct oonf_layer2_net *l2net, struct netaddr *ip, bool add) {
+   struct oonf_layer2_peer_address *l2addr;
+   if (add) {
+     oonf_layer2_net_add_ip(l2net, origin, ip);
+   }
+   else if ((l2addr = oonf_layer2_net_get_local_ip(l2net, ip))) {
+     oonf_layer2_net_remove_ip(l2addr, origin);
+   }
+ }
+ static enum dlep_parser_error
+ _router_process_session_update(struct dlep_extension *ext __attribute((unused)), struct dlep_session *session) {
+   struct oonf_layer2_net *l2net;
+   struct netaddr ip;
+   struct dlep_parser_value *value;
+   bool add_ip;
+   l2net = oonf_layer2_net_get(session->l2_listener.name);
+   if (!l2net) {
+     return 0;
+   }
+   /* ipv4 address */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV4_ADDRESS_TLV);
+   while (value) {
+     if (dlep_reader_ipv4_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_session_ip_tlvs(session->l2_origin, l2net, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv6 address */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV6_ADDRESS_TLV);
+   while (value) {
+     if (dlep_reader_ipv6_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_session_ip_tlvs(session->l2_origin, l2net, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv4 subnet */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV4_SUBNET_TLV);
+   while (value) {
+     if (dlep_reader_ipv4_subnet_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_session_ip_tlvs(session->l2_origin, l2net, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv6 subnet */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV6_SUBNET_TLV);
+   while (value) {
+     if (dlep_reader_ipv6_subnet_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_session_ip_tlvs(session->l2_origin, l2net, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   return 0;
+ }
+ static void
+ _process_destination_ip_tlv(
+   const struct oonf_layer2_origin *origin, struct oonf_layer2_neigh *l2neigh, struct netaddr *ip, bool add) {
+   struct oonf_layer2_neighbor_address *l2addr;
++  struct oonf_layer2_peer_address *peer_ip;
++  int af;
++  af = netaddr_get_address_family(ip);
+   if (add) {
++    if (!oonf_layer2_neigh_has_nexthop(l2neigh, af)) {
++      avl_for_each_element(&l2neigh->network->local_peer_ips, peer_ip, _net_node) {
++        if (netaddr_get_address_family(&peer_ip->ip) == af
++            && netaddr_get_prefix_length(&peer_ip->ip) == netaddr_get_af_maxprefix(af)) {
++          oonf_layer2_neigh_set_nexthop(l2neigh, &peer_ip->ip);
++          break;
++        }
++      }
++    }
+     oonf_layer2_neigh_add_ip(l2neigh, origin, ip);
+   }
+   else if ((l2addr = oonf_layer2_neigh_get_remote_ip(l2neigh, ip))) {
+     oonf_layer2_neigh_remove_ip(l2addr, origin);
+   }
+ }
+ static enum dlep_parser_error
+ _router_process_destination_update(struct dlep_extension *ext __attribute((unused)), struct dlep_session *session) {
+   struct oonf_layer2_neigh *l2neigh;
+   struct netaddr ip;
+   struct dlep_parser_value *value;
+   bool add_ip;
+   l2neigh = dlep_extension_get_l2_neighbor(session);
+   if (!l2neigh) {
+     return 0;
+   }
+   /* ipv4 address */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV4_ADDRESS_TLV);
+   while (value) {
+     if (dlep_reader_ipv4_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_destination_ip_tlv(session->l2_origin, l2neigh, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv6 address */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV6_ADDRESS_TLV);
+   while (value) {
+     if (dlep_reader_ipv6_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_destination_ip_tlv(session->l2_origin, l2neigh, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv4 subnet */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV4_SUBNET_TLV);
+   while (value) {
+     if (dlep_reader_ipv4_subnet_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_destination_ip_tlv(session->l2_origin, l2neigh, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   /* ipv6 subnet */
+   value = dlep_session_get_tlv_value(session, DLEP_IPV6_SUBNET_TLV);
+   while (value) {
+     if (dlep_reader_ipv6_subnet_tlv(&ip, &add_ip, session, value)) {
+       return -1;
+     }
+     _process_destination_ip_tlv(session->l2_origin, l2neigh, &ip, add_ip);
+     value = dlep_session_get_next_tlv_value(session, value);
+   }
+   return 0;
+ }
+ static void
+ _add_prefix(struct avl_tree *tree, struct netaddr *addr, bool add) {
+   struct _prefix_storage *storage;
+   storage = avl_find_element(tree, addr, storage, _node);
+   if (storage) {
+     storage->add = add;
+     return;
+   }
+   storage = oonf_class_malloc(&_prefix_class);
+   if (!storage) {
+     return;
+   }
+   /* copy key and put into tree */
+   memcpy(&storage->prefix, addr, sizeof(*addr));
+   storage->_node.key = &storage->prefix;
+   avl_insert(tree, &storage->_node);
+   storage->add = add;
+ }
+ static void
+ _modify_if_ip(const char *if_name, struct netaddr *prefix, bool add) {
+   struct dlep_radio_if *interf;
+   struct dlep_radio_session *radio_session;
+   avl_for_each_element(dlep_if_get_tree(true), interf, interf._node) {
+     if (strcmp(interf->interf.l2_ifname, if_name) == 0) {
+       avl_for_each_element(&interf->interf.session_tree, radio_session, _node) {
++        _add_prefix(&radio_session->session._ext_ip.prefix_modification, prefix, add);
+       }
+     }
+   }
+ }
+ static void
+ _cb_add_if_ip(void *ptr) {
+   struct oonf_layer2_peer_address *peer_ip = ptr;
+   _modify_if_ip(peer_ip->l2net->name, &peer_ip->ip, true);
+ }
+ static void
+ _cb_remove_if_ip(void *ptr) {
+   struct oonf_layer2_peer_address *peer_ip = ptr;
+   _modify_if_ip(peer_ip->l2net->name, &peer_ip->ip, false);
+ }
+ static void
+ _modify_neigh_ip(const char *if_name, struct oonf_layer2_neigh_key *neighbor, struct netaddr *prefix, bool add) {
+   struct dlep_radio_if *radio_interf;
+   struct dlep_local_neighbor *dlep_neighbor;
+   struct dlep_radio_session *radio_session;
+   avl_for_each_element(dlep_if_get_tree(true), radio_interf, interf._node) {
+     if (strcmp(radio_interf->interf.l2_ifname, if_name) == 0) {
+       avl_for_each_element(&radio_interf->interf.session_tree, radio_session, _node) {
+         dlep_neighbor = dlep_session_add_local_neighbor(&radio_session->session, neighbor);
+         if (dlep_neighbor) {
+           _add_prefix(&dlep_neighbor->_ip_prefix_modification, prefix, add);
+         }
+       }
+     }
+   }
+ }
+ static void
+ _cb_add_neigh_ip(void *ptr) {
+   struct oonf_layer2_neighbor_address *neigh_ip = ptr;
+   _modify_neigh_ip(neigh_ip->l2neigh->network->name, &neigh_ip->l2neigh->key, &neigh_ip->ip, true);
+ }
+ static void
+ _cb_remove_neigh_ip(void *ptr) {
+   struct oonf_layer2_neighbor_address *neigh_ip = ptr;
+   _modify_neigh_ip(neigh_ip->l2neigh->network->name, &neigh_ip->l2neigh->key, &neigh_ip->ip, false);
+ }
index 0000000,dcc82dc..9193360
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,279 +1,244 @@@
 -static const uint16_t _dst_up_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/oonf.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_extension.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ #include <oonf/generic/dlep/dlep_reader.h>
+ #include <oonf/generic/dlep/dlep_writer.h>
+ #include <oonf/generic/dlep/ext_lid/lid.h>
+ static void _cb_session_deactivate(struct dlep_session *session);
+ static int _write_lid_only(struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh);
+ static int _write_session_init_ack(struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh);
+ static enum dlep_parser_error _process_session_init_ack(struct dlep_extension *ext, struct dlep_session *session);
+ /* session initialization ack */
+ static const uint16_t _session_initack_tlvs[] = {
+   DLEP_LID_LENGTH_TLV,
+ };
+ /* destination up */
+ static const uint16_t _dst_up_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _dst_up_ack_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /* destination up ack */
+ static const uint16_t _dst_up_ack_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _dst_down_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /* destination down */
+ static const uint16_t _dst_down_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _dst_down_ack_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /* destination down ack */
+ static const uint16_t _dst_down_ack_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _dst_update_mandatory[] = {
 -  DLEP_MAC_ADDRESS_TLV,
 -};
+ /* destination update */
+ static const uint16_t _dst_update_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _linkchar_req_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /* link characteristics request */
+ static const uint16_t _linkchar_req_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -static const uint16_t _linkchar_ack_mandatory[] = {
 -  DLEP_LID_TLV,
 -};
+ /* link characteristics ack */
+ static const uint16_t _linkchar_ack_tlvs[] = {
+   DLEP_LID_TLV,
+ };
 -    .mandatory_tlvs = _dst_up_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_dst_up_mandatory),
+ /* supported signals of this extension, parsing the LID TLV is done by dlep_extension */
+ static struct dlep_extension_signal _signals[] = {
+   {
+     .id = DLEP_SESSION_INITIALIZATION_ACK,
+     .supported_tlvs = _session_initack_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_session_initack_tlvs),
+     .process_router = _process_session_init_ack,
+     .add_radio_tlvs = _write_session_init_ack,
+   },
+   {
+     .id = DLEP_DESTINATION_UP,
+     .supported_tlvs = _dst_up_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_dst_up_tlvs),
 -    .mandatory_tlvs = _dst_up_ack_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_dst_up_ack_mandatory),
+     .add_radio_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_DESTINATION_UP_ACK,
+     .supported_tlvs = _dst_up_ack_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_dst_up_ack_tlvs),
 -    .mandatory_tlvs = _dst_down_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_dst_down_mandatory),
+     .add_router_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_DESTINATION_DOWN,
+     .supported_tlvs = _dst_down_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_dst_down_tlvs),
 -    .mandatory_tlvs = _dst_down_ack_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_dst_down_ack_mandatory),
+     .add_radio_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_DESTINATION_DOWN_ACK,
+     .supported_tlvs = _dst_down_ack_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_dst_down_ack_tlvs),
 -    .mandatory_tlvs = _dst_update_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_dst_update_mandatory),
+     .add_router_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_DESTINATION_UPDATE,
+     .supported_tlvs = _dst_update_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_dst_update_tlvs),
 -    .mandatory_tlvs = _linkchar_req_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_linkchar_req_mandatory),
+     .add_radio_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_LINK_CHARACTERISTICS_REQUEST,
+     .supported_tlvs = _linkchar_req_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_linkchar_req_tlvs),
 -    .mandatory_tlvs = _linkchar_ack_mandatory,
 -    .mandatory_tlv_count = ARRAYSIZE(_linkchar_ack_mandatory),
+     .add_router_tlvs = _write_lid_only,
+   },
+   {
+     .id = DLEP_LINK_CHARACTERISTICS_ACK,
+     .supported_tlvs = _linkchar_ack_tlvs,
+     .supported_tlv_count = ARRAYSIZE(_linkchar_ack_tlvs),
+     .add_radio_tlvs = _write_lid_only,
+   },
+ };
+ /* supported TLVs of this extension */
+ static struct dlep_extension_tlv _tlvs[] = {
+   { DLEP_LID_TLV, 1, OONF_LAYER2_MAX_LINK_ID },
+   { DLEP_LID_LENGTH_TLV, 2, 2 },
+ };
+ /* DLEP base extension, radio side */
+ static struct dlep_extension _lid = {
+   .id = DLEP_EXTENSION_LINK_ID,
+   .name = "linkid",
+   .signals = _signals,
+   .signal_count = ARRAYSIZE(_signals),
+   .tlvs = _tlvs,
+   .tlv_count = ARRAYSIZE(_tlvs),
+   .cb_session_deactivate_radio = _cb_session_deactivate,
+   .cb_session_deactivate_router = _cb_session_deactivate,
+ };
+ /**
+  * Get link-id DLEP extension
+  * @return this extension
+  */
+ struct dlep_extension *
+ dlep_lid_init(void) {
+   dlep_extension_add(&_lid);
+   return &_lid;
+ }
+ static void
+ _cb_session_deactivate(struct dlep_session *session) {
+   session->cfg.lid_length = 0;
+ }
+ /**
+  * Write the link-id TLV into the DLEP message
+  * @param ext (this) dlep extension
+  * @param session dlep session
+  * @param neigh layer2 neighbor key to write into TLV
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _write_lid_only(
+   struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh) {
+   return dlep_writer_add_lid_tlv(&session->writer, neigh);
+ }
+ /**
+ * Write link-id-length TLV if necessary
+  * @param ext (this) dlep extension
+  * @param session dlep session
+  * @param neigh layer2 neighbor key to write into TLV (NULL in this case)
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _write_session_init_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
+                         const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
+   if (session->cfg.lid_length == 0 || session->cfg.lid_length == DLEP_DEFAULT_LID_LENGTH) {
+     return 0;
+   }
+   return dlep_writer_add_lid_length_tlv(&session->writer, session->cfg.lid_length);
+ }
+ /**
+  * Handle incoming link-id-length TLV
+  * @param ext (this) dlep extension
+  * @param session dlep session
+  * @return parser return code
+  */
+ static enum dlep_parser_error
+ _process_session_init_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
+   uint16_t length;
+   if (dlep_reader_lid_length_tlv(&length, session, NULL)) {
+     session->cfg.lid_length = DLEP_DEFAULT_LID_LENGTH;
+     return DLEP_NEW_PARSER_OKAY;
+   }
+   if (length > OONF_LAYER2_MAX_LINK_ID) {
+     dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, DLEP_STATUS_REQUEST_DENIED,
+         "Cannot handle link-id length this large");
+     return DLEP_NEW_PARSER_TERMINDATED;
+   }
+   session->cfg.lid_length = length;
+   return DLEP_NEW_PARSER_OKAY;
+ }
index 0000000,346b1f7..f4868c3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,234 +1,234 @@@
 -  CFG_MAP_ACL_V46(dlep_radio_if, interf.udp_config.bindto, "discovery_bindto", "fe80::/10",
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <errno.h>
+ #include <unistd.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/libcommon/avl_comp.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libconfig/cfg_schema.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_class.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_packet_socket.h>
+ #include <oonf/base/oonf_stream_socket.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ #include <oonf/generic/dlep/dlep_writer.h>
+ #include <oonf/generic/dlep/radio/dlep_radio.h>
+ #include <oonf/generic/dlep/radio/dlep_radio_interface.h>
+ #include <oonf/generic/dlep/radio/dlep_radio_internal.h>
+ /* prototypes */
+ static void _early_cfg_init(void);
+ static int _init(void);
+ static void _cleanup(void);
+ static void _initiate_shutdown(void);
+ static void _cb_config_changed(void);
+ /* configuration */
+ static const char *_UDP_MODE[] = {
+   [DLEP_IF_UDP_NONE] = DLEP_IF_UDP_NONE_STR,
+   [DLEP_IF_UDP_SINGLE_SESSION] = DLEP_IF_UDP_SINGLE_SESSION_STR,
+   [DLEP_IF_UDP_ALWAYS] = DLEP_IF_UDP_ALWAYS_STR,
+ };
+ static struct cfg_schema_entry _radio_entries[] = {
+   CFG_MAP_STRING_ARRAY(dlep_radio_if, interf.udp_config.interface, "datapath_if", "",
+     "Name of interface to talk to dlep router (default is section name)", IF_NAMESIZE),
+   CFG_MAP_STRING(dlep_radio_if, interf.session.cfg.peer_type, "peer_type", "OONF DLEP Radio",
+     "Identification string of DLEP radio endpoint"),
+   CFG_MAP_NETADDR_V4(dlep_radio_if, interf.udp_config.multicast_v4, "discovery_mc_v4",
+     DLEP_WELL_KNOWN_MULTICAST_ADDRESS, "IPv4 address to send discovery UDP packet to", false, false),
+   CFG_MAP_NETADDR_V6(dlep_radio_if, interf.udp_config.multicast_v6, "discovery_mc_v6",
+     DLEP_WELL_KNOWN_MULTICAST_ADDRESS_6, "IPv6 address to send discovery UDP packet to", false, false),
+   CFG_MAP_INT32_MINMAX(dlep_radio_if, interf.udp_config.port, "discovery_port", DLEP_WELL_KNOWN_MULTICAST_PORT_TXT,
+     "UDP port for discovery packets", 0, 1, 65535),
++  CFG_MAP_ACL_V46(dlep_radio_if, interf.udp_config.bindto, "discovery_bindto", "fe80::/64",
+     "Filter to determine the binding of the UDP discovery socket"),
+   CFG_MAP_INT32_MINMAX(dlep_radio_if, tcp_config.port, "session_port", DLEP_WELL_KNOWN_SESSION_PORT_TXT,
+     "Server port for DLEP tcp sessions", 0, 1, 65535),
+   CFG_MAP_ACL_V46(dlep_radio_if, tcp_config.bindto, "session_bindto", "169.254.0.0/16\0fe80::/10",
+     "Filter to determine the binding of the TCP server socket"),
+   CFG_MAP_CLOCK_MINMAX(dlep_radio_if, interf.session.cfg.heartbeat_interval, "heartbeat_interval", "1.000",
+     "Interval in seconds between two heartbeat signals", 1000, 65535 * 1000),
+   CFG_MAP_CHOICE(dlep_radio_if, interf.udp_mode, "udp_mode", DLEP_IF_UDP_SINGLE_SESSION_STR,
+     "Determines the UDP behavior of the radio. 'none' never sends/processes UDP, 'single_session' only does"
+     " if no DLEP session is active and 'always' always sends/processes UDP and allows multiple sessions",
+     _UDP_MODE),
+   CFG_MAP_BOOL(dlep_radio_if, interf.session.cfg.send_proxied, "proxied", "true",
+     "Report 802.11s proxied mac address for neighbors"),
+   CFG_MAP_BOOL(dlep_radio_if, interf.session.cfg.send_neighbors, "not_proxied", "false", "Report direct neighbors"),
+   CFG_MAP_INT32_MINMAX(dlep_radio_if, interf.session.cfg.lid_length, "lid_length", DLEP_DEFAULT_LID_LENGTH_TXT,
+     "Link-ID length in octets that can be used to communicate with router", 0, 0, OONF_LAYER2_MAX_LINK_ID-1),
+ };
+ static struct cfg_schema_section _radio_section = {
+   .type = OONF_DLEP_RADIO_SUBSYSTEM,
+   .mode = CFG_SSMODE_NAMED,
+   .help = "name of the layer2 interface DLEP radio will take its data from",
+   .cb_delta_handler = _cb_config_changed,
+   .entries = _radio_entries,
+   .entry_count = ARRAYSIZE(_radio_entries),
+ };
+ /* subsystem declaration */
+ static const char *_dependencies[] = {
+   OONF_CLASS_SUBSYSTEM,
+   OONF_LAYER2_SUBSYSTEM,
+   OONF_PACKET_SUBSYSTEM,
+   OONF_STREAM_SUBSYSTEM,
+   OONF_TIMER_SUBSYSTEM,
+ };
+ static struct oonf_subsystem _dlep_radio_subsystem = {
+   .name = OONF_DLEP_RADIO_SUBSYSTEM,
+   .dependencies = _dependencies,
+   .dependencies_count = ARRAYSIZE(_dependencies),
+   .descr = "OONF DLEP radio plugin",
+   .author = "Henning Rogge",
+   .cfg_section = &_radio_section,
+   .early_cfg_init = _early_cfg_init,
+   .init = _init,
+   .initiate_shutdown = _initiate_shutdown,
+   .cleanup = _cleanup,
+ };
+ DECLARE_OONF_PLUGIN(_dlep_radio_subsystem);
+ /* logging */
+ enum oonf_log_source LOG_DLEP_RADIO;
+ static void
+ _early_cfg_init(void) {
+   LOG_DLEP_RADIO = _dlep_radio_subsystem.logging;
+ }
+ /**
+  * Plugin constructor for dlep radio
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _init(void) {
+   dlep_radio_interface_init();
+   return 0;
+ }
+ /**
+  * Send a clean Peer Terminate before we drop the session to shutdown
+  */
+ static void
+ _initiate_shutdown(void) {
+   dlep_radio_terminate_all_sessions();
+ }
+ /**
+  * Plugin destructor for dlep radio
+  */
+ static void
+ _cleanup(void) {
+   dlep_radio_interface_cleanup();
+ }
+ /**
+  * Callback for configuration changes
+  */
+ static void
+ _cb_config_changed(void) {
+   struct dlep_radio_if *interface;
+   const char *ifname;
+   char ifbuf[IF_NAMESIZE];
+   int error;
+   ifname = cfg_get_phy_if(ifbuf, _radio_section.section_name);
+   if (!_radio_section.post) {
+     /* remove old interface object */
+     interface = dlep_radio_get_by_layer2_if(ifname);
+     if (interface) {
+       dlep_radio_remove_interface(interface);
+     }
+     return;
+   }
+   /* get interface object or create one */
+   interface = dlep_radio_add_interface(ifname);
+   if (!interface) {
+     return;
+   }
+   /* read configuration */
+   error = cfg_schema_tobin(interface, _radio_section.post, _radio_entries, ARRAYSIZE(_radio_entries));
+   if (error) {
+     OONF_WARN(LOG_DLEP_RADIO, "Could not convert " OONF_DLEP_RADIO_SUBSYSTEM " config to bin (%d)", error);
+     return;
+   }
+   if (!interface->interf.udp_config.interface[0]) {
+     strscpy(interface->interf.udp_config.interface, ifname, IF_NAMESIZE);
+   }
+   else {
+     cfg_get_phy_if(interface->interf.udp_config.interface, interface->interf.udp_config.interface);
+   }
+   /* apply interface name also to TCP socket */
+   strscpy(interface->tcp_config.interface, interface->interf.udp_config.interface, IF_NAMESIZE);
+   /* apply settings */
+   dlep_radio_apply_interface_settings(interface);
+ }
index 0000000,e6c854e..94fd1fd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,229 +1,229 @@@
 -  CFG_MAP_ACL_V46(dlep_router_if, interf.udp_config.bindto, "discovery_bindto", "224.0.0.1\0fe80::/10",
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <errno.h>
+ #include <unistd.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/libcommon/avl_comp.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libconfig/cfg_schema.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_class.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_packet_socket.h>
+ #include <oonf/base/oonf_stream_socket.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/dlep/dlep_iana.h>
+ #include <oonf/generic/dlep/dlep_session.h>
+ #include <oonf/generic/dlep/router/dlep_router.h>
+ #include <oonf/generic/dlep/router/dlep_router_interface.h>
+ /* prototypes */
+ static void _early_cfg_init(void);
+ static int _init(void);
+ static void _initiate_shutdown(void);
+ static void _cleanup(void);
+ static void _cb_config_changed(void);
+ /* configuration */
+ static const char *_UDP_MODE[] = {
+   [DLEP_IF_UDP_NONE] = DLEP_IF_UDP_NONE_STR,
+   [DLEP_IF_UDP_SINGLE_SESSION] = DLEP_IF_UDP_SINGLE_SESSION_STR,
+   [DLEP_IF_UDP_ALWAYS] = DLEP_IF_UDP_ALWAYS_STR,
+ };
+ static struct cfg_schema_entry _router_entries[] = {
+   CFG_MAP_STRING(dlep_router_if, interf.session.cfg.peer_type, "peer_type", "OONF DLEP Router",
+     "Identification string of DLEP router endpoint"),
+   CFG_MAP_NETADDR_V4(dlep_router_if, interf.udp_config.multicast_v4, "discovery_mc_v4",
+     DLEP_WELL_KNOWN_MULTICAST_ADDRESS, "IPv4 address to send discovery UDP packet to", false, false),
+   CFG_MAP_NETADDR_V6(dlep_router_if, interf.udp_config.multicast_v6, "discovery_mc_v6",
+     DLEP_WELL_KNOWN_MULTICAST_ADDRESS_6, "IPv6 address to send discovery UDP packet to", false, false),
+   CFG_MAP_INT32_MINMAX(dlep_router_if, interf.udp_config.multicast_port, "discovery_port",
+     DLEP_WELL_KNOWN_MULTICAST_PORT_TXT, "UDP port for discovery packets", 0, 1, 65535),
++  CFG_MAP_ACL_V46(dlep_router_if, interf.udp_config.bindto, "discovery_bindto", "fe80::/64",
+     "Filter to determine the binding of the UDP discovery socket"),
+   CFG_MAP_CLOCK_MIN(dlep_router_if, interf.session.cfg.discovery_interval, "discovery_interval", "1.000",
+     "Interval in seconds between two discovery beacons", 1000),
+   CFG_MAP_CLOCK_MINMAX(dlep_router_if, interf.session.cfg.heartbeat_interval, "heartbeat_interval", "1.000",
+     "Interval in seconds between two heartbeat signals", 1000, 65535000),
+   CFG_MAP_CHOICE(dlep_router_if, interf.udp_mode, "udp_mode", DLEP_IF_UDP_SINGLE_SESSION_STR,
+     "Determines the UDP behavior of the router. 'none' never sends/processes UDP, 'single_session' only does"
+     " if no DLEP session is active and 'always' always sends/processes UDP and allows multiple sessions",
+     _UDP_MODE),
+   CFG_MAP_STRING_ARRAY(dlep_router_if, interf.udp_config.interface, "datapath_if", "",
+     "Overwrite datapath interface for incoming dlep traffic, used for"
+     " receiving DLEP data through out-of-band channel.",
+     IF_NAMESIZE),
+   CFG_MAP_NETADDR_V46(dlep_router_if, connect_to_addr, "connect_to", "-",
+     "IP to directly connect to a known DLEP radio TCP socket", false, true),
+   CFG_MAP_INT32_MINMAX(dlep_router_if, connect_to_port, "connect_to_port", DLEP_WELL_KNOWN_SESSION_PORT_TXT,
+     "TCP port to directly connect to a known DLEP radio TCP socket", 0, 1, 65535),
+ };
+ static struct cfg_schema_section _router_section = {
+   .type = OONF_DLEP_ROUTER_SUBSYSTEM,
+   .mode = CFG_SSMODE_NAMED,
+   .help = "name of the layer2 interface DLEP router will put its data into",
+   .cb_delta_handler = _cb_config_changed,
+   .entries = _router_entries,
+   .entry_count = ARRAYSIZE(_router_entries),
+ };
+ /* plugin declaration */
+ static const char *_dependencies[] = {
+   OONF_CLASS_SUBSYSTEM,
+   OONF_LAYER2_SUBSYSTEM,
+   OONF_PACKET_SUBSYSTEM,
+   OONF_STREAM_SUBSYSTEM,
+   OONF_TIMER_SUBSYSTEM,
+ };
+ static struct oonf_subsystem _dlep_router_subsystem = {
+   .name = OONF_DLEP_ROUTER_SUBSYSTEM,
+   .dependencies = _dependencies,
+   .dependencies_count = ARRAYSIZE(_dependencies),
+   .descr = "OONF DLEP router plugin",
+   .author = "Henning Rogge",
+   .cfg_section = &_router_section,
+   .early_cfg_init = _early_cfg_init,
+   .init = _init,
+   .initiate_shutdown = _initiate_shutdown,
+   .cleanup = _cleanup,
+ };
+ DECLARE_OONF_PLUGIN(_dlep_router_subsystem);
+ enum oonf_log_source LOG_DLEP_ROUTER;
+ static void
+ _early_cfg_init(void) {
+   LOG_DLEP_ROUTER = _dlep_router_subsystem.logging;
+ }
+ /**
+  * Plugin constructor for dlep router
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _init(void) {
+   dlep_router_interface_init();
+   return 0;
+ }
+ /**
+  * Send a clean Peer Terminate before we drop the session to shutdown
+  */
+ static void
+ _initiate_shutdown(void) {
+   dlep_router_terminate_all_sessions();
+ }
+ /**
+  * Plugin destructor for dlep router
+  */
+ static void
+ _cleanup(void) {
+   dlep_router_interface_cleanup();
+ }
+ /**
+  * Callback for configuration changes
+  */
+ static void
+ _cb_config_changed(void) {
+   struct dlep_router_if *interface;
+   const char *ifname;
+   char ifbuf[IF_NAMESIZE];
+   ifname = cfg_get_phy_if(ifbuf, _router_section.section_name);
+   if (!_router_section.post) {
+     /* remove old session object */
+     interface = dlep_router_get_by_layer2_if(ifname);
+     if (interface) {
+       dlep_router_remove_interface(interface);
+     }
+     return;
+   }
+   /* get session object or create one */
+   interface = dlep_router_add_interface(ifname);
+   if (!interface) {
+     return;
+   }
+   /* read configuration */
+   if (cfg_schema_tobin(interface, _router_section.post, _router_entries, ARRAYSIZE(_router_entries))) {
+     OONF_WARN(LOG_DLEP_ROUTER, "Could not convert " OONF_DLEP_ROUTER_SUBSYSTEM " config to bin");
+     return;
+   }
+   /* use section name as default for datapath interface */
+   if (!interface->interf.udp_config.interface[0]) {
+     strscpy(interface->interf.udp_config.interface, _router_section.section_name,
+       sizeof(interface->interf.udp_config.interface));
+   }
+   else {
+     cfg_get_phy_if(interface->interf.udp_config.interface, interface->interf.udp_config.interface);
+   }
+   /* apply settings */
+   dlep_router_apply_interface_settings(interface);
+ }
index 0000000,0000000..aa4293d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,510 @@@
++
++/*
++ * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
++ * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ *   notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ *   notice, this list of conditions and the following disclaimer in
++ *   the documentation and/or other materials provided with the
++ *   distribution.
++ * * Neither the name of olsr.org, olsrd nor the names of its
++ *   contributors may be used to endorse or promote products derived
++ *   from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
++ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
++ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Visit http://www.olsr.org for more information.
++ *
++ * If you find this software useful feel free to make a donation
++ * to the project. For more information see the website or contact
++ * the copyright holders.
++ *
++ */
++
++#include <errno.h>
++
++#include <oonf/oonf.h>
++#include <oonf/libcommon/avl.h>
++#include <oonf/libcommon/avl_comp.h>
++#include <oonf/libcommon/netaddr.h>
++#include <oonf/libconfig/cfg_schema.h>
++#include <oonf/libcore/oonf_logging.h>
++#include <oonf/libcore/oonf_subsystem.h>
++#include <oonf/base/oonf_class.h>
++#include <oonf/base/oonf_layer2.h>
++#include <oonf/base/os_routing.h>
++
++#include "oonf/generic/layer2_export/layer2_export.h"
++
++/*! logging for plugin */
++#define LOG_L2EXPORT _l2export_subsystem.logging
++
++/**
++ * Additional parameters of an imported layer2 network
++ */
++struct _l2export_data {
++  /*! originator to import, defined as the section name */
++  char originator[16];
++
++  /*! fib distance */
++  int32_t fib_distance;
++
++  /*! fib routing table */
++  int32_t fib_table;
++
++  /*! fib protocol */
++  int32_t fib_protocol;
++
++  /*! tree of routes imported by this section */
++  struct avl_tree route_tree;
++
++  /*! node to hold all l2imports together */
++  struct avl_node _node;
++};
++
++/*! Life cycle of a route exported by this plugin */
++enum route_status {
++  /*! nothing has been done */
++  ROUTE_NOTHING,
++
++  /*! route is currently being added to the FIB */
++  ROUTE_ADDING,
++
++  /*! route has been added to the FIB */
++  ROUTE_ADDED,
++
++  /*! route is currently being removed from the FIB */
++  ROUTE_REMOVING,
++
++  /*! route has been removed from the FIB */
++  ROUTE_REMOVED,
++};
++
++/*! route object for export to FIB */
++struct _l2export_route {
++  /*! os route settings */
++  struct os_route os;
++
++  /*! lifecycle status of this object */
++  enum route_status status;
++
++  /*! back pointer to export data object */
++  struct _l2export_data *export_data;
++
++  /*! node for export data route tree */
++  struct avl_node _node;
++};
++
++/* prototypes */
++static int _init(void);
++static void _cleanup(void);
++static void _initiate_shutdown(void);
++
++static struct _l2export_data *_get_l2export(const char *name);
++static void _destroy_l2export(struct _l2export_data *);
++static bool _is_matching_origin(struct oonf_layer2_neighbor_address *, const char *pattern);
++
++static struct _l2export_route *_get_route(struct _l2export_data *data, struct os_route_key *key);
++static void _destroy_route(struct _l2export_route *route);
++static void _cb_route_finished(struct os_route *route, int error);
++
++static void _cb_l2neigh_ip_added(void *);
++static void _cb_l2neigh_ip_removed(void *);
++
++static void _cb_cfg_changed(void);
++
++static struct cfg_schema_entry _l2export_entries[] = {
++  CFG_MAP_INT32_MINMAX(_l2export_data, fib_distance, "fib_distance", "2",
++      "fib distance for exported layer2 entries", 0, 1, 255),
++  CFG_MAP_INT32_MINMAX(_l2export_data, fib_table, "fib_table", "254",
++      "fib table for exported layer2 entries", 0, 1, 65535),
++  CFG_MAP_INT32_MINMAX(_l2export_data, fib_protocol, "fib_protocol", "100",
++      "fib protocol for exported layer2 entries", 0, 1, 255),
++};
++
++static struct cfg_schema_section _l2export_section = {
++  .type = OONF_LAYER2_EXPORT_SUBSYSTEM,
++  .mode = CFG_SSMODE_NAMED,
++
++  .cb_delta_handler = _cb_cfg_changed,
++  .entries = _l2export_entries,
++  .entry_count = ARRAYSIZE(_l2export_entries),
++};
++
++static const char *_dependencies[] = {
++  OONF_CLASS_SUBSYSTEM,
++  OONF_LAYER2_SUBSYSTEM,
++  OONF_OS_ROUTING_SUBSYSTEM,
++};
++static struct oonf_subsystem _l2export_subsystem = {
++  .name = OONF_LAYER2_EXPORT_SUBSYSTEM,
++  .dependencies = _dependencies,
++  .dependencies_count = ARRAYSIZE(_dependencies),
++  .init = _init,
++  .cleanup = _cleanup,
++  .initiate_shutdown = _initiate_shutdown,
++
++  .cfg_section = &_l2export_section,
++};
++DECLARE_OONF_PLUGIN(_l2export_subsystem);
++
++/*! tree to remember all imported layer2 originators */
++static struct avl_tree _l2export_tree;
++
++/* class definition for filters */
++static struct oonf_class _l2export_class = {
++  .name = "layer2 export",
++  .size = sizeof(struct _l2export_data),
++};
++
++static struct oonf_class _route_class = {
++  .name = "layer2 route",
++  .size = sizeof(struct _l2export_route),
++};
++
++static struct oonf_class_extension _l2neighip_ext = {
++  .ext_name = "l2export listener",
++  .class_name = LAYER2_CLASS_NEIGHBOR_ADDRESS,
++
++  .cb_add = _cb_l2neigh_ip_added,
++  .cb_remove = _cb_l2neigh_ip_removed,
++};
++
++/* tree of routing exporters */
++static struct avl_tree _l2export_tree;
++
++/* tree of removing routes */
++static struct avl_tree _removal_tree;
++
++/**
++ * Initialize plugin
++ * @return always returns 0 (cannot fail)
++ */
++static int
++_init(void) {
++  if (oonf_class_extension_add(&_l2neighip_ext)) {
++    return -1;
++  }
++  avl_init(&_l2export_tree, avl_comp_strcasecmp, false);
++  avl_init(&_removal_tree, os_routing_avl_cmp_route_key, false);
++  oonf_class_add(&_l2export_class);
++  oonf_class_add(&_route_class);
++  return 0;
++}
++
++/**
++ * Cleanup plugin
++ */
++static void
++_cleanup(void) {
++  oonf_class_remove(&_l2export_class);
++  oonf_class_extension_remove(&_l2neighip_ext);
++}
++
++static void
++_initiate_shutdown(void) {
++  struct _l2export_data *mod, *mod_it;
++
++  avl_for_each_element_safe(&_l2export_tree, mod, _node, mod_it) {
++    _destroy_l2export(mod);
++  }
++}
++
++/**
++ * Lookups a layer2 export or create a new one
++ * @param name name of layer2 export
++ * @return pointer to export data or NULL if out of memory
++ */
++static struct _l2export_data *
++_get_l2export(const char *name) {
++  struct _l2export_data *mod;
++
++  mod = avl_find_element(&_l2export_tree, name, mod, _node);
++  if (mod) {
++    return mod;
++  }
++
++  mod = oonf_class_malloc(&_l2export_class);
++  if (mod == NULL) {
++    return NULL;
++  }
++
++  /* copy key and add to tree */
++  strscpy(mod->originator, name, sizeof(mod->originator));
++  mod->_node.key = mod->originator;
++  avl_insert(&_l2export_tree, &mod->_node);
++
++  /* initialize */
++  avl_init(&mod->route_tree, os_routing_avl_cmp_route_key, false);
++
++  return mod;
++}
++
++/**
++ * Free all resources associated with a layer2 import
++ * @param l2export layer2 import
++ */
++static void
++_destroy_l2export(struct _l2export_data *l2export) {
++  struct _l2export_route *l2route, *l2route_it;
++
++  avl_for_each_element_safe(&l2export->route_tree, l2route, _node, l2route_it) {
++    _destroy_route(l2route);
++  }
++
++  /* first remove the import settings from the tree */
++  avl_remove(&_l2export_tree, &l2export->_node);
++
++  oonf_class_free(&_l2export_class, l2export);
++}
++
++/**
++* Checks if the originator name of a l2 neighbor address matches a pattern
++* @param addr l2 neighbor address
++* @param pattern pattern (can end with an asterix wildcard)
++* @return true if matching, false otherwise
++*/
++static bool
++_is_matching_origin(struct oonf_layer2_neighbor_address *addr, const char *pattern) {
++  int len;
++
++  if (strcmp(addr->origin->name, pattern) == 0) {
++    return true;
++  }
++
++  len = strlen(pattern);
++  if (len == 0 || pattern[len-1] != '*') {
++    return false;
++  }
++
++  return strncmp(addr->origin->name, pattern, len-1) == 0;
++}
++
++/**
++* Returns an existing route object or creates a new one
++* @param data layer export data this route belongs to
++* @param key routing key (source/destination) IP
++* @return route, NULL if out of memory
++*/
++static struct _l2export_route *
++_get_route(struct _l2export_data *data, struct os_route_key *key) {
++  struct _l2export_route *l2route;
++
++  l2route = avl_find_element(&data->route_tree, key, l2route, _node);
++  if (l2route) {
++    return l2route;
++  }
++
++  l2route = oonf_class_malloc(&_route_class);
++  if (!l2route) {
++    return NULL;
++  }
++
++  /* hook into tree */
++  memcpy(&l2route->os.p.key, key, sizeof(*key));
++  l2route->_node.key = &l2route->os.p.key;
++  avl_insert(&data->route_tree, &l2route->_node);
++
++  /* initialize */
++  l2route->os.cb_finished = _cb_route_finished;
++  l2route->export_data = data;
++  return l2route;
++}
++
++/**
++* triggers the removal of a route or removes the object from memory
++* @param l2route route object
++*/
++static void
++_destroy_route(struct _l2export_route *l2route) {
++  struct os_route_str rbuf;
++
++  switch (l2route->status) {
++    case ROUTE_NOTHING:
++      avl_remove(&l2route->export_data->route_tree, &l2route->_node);
++      oonf_class_free(&_route_class, l2route);
++      break;
++    case ROUTE_ADDING:
++      os_routing_interrupt(&l2route->os);
++      break;
++      /* fallthrough */
++    case ROUTE_ADDED:
++      /* remove from export database */
++      avl_remove(&l2route->export_data->route_tree, &l2route->_node);
++      l2route->export_data = NULL;
++
++      /* remove route */
++      OONF_DEBUG(LOG_L2EXPORT, "remove route %s from fib", os_routing_to_string(&rbuf, &l2route->os.p));
++      os_routing_set(&l2route->os, false, false);
++      avl_insert(&_removal_tree, &l2route->_node);
++      l2route->status = ROUTE_REMOVING;
++      break;
++    case ROUTE_REMOVING:
++      /* wait for finisher */
++      break;
++    case ROUTE_REMOVED:
++      avl_remove(&_removal_tree, &l2route->_node);
++      oonf_class_free(&_route_class, l2route);
++      break;
++    default:
++      break;
++  }
++}
++
++/**
++* Callback for os routing system when route handling is finished
++* @param os_route route that has been finished
++* @param error error code, 0 if everything is okay
++*/
++static void
++_cb_route_finished(struct os_route *os_route, int error) {
++  struct _l2export_route *l2route;
++  struct os_route_str rbuf;
++
++  l2route = container_of(os_route, struct _l2export_route, os);
++
++  OONF_DEBUG(LOG_L2EXPORT, "route finished (error=%d, status=%d): %s",
++      error, l2route->status, os_routing_to_string(&rbuf, &os_route->p));
++  switch (l2route->status) {
++    case ROUTE_ADDING:
++      l2route->status = ROUTE_ADDED;
++      if (error) {
++        _destroy_route(l2route);
++      }
++      break;
++    case ROUTE_REMOVING:
++      l2route->status = ROUTE_REMOVED;
++      _destroy_route(l2route);
++      break;
++    default:
++      OONF_WARN(LOG_L2EXPORT, "Got route feedback for state %d", l2route->status);
++      _destroy_route(l2route);
++      break;
++  }
++}
++
++/**
++* Callback triggered when a l2 neighbor address is addrd
++* @param ptr address being added
++*/
++static void
++_cb_l2neigh_ip_added(void *ptr) {
++  struct oonf_layer2_neighbor_address *nip = ptr;
++  struct _l2export_data *l2export;
++  struct _l2export_route *l2route;
++  struct os_route_key rt_key;
++  int8_t af;
++  struct os_route_str rbuf;
++  struct netaddr_str nbuf;
++
++  os_routing_init_sourcespec_prefix(&rt_key, &nip->ip);
++
++  avl_for_each_element(&_l2export_tree, l2export, _node) {
++    OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s",
++                   l2export->originator, nip->origin->name);
++    if (_is_matching_origin(nip, l2export->originator)) {
++      OONF_DEBUG(LOG_L2EXPORT, "match");
++      l2route = _get_route(l2export, &rt_key);
++      if (!l2route) {
++        continue;
++      }
++
++      OONF_DEBUG(LOG_L2EXPORT, "got entry");
++
++      // TODO: what if this route is not in state "nothing" ?
++      af = netaddr_get_address_family(&nip->ip);
++
++      /* set route parameters */
++      l2route->os.p.family = af;
++      memcpy(&l2route->os.p.gw, oonf_layer2_neigh_get_nexthop(nip->l2neigh, af), sizeof(struct netaddr));
++      l2route->os.p.type = OS_ROUTE_UNICAST;
++      l2route->os.p.metric   = l2export->fib_distance;
++      l2route->os.p.if_index = nip->l2neigh->network->if_listener.data->index;
++      l2route->os.p.protocol = l2export->fib_protocol;
++      l2route->os.p.table    = l2export->fib_table;
++
++      OONF_DEBUG(LOG_L2EXPORT, "Add route %s to fib (gw was %s)",
++          os_routing_to_string(&rbuf, &l2route->os.p),
++          netaddr_to_string(&nbuf, oonf_layer2_neigh_get_nexthop(nip->l2neigh, af)));
++      if (!os_routing_set(&l2route->os, true, false)) {
++        l2route->status = ROUTE_ADDING;
++      }
++    }
++  }
++}
++
++/**
++* Callback triggered when a l2 neighbor address is removed
++* @param ptr address being removed
++*/
++static void
++_cb_l2neigh_ip_removed(void *ptr) {
++  struct oonf_layer2_neighbor_address *nip = ptr;
++  struct _l2export_data *l2export;
++  struct _l2export_route *l2route;
++  struct os_route_key rt_key;
++
++  os_routing_init_sourcespec_prefix(&rt_key, &nip->ip);
++
++  avl_for_each_element(&_l2export_tree, l2export, _node) {
++    OONF_DEBUG(LOG_L2EXPORT, "Check export %s against originator %s",
++               l2export->originator, nip->origin->name);
++    if (_is_matching_origin(nip, l2export->originator)) {
++      OONF_DEBUG(LOG_L2EXPORT, "match");
++      l2route = avl_find_element(&l2export->route_tree, &rt_key, l2route, _node);
++      if (l2route) {
++        OONF_DEBUG(LOG_L2EXPORT, "found entry");
++        _destroy_route(l2route);
++      }
++    }
++  }
++}
++
++/**
++ * Configuration changed
++ */
++static void
++_cb_cfg_changed(void) {
++  struct _l2export_data *l2export;
++
++  /* get existing import */
++    l2export = _get_l2export(_l2export_section.section_name);
++  if (!l2export) {
++    /* out of memory */
++    return;
++  }
++
++  if (!_l2export_section.post) {
++    /* section was removed */
++        _destroy_l2export(l2export);
++    return;
++  }
++
++  if (cfg_schema_tobin(l2export, _l2export_section.post, _l2export_entries, ARRAYSIZE(_l2export_entries))) {
++    OONF_WARN(LOG_L2EXPORT,
++        "Could not convert configuration data of section '%s'", _l2export_section.section_name);
++
++    if (!_l2export_section.pre) {
++            _destroy_l2export(l2export);
++    }
++    return;
++  }
++}
index 0000000,9897866..1b1b982
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,277 +1,277 @@@
 -  neigh->last_seen = oonf_clock_getNow();
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <stdio.h>
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libcommon/string.h>
+ #include <oonf/libcommon/template.h>
+ #include <oonf/libconfig/cfg_schema.h>
+ #include <oonf/libcore/oonf_logging.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_clock.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_timer.h>
+ #include <oonf/generic/layer2_generator/layer2_generator.h>
+ /* Definitions */
+ #define LOG_L2GEN _layer2_generator_subsystem.logging
+ /* prototypes */
+ static int _init(void);
+ static void _cleanup(void);
+ static void _cb_l2gen_event(struct oonf_timer_instance *);
+ static void _cb_config_changed(void);
+ /**
+  * Configuration of layer2 generator
+  */
+ struct _l2_generator_config {
+   /*! interval between two layer2 event generations */
+   uint64_t interval;
+   /*! true if generator is active */
+   bool active;
+   /*! name of interface for event generation */
+   char interface[IF_NAMESIZE];
+   /*! neighbor mac address for event generation */
+   struct netaddr neighbor;
+   /*! proxied MAC behind neighbor for event generation */
+   struct netaddr destination;
+ };
+ static struct oonf_timer_class _l2gen_timer_info = {
+   .name = "L2 Generator event",
+   .callback = _cb_l2gen_event,
+   .periodic = true,
+ };
+ static struct oonf_timer_instance _l2gen_timer = {
+   .class = &_l2gen_timer_info,
+ };
+ /* configuration */
+ static struct _l2_generator_config _l2gen_config;
+ static struct cfg_schema_entry _l2gen_entries[] = {
+   CFG_MAP_CLOCK_MIN(_l2_generator_config, interval, "interval", "3.000", "Interval between L2 generator events", 500),
+   CFG_MAP_STRING_ARRAY(_l2_generator_config, interface, "interface", "eth0", "Interface of example radio", IF_NAMESIZE),
+   CFG_MAP_NETADDR_MAC48(
+     _l2_generator_config, neighbor, "neighbor", "02:00:00:00:00:01", "Mac address of example radio", false, false),
+   CFG_MAP_NETADDR_MAC48(_l2_generator_config, destination, "destination", "02:00:00:00:00:02",
+     "Mac address of example radio destination", false, true),
+   CFG_MAP_BOOL(_l2_generator_config, active, "active", "false", "Activates artificially generated layer2 data"),
+ };
+ static struct cfg_schema_section _l2gen_section = {
+   .type = OONF_L2GEN_SUBSYSTEM,
+   .cb_delta_handler = _cb_config_changed,
+   .entries = _l2gen_entries,
+   .entry_count = ARRAYSIZE(_l2gen_entries),
+ };
+ /* plugin declaration */
+ static const char *_dependencies[] = {
+   OONF_CLOCK_SUBSYSTEM,
+   OONF_LAYER2_SUBSYSTEM,
+   OONF_TIMER_SUBSYSTEM,
+ };
+ static struct oonf_subsystem _layer2_generator_subsystem = {
+   .name = OONF_L2GEN_SUBSYSTEM,
+   .dependencies = _dependencies,
+   .dependencies_count = ARRAYSIZE(_dependencies),
+   .descr = "OONF layer2-generator plugin",
+   .author = "Henning Rogge",
+   .cfg_section = &_l2gen_section,
+   .init = _init,
+   .cleanup = _cleanup,
+ };
+ DECLARE_OONF_PLUGIN(_layer2_generator_subsystem);
+ static struct oonf_layer2_origin _origin = {
+   .name = "layer2 generator",
+   .proactive = true,
+   .priority = OONF_LAYER2_ORIGIN_CONFIGURED,
+ };
+ /**
+  * Constructor of plugin
+  * @return 0 if initialization was successful, -1 otherwise
+  */
+ static int
+ _init(void) {
+   memset(&_l2gen_config, 0, sizeof(_l2gen_config));
+   oonf_layer2_origin_add(&_origin);
+   oonf_timer_add(&_l2gen_timer_info);
+   oonf_timer_start(&_l2gen_timer, 5000);
+   return 0;
+ }
+ /**
+  * Destructor of plugin
+  */
+ static void
+ _cleanup(void) {
+   oonf_layer2_origin_remove(&_origin);
+   oonf_timer_stop(&_l2gen_timer);
+   oonf_timer_remove(&_l2gen_timer_info);
+ }
+ static void
+ _set_data(struct oonf_layer2_data *data, enum oonf_layer2_data_type type, int64_t value) {
+   switch (type) {
+     case OONF_LAYER2_INTEGER_DATA:
+       oonf_layer2_data_set_int64(data, &_origin, value);
+       break;
+     case OONF_LAYER2_BOOLEAN_DATA:
+       oonf_layer2_data_set_bool(data, &_origin, (value & 1) != 0);
+       break;
+     default:
+       break;
+   }
+ }
+ /**
+  * Callback for generating new layer2 test data
+  * @param ptr timer instance that fired
+  */
+ static void
+ _cb_l2gen_event(struct oonf_timer_instance *ptr __attribute((unused))) {
+   static uint64_t event_counter = 100;
+   enum oonf_layer2_network_index net_idx;
+   enum oonf_layer2_neighbor_index neigh_idx;
+   struct oonf_layer2_net *net;
+   struct oonf_layer2_neigh *neigh;
+ #ifdef OONF_LOG_DEBUG_INFO
+   struct netaddr_str buf1;
+ #endif
+   if (oonf_layer2_origin_is_added(&_origin)) {
+     return;
+   }
+   event_counter++;
+   OONF_DEBUG(LOG_L2GEN, "L2Gen-Event triggered (%s/%s/%" PRIu64 ")", _l2gen_config.interface,
+     netaddr_to_string(&buf1, &_l2gen_config.neighbor), event_counter);
+   net = oonf_layer2_net_add(_l2gen_config.interface);
+   if (net == NULL) {
+     OONF_WARN(LOG_L2GEN, "Cannot allocate layer2_network");
+     return;
+   }
+   strscpy(net->if_ident, "Interface generated by layer2-generator plugin", sizeof(net->if_ident));
+   net->if_type = OONF_LAYER2_TYPE_UNDEFINED;
+   net->last_seen = oonf_clock_getNow();
+   for (net_idx = 0; net_idx < OONF_LAYER2_NET_COUNT; net_idx++) {
+     _set_data(&net->data[net_idx], oonf_layer2_net_metadata_get(net_idx)->type, event_counter);
+   }
+   for (neigh_idx = 0; neigh_idx < OONF_LAYER2_NEIGH_COUNT; neigh_idx++) {
+     _set_data(&net->neighdata[neigh_idx], oonf_layer2_neigh_metadata_get(neigh_idx)->type, event_counter);
+   }
+   if (oonf_layer2_net_commit(net)) {
+     /* something bad has happened, l2net was removed */
+     OONF_WARN(LOG_L2GEN, "Could not commit interface %s", _l2gen_config.interface);
+     return;
+   }
+   neigh = oonf_layer2_neigh_add(net, &_l2gen_config.neighbor);
+   if (neigh == NULL) {
+     OONF_WARN(LOG_L2GEN, "Cannot allocate layer2_neighbor");
+     return;
+   }
+   if (netaddr_get_address_family(&_l2gen_config.destination) == AF_MAC48) {
+     oonf_layer2_destination_add(neigh, &_l2gen_config.destination, &_origin);
+   }
+   memcpy(&neigh->key.addr, &_l2gen_config.neighbor, sizeof(neigh->key.addr));
+   neigh->key.link_id[0] = event_counter & 0xff;
+   neigh->key.link_id_length = 1;
++  oonf_layer2_neigh_set_lastseen(neigh, oonf_clock_getNow());
+   for (neigh_idx = 0; neigh_idx < OONF_LAYER2_NEIGH_COUNT; neigh_idx++) {
+     _set_data(&neigh->data[neigh_idx], oonf_layer2_neigh_metadata_get(neigh_idx)->type, event_counter);
+   }
+   oonf_layer2_neigh_commit(neigh);
+ }
+ static void
+ _cb_config_changed(void) {
+   if (cfg_schema_tobin(&_l2gen_config, _l2gen_section.post, _l2gen_entries, ARRAYSIZE(_l2gen_entries))) {
+     OONF_WARN(LOG_L2GEN, "Could not convert " OONF_L2GEN_SUBSYSTEM " plugin configuration");
+     return;
+   }
+   cfg_get_phy_if(_l2gen_config.interface, _l2gen_config.interface);
+   OONF_DEBUG(LOG_L2GEN, "Generator is now %s for interface %s\n", _l2gen_config.active ? "active" : "inactive",
+     _l2gen_config.interface);
+   if (!oonf_layer2_origin_is_added(&_origin) && _l2gen_config.active) {
+     oonf_layer2_origin_add(&_origin);
+   }
+   else if (oonf_layer2_origin_is_added(&_origin) && !_l2gen_config.active) {
+     oonf_layer2_origin_remove(&_origin);
+   }
+   /* set new interval */
+   oonf_timer_set(&_l2gen_timer, _l2gen_config.interval);
+ }
index 0000000,0000000..2824d1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,594 @@@
++
++/*
++ * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
++ * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ *   notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ *   notice, this list of conditions and the following disclaimer in
++ *   the documentation and/or other materials provided with the
++ *   distribution.
++ * * Neither the name of olsr.org, olsrd nor the names of its
++ *   contributors may be used to endorse or promote products derived
++ *   from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
++ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
++ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Visit http://www.olsr.org for more information.
++ *
++ * If you find this software useful feel free to make a donation
++ * to the project. For more information see the website or contact
++ * the copyright holders.
++ *
++ */
++
++/**
++ * @file
++ */
++
++#include <oonf/oonf.h>
++#include <oonf/libcommon/autobuf.h>
++#include <oonf/libcommon/avl.h>
++#include <oonf/libcommon/avl_comp.h>
++#include <oonf/libcommon/list.h>
++#include <oonf/libcommon/netaddr.h>
++#include <oonf/libcommon/netaddr_acl.h>
++#include <oonf/libconfig/cfg_schema.h>
++#include <oonf/libcore/oonf_logging.h>
++#include <oonf/libcore/oonf_subsystem.h>
++
++#include <oonf/base/oonf_class.h>
++#include <oonf/base/oonf_clock.h>
++#include <oonf/base/oonf_layer2.h>
++#include <oonf/base/oonf_timer.h>
++#include <oonf/base/os_interface.h>
++#include <oonf/base/os_routing.h>
++
++#include <oonf/generic/layer2_import/layer2_import.h>
++
++/* definitions */
++#define LOG_L2_IMPORT _import_subsystem.logging
++
++/**
++ * configuration of one LAN import instance
++ */
++struct _import_entry {
++  /*! name of the lan import */
++  char name[20];
++
++  struct oonf_layer2_origin l2origin;
++
++  /*! domain of the lan import */
++  int32_t domain;
++
++  /*! address filter */
++  struct netaddr_acl filter;
++
++  /*! filter by prefix length, -1 to ignore */
++  int32_t prefix_length;
++
++  /*! filter by interface name, length null to ignore*/
++  char ifname[IF_NAMESIZE];
++
++  /*! filter by routing table id, 0 to ignore */
++  int32_t table;
++
++  /*! filter by routing protocol id, 0 to ignore */
++  int32_t protocol;
++
++  /*! filter by routing metric, 0 to ignore */
++  int32_t distance;
++
++  /*! set MAC address of imported entries to this interface */
++  char fixed_mac_if[IF_NAMESIZE];
++
++  /*! helper to keep track of MAC of 'fixed' interface */
++  struct os_interface_listener fixed_if_listener;
++
++  /*! tree of all configured lan import */
++  struct avl_node _node;
++};
++
++/* prototypes */
++static int _init(void);
++static void _initiate_shutdown(void);
++static void _cleanup(void);
++
++static struct _import_entry *_get_import(const char *name);
++static void _remove_import(struct _import_entry *);
++
++static void _cb_query(struct os_route *filter, struct os_route *route);
++static void _cb_query_finished(struct os_route *, int error);
++
++static void _cb_rt_event(const struct os_route *, bool);
++static void _cb_reload_routes(struct oonf_timer_instance *);
++
++static void _cb_lan_cfg_changed(void);
++static void _cb_l2_cfg_changed(void);
++static void _cb_cfg_changed(struct cfg_schema_section *section, char *section_name);
++
++/* plugin declaration */
++static struct cfg_schema_entry _l2_entries[] = {
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, domain, "domain", "-1", "Routing domain extension for filter, -1 for all domains", 0, -1, 255),
++  CFG_MAP_ACL(_import_entry, filter, "matches", ACL_DEFAULT_ACCEPT,
++    "Ip addresses the filter should be applied to"
++    " (the plugin will never import loopback, linklocal or multicast IPs)"),
++  CFG_MAP_INT32_MINMAX(_import_entry, prefix_length, "prefix_length", "-1",
++    "Prefix length the filter should be applied to, -1 for any prefix length", 0, -1, 128),
++  CFG_MAP_STRING_ARRAY(
++    _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 255),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, distance, "metric", "-1", "Metric of matching routes, 0 for all metrics", 0, -1, INT32_MAX),
++  CFG_MAP_STRING_ARRAY(_import_entry, fixed_mac_if, "fixed_mac_if", "",
++    "Name of interface that will be used to fill in layer2 entry MAC addresses", IF_NAMESIZE),
++};
++
++static struct cfg_schema_entry _lan_entries[] = {
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, domain, "domain", "-1", "Routing domain extension for filter, -1 for all domains", 0, -1, 255),
++  CFG_MAP_ACL(_import_entry, filter, "matches", ACL_DEFAULT_ACCEPT,
++    "Ip addresses the filter should be applied to"
++    " (the plugin will never import loopback, linklocal or multicast IPs)"),
++  CFG_MAP_INT32_MINMAX(_import_entry, prefix_length, "prefix_length", "-1",
++    "Prefix length the filter should be applied to, -1 for any prefix length", 0, -1, 128),
++  CFG_MAP_STRING_ARRAY(
++    _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 255),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255),
++  CFG_MAP_INT32_MINMAX(
++    _import_entry, distance, "metric", "-1", "Metric of matching routes, 0 for all metrics", 0, -1, INT32_MAX),
++  CFG_MAP_STRING_ARRAY(_import_entry, fixed_mac_if, "fixed_mac_if", "",
++    "Name of interface that will be used to fill in layer2 entry MAC addresses", IF_NAMESIZE),
++};
++
++static struct cfg_schema_section _lan_import_section = {
++  .type = OONF_LAN_IMPORT_SECTION,
++
++  /*
++   * this MUST NOT be CFG_SSMODE_NAMED_WITH_DEFAULT, otherwise it will
++   * activate without user interaction
++   */
++  .mode = CFG_SSMODE_NAMED,
++
++  .cb_delta_handler = _cb_lan_cfg_changed,
++
++  .entries = _lan_entries,
++  .entry_count = ARRAYSIZE(_lan_entries),
++};
++
++static struct cfg_schema_section _l2_import_section = {
++  .type = OONF_LAYER2_IMPORT_SUBSYSTEM,
++
++  /*
++   * this MUST NOT be CFG_SSMODE_NAMED_WITH_DEFAULT, otherwise it will
++   * activate without user interaction
++   */
++  .mode = CFG_SSMODE_NAMED,
++
++  .cb_delta_handler = _cb_l2_cfg_changed,
++
++  .entries = _l2_entries,
++  .entry_count = ARRAYSIZE(_l2_entries),
++
++  .next_section = &_lan_import_section,
++};
++
++static const char *_dependencies[] = {
++  OONF_CLASS_SUBSYSTEM,
++  OONF_CLOCK_SUBSYSTEM,
++  OONF_TIMER_SUBSYSTEM,
++  OONF_OS_INTERFACE_SUBSYSTEM,
++  OONF_OS_ROUTING_SUBSYSTEM,
++};
++static struct oonf_subsystem _import_subsystem = {
++  .name = OONF_LAYER2_IMPORT_SUBSYSTEM,
++  .dependencies = _dependencies,
++  .dependencies_count = ARRAYSIZE(_dependencies),
++  .descr = "OLSRv2 lan-import plugin",
++  .author = "Henning Rogge",
++
++  .cfg_section = &_l2_import_section,
++
++  .init = _init,
++  .cleanup = _cleanup,
++  .initiate_shutdown = _initiate_shutdown,
++};
++DECLARE_OONF_PLUGIN(_import_subsystem);
++
++/* class definition for filters */
++static struct oonf_class _import_class = {
++  .name = "l2 import filter",
++  .size = sizeof(struct _import_entry),
++};
++
++/* timer for triggering 'lazy' reload of routes */
++static struct oonf_timer_class _route_reload = {
++  .name = "l2 import route reload",
++  .callback = _cb_reload_routes,
++};
++static struct oonf_timer_instance _route_reload_instance = {
++  .class = &_route_reload,
++};
++
++/* callback filter for dijkstra */
++static struct os_route_listener _routing_listener = {
++  .cb_get = _cb_rt_event,
++};
++
++/* tree of lan importers */
++static struct avl_tree _import_tree;
++
++/* wildcard route for first query */
++static struct os_route _unicast_query;
++
++/**
++ * Initialize plugin
++ * @return always returns 0 (cannot fail)
++ */
++static int
++_init(void) {
++  avl_init(&_import_tree, avl_comp_strcasecmp, false);
++
++  oonf_class_add(&_import_class);
++  oonf_timer_add(&_route_reload);
++  os_routing_listener_add(&_routing_listener);
++
++  /* initialize wildcard query */
++  os_routing_init_wildcard_route(&_unicast_query);
++  _unicast_query.cb_get = _cb_query;
++  _unicast_query.cb_finished = _cb_query_finished;
++  _unicast_query.p.type = OS_ROUTE_UNICAST;
++  return 0;
++}
++
++static void
++_initiate_shutdown(void) {
++  /* we are not interested in listening to all the routing cleanup */
++  os_routing_listener_remove(&_routing_listener);
++}
++
++/**
++ * Cleanup plugin
++ */
++static void
++_cleanup(void) {
++  struct _import_entry *import, *import_it;
++
++  avl_for_each_element_safe(&_import_tree, import, _node, import_it) {
++    _remove_import(import);
++  }
++
++  oonf_timer_remove(&_route_reload);
++  oonf_class_remove(&_import_class);
++}
++
++/**
++ * Wrapper for cb_get for wildcard query
++ * @param filter unused filter
++ * @param route route found by wildcard query
++ */
++static void
++_cb_query(struct os_route *filter __attribute__((unused)), struct os_route *route) {
++  _cb_rt_event(route, true);
++}
++
++/**
++ * Dummy cb_finished callback for wildcard query
++ * @param route route that was finished
++ * @param error error code
++ */
++static void
++_cb_query_finished(struct os_route *route __attribute__((unused)), int error __attribute__((unused))) {}
++
++/**
++ * Callback for route listener
++ * @param route routing data
++ * @param set true if route was set, false otherwise
++ */
++static void
++_cb_rt_event(const struct os_route *route, bool set) {
++  struct _import_entry *import;
++  char ifname[IF_NAMESIZE];
++  struct oonf_layer2_net *l2net;
++  struct oonf_layer2_neigh *l2neigh;
++  struct oonf_layer2_neighbor_address *l2neigh_ip;
++  struct oonf_layer2_neigh_key nb_key;
++  const struct netaddr *gw, *dst, *mac;
++
++#ifdef OONF_LOG_DEBUG_INFO
++  struct os_route_str rbuf;
++  struct netaddr_str nbuf;
++#endif
++
++  if (netaddr_is_in_subnet(&NETADDR_IPV4_MULTICAST, &route->p.key.dst) ||
++      netaddr_is_in_subnet(&NETADDR_IPV4_LINKLOCAL, &route->p.key.dst) ||
++      netaddr_is_in_subnet(&NETADDR_IPV4_LOOPBACK_NET, &route->p.key.dst) ||
++      netaddr_is_in_subnet(&NETADDR_IPV6_MULTICAST, &route->p.key.dst) ||
++      netaddr_is_in_subnet(&NETADDR_IPV6_LINKLOCAL, &route->p.key.dst) ||
++      netaddr_is_in_subnet(&NETADDR_IPV6_LOOPBACK, &route->p.key.dst)) {
++    /* ignore multicast, linklocal and loopback */
++    return;
++  }
++  if (route->p.type != OS_ROUTE_UNICAST) {
++    /* return all non-unicast type routes */
++    return;
++  }
++
++  OONF_DEBUG(
++    LOG_L2_IMPORT, "Received route event (%s): %s", set ? "set" : "remove", os_routing_to_string(&rbuf, &route->p));
++
++  /* get interface name for route */
++  if (!route->p.if_index) {
++    /* should not happen for unicast routes */
++    return;
++  }
++
++  if_indextoname(route->p.if_index, ifname);
++
++  avl_for_each_element(&_import_tree, import, _node) {
++    OONF_DEBUG(LOG_L2_IMPORT, "Check for import: %s", import->name);
++
++    /* check prefix length */
++    if (import->prefix_length != -1 && import->prefix_length != netaddr_get_prefix_length(&route->p.key.dst)) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Bad prefix length %u (filter was %d)",
++                 netaddr_get_prefix_length(&route->p.key.dst), import->prefix_length);
++      continue;
++    }
++
++    /* check if destination matches */
++    if (!netaddr_acl_check_accept(&import->filter, &route->p.key.dst)) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Bad prefix %s", netaddr_to_string(&nbuf, &route->p.key.dst));
++      continue;
++    }
++
++    /* check routing table */
++    if (import->table != -1 && import->table != route->p.table) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Bad routing table %u (filter was %d)", route->p.table, import->table);
++      continue;
++    }
++
++    /* check protocol */
++    if (import->protocol != -1 && import->protocol != route->p.protocol) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Bad protocol %u (filter was %d)", route->p.protocol, import->protocol);
++      continue;
++    }
++
++    /* check metric */
++    if (import->distance != -1 && import->distance != route->p.metric) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Bad distance %u (filter was %d)", route->p.metric, import->distance);
++      continue;
++    }
++
++    /* check interface name */
++    if (import->ifname[0]) {
++      if (route->p.if_index == 0) {
++        OONF_DEBUG(LOG_L2_IMPORT, "Route has no interface");
++        continue;
++      }
++      if (strcmp(import->ifname, ifname) != 0) {
++        OONF_DEBUG(LOG_L2_IMPORT, "Bad interface '%s' (filter was '%s')", ifname, import->ifname);
++        continue;
++      }
++    }
++
++    /* get layer2 network */
++    if (set) {
++      l2net = oonf_layer2_net_add(ifname);
++    }
++    else {
++      l2net = oonf_layer2_net_get(ifname);
++    }
++    if (!l2net) {
++      OONF_DEBUG(LOG_L2_IMPORT, "No l2 network found");
++      return;
++    }
++
++    mac = NULL;
++    if (import->fixed_mac_if[0]) {
++      if (import->fixed_if_listener.data) {
++        mac = &import->fixed_if_listener.data->mac;
++      }
++    }
++    else {
++      mac = &l2net->if_listener.data->mac;
++    }
++    if (netaddr_is_unspec(mac)) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Wait for interface data to be initialized");
++      if (!oonf_timer_is_active(&_route_reload_instance)) {
++        oonf_timer_set(&_route_reload_instance, 1000);
++      }
++      return;
++    }
++
++    dst = &route->p.key.dst;
++    gw = &route->p.gw;
++
++    /* generate l2 key including LID */
++    if (oonf_layer2_neigh_generate_lid(&nb_key, &import->l2origin, mac)) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Could not generate LID for MAC %s",
++          netaddr_to_string(&nbuf, mac));
++      continue;
++    }
++
++    /* get layer2 neighbor */
++    if (set) {
++      l2neigh = oonf_layer2_neigh_add_lid(l2net, &nb_key);
++    }
++    else {
++      l2neigh = oonf_layer2_neigh_get_lid(l2net, &nb_key);
++    }
++    if (!l2neigh) {
++      OONF_DEBUG(LOG_L2_IMPORT, "No l2 neighbor found");
++      return;
++    }
++
++    /* make sure next hop is initialized */
++    if (!oonf_layer2_neigh_set_nexthop(l2neigh, gw)) {
++      oonf_layer2_neigh_commit(l2neigh);
++    }
++
++    if (set) {
++      OONF_DEBUG(LOG_L2_IMPORT, "Add lan...");
++
++      if (!oonf_layer2_neigh_get_remote_ip(l2neigh, dst)) {
++        oonf_layer2_neigh_add_ip(l2neigh, &import->l2origin, dst);
++      }
++    }
++    else {
++      OONF_DEBUG(LOG_L2_IMPORT, "Remove lan...");
++
++      l2neigh_ip = oonf_layer2_neigh_get_remote_ip(l2neigh, dst);
++      if (l2neigh_ip) {
++        oonf_layer2_neigh_remove_ip(l2neigh_ip, &import->l2origin);
++      }
++    }
++  }
++}
++
++/**
++ * Lookups a lan importer or create a new one
++ * @param name name of lan importer
++ * @return pointer to lan importer or NULL if out of memory
++ */
++static struct _import_entry *
++_get_import(const char *name) {
++  struct _import_entry *import;
++
++  import = avl_find_element(&_import_tree, name, import, _node);
++  if (import) {
++    return import;
++  }
++
++  import = oonf_class_malloc(&_import_class);
++  if (import == NULL) {
++    return NULL;
++  }
++
++  /* copy key and add to tree */
++  snprintf(import->name, sizeof(import->name), LAN_ORIGIN_PREFIX "%s", name);
++  import->_node.key = import->name;
++  avl_insert(&_import_tree, &import->_node);
++
++  /* request layer2 origin */
++  import->l2origin.name = import->name;
++  import->l2origin.priority = OONF_LAYER2_ORIGIN_RELIABLE;
++  import->l2origin.proactive = true;
++  import->l2origin.lid = true;
++
++  oonf_layer2_origin_add(&import->l2origin);
++
++  /* initialize l2 fixed interface listener */
++  import->fixed_if_listener.name = import->fixed_mac_if;
++
++  return import;
++}
++
++/**
++ * Free all resources associated with a route modifier
++ * @param import import entry
++ */
++static void
++_remove_import(struct _import_entry *import) {
++  oonf_layer2_origin_remove(&import->l2origin);
++  avl_remove(&_import_tree, &import->_node);
++  netaddr_acl_remove(&import->filter);
++  oonf_class_free(&_import_class, import);
++}
++
++static void
++_cb_reload_routes(struct oonf_timer_instance *timer __attribute__((unused))) {
++  /* trigger wildcard query */
++  if (!os_routing_is_in_progress(&_unicast_query)) {
++    os_routing_query(&_unicast_query);
++  }
++}
++
++/**
++ * Configuration changed
++ */
++static void
++_cb_lan_cfg_changed(void) {
++  char name[20];
++
++  snprintf(name, sizeof(name), LAN_ORIGIN_PREFIX "%s", _lan_import_section.section_name);
++  _cb_cfg_changed(&_lan_import_section, name);
++}
++
++static void
++_cb_l2_cfg_changed(void) {
++  char name[20];
++
++  snprintf(name, sizeof(name), L2IMPORT_ORIGIN_PREFIX "%s", _l2_import_section.section_name);
++  _cb_cfg_changed(&_l2_import_section, name);
++}
++
++/**
++ * Configuration changed
++ */
++static void
++_cb_cfg_changed(struct cfg_schema_section *section, char *section_name) {
++  struct _import_entry *import;
++
++  /* get existing modifier */
++  import = _get_import(section_name);
++  if (!import) {
++    /* out of memory */
++    return;
++  }
++
++  if (section->post == NULL) {
++    /* section was removed */
++    _remove_import(import);
++    return;
++  }
++
++  /* remove old interface listener */
++  os_interface_remove(&import->fixed_if_listener);
++
++  if (cfg_schema_tobin(import, section->post, section->entries, section->entry_count)) {
++    OONF_WARN(LOG_L2_IMPORT, "Could not convert configuration data of section '%s'", section->section_name);
++
++    if (section->pre == NULL) {
++      _remove_import(import);
++    }
++    return;
++  }
++
++  cfg_get_phy_if(import->ifname, import->ifname);
++  cfg_get_phy_if(import->fixed_mac_if, import->fixed_mac_if);
++
++  if (!import->fixed_mac_if[0]) {
++    strscpy(import->fixed_mac_if, import->ifname, IF_NAMESIZE);
++  }
++  if (import->fixed_mac_if[0]) {
++    os_interface_add(&import->fixed_if_listener);
++  }
++
++  if (!oonf_timer_is_active(&_route_reload_instance)) {
++    oonf_timer_set(&_route_reload_instance, 1000);
++  }
++}
index 0000000,19e7f17..3e078be
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,705 +1,725 @@@
 -  if (neigh->last_seen) {
 -    oonf_clock_toIntervalString(&_value_neigh_lastseen, -oonf_clock_get_relative(neigh->last_seen));
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <stdio.h>
+ #include <oonf/libcommon/autobuf.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libcommon/netaddr_acl.h>
+ #include <oonf/libcommon/string.h>
+ #include <oonf/libcommon/template.h>
+ #include <oonf/libcore/oonf_logging.h>
+ #include <oonf/libcore/oonf_subsystem.h>
+ #include <oonf/base/oonf_clock.h>
+ #include <oonf/base/oonf_layer2.h>
+ #include <oonf/base/oonf_telnet.h>
+ #include <oonf/base/oonf_viewer.h>
+ #include <oonf/generic/layer2info/layer2info.h>
+ /* definitions */
+ #define LOG_LAYER2INFO _oonf_layer2info_subsystem.logging
+ /* prototypes */
+ static int _init(void);
+ static void _cleanup(void);
+ static enum oonf_telnet_result _cb_layer2info(struct oonf_telnet_data *con);
+ static enum oonf_telnet_result _cb_layer2info_help(struct oonf_telnet_data *con);
+ static void _initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data);
+ static void _initialize_if_origin_values(struct oonf_layer2_data *data);
+ static void _initialize_if_values(struct oonf_layer2_net *net);
+ static void _initialize_if_ip_values(struct oonf_layer2_peer_address *peer_ip);
+ static void _initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data);
+ static void _initialize_neigh_origin_values(struct oonf_layer2_data *data);
+ static void _initialize_neigh_values(struct oonf_layer2_neigh *neigh);
+ static void _initialize_neigh_ip_values(struct oonf_layer2_neighbor_address *neigh_addr);
+ static int _cb_create_text_interface(struct oonf_viewer_template *);
+ static int _cb_create_text_interface_ip(struct oonf_viewer_template *);
+ static int _cb_create_text_neighbor(struct oonf_viewer_template *);
+ static int _cb_create_text_neighbor_ip(struct oonf_viewer_template *);
+ static int _cb_create_text_default(struct oonf_viewer_template *);
+ static int _cb_create_text_dst(struct oonf_viewer_template *);
+ /*
+  * list of template keys and corresponding buffers for values.
+  *
+  * The keys are API, so they should not be changed after published
+  */
+ /*! template key for interface name */
+ #define KEY_IF "if"
+ /*! template key for interface index */
+ #define KEY_IF_INDEX "if_index"
+ /*! template key for interface type */
+ #define KEY_IF_TYPE "if_type"
+ /*! template key for DLEP interface */
+ #define KEY_IF_DLEP "if_dlep"
+ /*! template key for interface identifier */
+ #define KEY_IF_IDENT "if_ident"
+ /*! template key for interface address identifier */
+ #define KEY_IF_IDENT_ADDR "if_ident_addr"
+ /*! template key for local interface address */
+ #define KEY_IF_LOCAL_ADDR "if_local_addr"
+ /*! template key for last time interface was active */
+ #define KEY_IF_LASTSEEN "if_lastseen"
+ /*! template key for IP/prefixes of the local radio/model */
+ #define KEY_IF_PEER_IP "if_peer_ip"
+ /*! template key for IP/prefixes origin of the local radio/model */
+ #define KEY_IF_PEER_IP_ORIGIN "if_peer_ip_origin"
+ /*! template key for neighbor address */
+ #define KEY_NEIGH_ADDR "neigh_addr"
+ /*! template key for neighbor link-id */
+ #define KEY_NEIGH_LID "neigh_lid"
+ /*! template key for neighbor link-id length */
+ #define KEY_NEIGH_LID_LEN "neigh_lid_length"
++/*! template key for neighbor IPv4 next hop */
++#define KEY_NEIGH_NEXTHOP_V4 "neigh_nexthop_v4"
++
++/*! template key for neighbor IPv4 next hop */
++#define KEY_NEIGH_NEXTHOP_V6 "neigh_nexthop_v6"
++
+ /*! template key for last time neighbor was active */
+ #define KEY_NEIGH_LASTSEEN "neigh_lastseen"
+ /*! template key for IP/prefixes of the neighbors remote router */
+ #define KEY_NEIGH_REMOTE_IP "neigh_remote_ip"
++/*! template key for neighbors IP next hop */
++#define KEY_NEIGH_REMOTE_NEXTHOP "neigh_remote_ip_nexthop"
++
+ /*! template key for IP/prefixes origin of the neighbors remote router */
+ #define KEY_NEIGH_REMOTE_IP_ORIGIN "neigh_remote_ip_origin"
+ /*! template key for destination address */
+ #define KEY_DST_ADDR "dst_addr"
+ /*! template key for destination origin */
+ #define KEY_DST_ORIGIN "dst_origin"
+ /*! string prefix for all interface keys */
+ #define KEY_IF_PREFIX "if_"
+ /*! string prefix for all neighbor keys */
+ #define KEY_NEIGH_PREFIX "neigh_"
+ /*! string suffix for all data originators */
+ #define KEY_ORIGIN_SUFFIX "_origin"
+ /*
+  * buffer space for values that will be assembled
+  * into the output of the plugin
+  */
+ static char _value_if[IF_NAMESIZE];
+ static char _value_if_index[12];
+ static char _value_if_type[16];
+ static char _value_if_dlep[TEMPLATE_JSON_BOOL_LENGTH];
+ static char _value_if_ident[33];
+ static struct netaddr_str _value_if_ident_addr;
+ static struct netaddr_str _value_if_local_addr;
+ static struct isonumber_str _value_if_lastseen;
+ static struct netaddr_str _value_if_peer_ip;
+ static char _value_if_peer_ip_origin[IF_NAMESIZE];
+ static char _value_if_data[OONF_LAYER2_NET_COUNT][64];
+ static char _value_if_origin[OONF_LAYER2_NET_COUNT][IF_NAMESIZE];
+ static struct netaddr_str _value_neigh_addr;
+ static union oonf_layer2_neigh_key_str _value_neigh_key;
++static struct netaddr_str _value_neigh_nexthop_v4;
++static struct netaddr_str _value_neigh_nexthop_v6;
+ static char _value_neigh_key_length[6];
+ static struct isonumber_str _value_neigh_lastseen;
+ static struct netaddr_str _value_neigh_remote_ip;
++static struct netaddr_str _value_neigh_remote_ip_nexthop;
+ static char _value_neigh_remote_ip_origin[IF_NAMESIZE];
+ static char _value_neigh_data[OONF_LAYER2_NEIGH_COUNT][64];
+ static char _value_neigh_origin[OONF_LAYER2_NEIGH_COUNT][IF_NAMESIZE];
+ static struct netaddr_str _value_dst_addr;
+ static char _value_dst_origin[IF_NAMESIZE];
+ /* definition of the template data entries for JSON and table output */
+ static struct abuf_template_data_entry _tde_if_key[] = {
+   { KEY_IF, _value_if, true },
+   { KEY_IF_INDEX, _value_if_index, false },
+   { KEY_IF_LOCAL_ADDR, _value_if_local_addr.buf, true },
+ };
+ static struct abuf_template_data_entry _tde_if[] = {
+   { KEY_IF_TYPE, _value_if_type, true },
+   { KEY_IF_DLEP, _value_if_dlep, true },
+   { KEY_IF_IDENT, _value_if_ident, true },
+   { KEY_IF_IDENT_ADDR, _value_if_ident_addr.buf, true },
+   { KEY_IF_LASTSEEN, _value_if_lastseen.buf, false },
+ };
+ static struct abuf_template_data_entry _tde_if_peer_ip[] = {
+   { KEY_IF_PEER_IP, _value_if_peer_ip.buf, true },
+   { KEY_IF_PEER_IP_ORIGIN, _value_if_peer_ip_origin, true },
+ };
+ static struct abuf_template_data_entry _tde_if_data[OONF_LAYER2_NET_COUNT];
+ static struct abuf_template_data_entry _tde_if_origin[OONF_LAYER2_NET_COUNT];
+ static struct abuf_template_data_entry _tde_neigh_key[] = {
+   { KEY_NEIGH_ADDR, _value_neigh_addr.buf, true },
+   { KEY_NEIGH_LID, _value_neigh_key.buf, true },
+   { KEY_NEIGH_LID_LEN, _value_neigh_key_length, false },
+ };
+ static struct abuf_template_data_entry _tde_neigh[] = {
++  { KEY_NEIGH_NEXTHOP_V4, _value_neigh_nexthop_v4.buf, true },
++  { KEY_NEIGH_NEXTHOP_V6, _value_neigh_nexthop_v6.buf, true },
+   { KEY_NEIGH_LASTSEEN, _value_neigh_lastseen.buf, false },
+ };
+ static struct abuf_template_data_entry _tde_neigh_remote_ip[] = {
+   { KEY_NEIGH_REMOTE_IP, _value_neigh_remote_ip.buf, true },
++  { KEY_NEIGH_REMOTE_NEXTHOP, _value_neigh_remote_ip_nexthop.buf, true },
+   { KEY_NEIGH_REMOTE_IP_ORIGIN, _value_neigh_remote_ip_origin, true },
+ };
+ static struct abuf_template_data_entry _tde_neigh_data[OONF_LAYER2_NEIGH_COUNT];
+ static struct abuf_template_data_entry _tde_neigh_origin[OONF_LAYER2_NEIGH_COUNT];
+ static struct abuf_template_data_entry _tde_dst_key[] = {
+   { KEY_DST_ADDR, _value_dst_addr.buf, true },
+ };
+ static struct abuf_template_data_entry _tde_dst[] = {
+   { KEY_DST_ORIGIN, _value_dst_origin, true },
+ };
+ static struct abuf_template_storage _template_storage;
+ static struct autobuf _key_storage;
+ /* Template Data objects (contain one or more Template Data Entries) */
+ static struct abuf_template_data _td_if[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_if, ARRAYSIZE(_tde_if) },
+   { _tde_if_data, ARRAYSIZE(_tde_if_data) },
+   { _tde_if_origin, ARRAYSIZE(_tde_if_origin) },
+ };
+ static struct abuf_template_data _td_if_ips[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_if_peer_ip, ARRAYSIZE(_tde_if_peer_ip) },
+ };
+ static struct abuf_template_data _td_neigh[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_neigh_key, ARRAYSIZE(_tde_neigh_key) },
+   { _tde_neigh, ARRAYSIZE(_tde_neigh) },
+   { _tde_neigh_data, ARRAYSIZE(_tde_neigh_data) },
+   { _tde_neigh_origin, ARRAYSIZE(_tde_neigh_origin) },
+ };
+ static struct abuf_template_data _td_neigh_ips[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_neigh_key, ARRAYSIZE(_tde_neigh_key) },
+   { _tde_neigh_remote_ip, ARRAYSIZE(_tde_neigh_remote_ip) },
+ };
+ static struct abuf_template_data _td_default[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_neigh_data, ARRAYSIZE(_tde_neigh_data) },
+   { _tde_neigh_origin, ARRAYSIZE(_tde_neigh_origin) },
+ };
+ static struct abuf_template_data _td_dst[] = {
+   { _tde_if_key, ARRAYSIZE(_tde_if_key) },
+   { _tde_neigh_key, ARRAYSIZE(_tde_neigh_key) },
+   { _tde_dst_key, ARRAYSIZE(_tde_dst_key) },
+   { _tde_dst, ARRAYSIZE(_tde_dst) },
+ };
+ /* OONF viewer templates (based on Template Data arrays) */
+ static struct oonf_viewer_template _templates[] = {
+   {
+     .data = _td_if,
+     .data_size = ARRAYSIZE(_td_if),
+     .json_name = "interface",
+     .cb_function = _cb_create_text_interface,
+   },
+   {
+     .data = _td_if_ips,
+     .data_size = ARRAYSIZE(_td_if_ips),
+     .json_name = "interface_ip",
+     .cb_function = _cb_create_text_interface_ip,
+   },
+   {
+     .data = _td_neigh,
+     .data_size = ARRAYSIZE(_td_neigh),
+     .json_name = "neighbor",
+     .cb_function = _cb_create_text_neighbor,
+   },
+   {
+     .data = _td_neigh_ips,
+     .data_size = ARRAYSIZE(_td_neigh_ips),
+     .json_name = "neighbor_ip",
+     .cb_function = _cb_create_text_neighbor_ip,
+   },
+   {
+     .data = _td_default,
+     .data_size = ARRAYSIZE(_td_default),
+     .json_name = "default",
+     .cb_function = _cb_create_text_default,
+   },
+   {
+     .data = _td_dst,
+     .data_size = ARRAYSIZE(_td_dst),
+     .json_name = "destination",
+     .cb_function = _cb_create_text_dst,
+   },
+ };
+ /* telnet command of this plugin */
+ static struct oonf_telnet_command _telnet_commands[] = {
+   TELNET_CMD(OONF_LAYER2INFO_SUBSYSTEM, _cb_layer2info, "", .help_handler = _cb_layer2info_help),
+ };
+ /* plugin declaration */
+ static const char *_dependencies[] = {
+   OONF_CLOCK_SUBSYSTEM,
+   OONF_LAYER2_SUBSYSTEM,
+   OONF_TELNET_SUBSYSTEM,
+   OONF_VIEWER_SUBSYSTEM,
+ };
+ static struct oonf_subsystem _olsrv2_layer2info_subsystem = {
+   .name = OONF_LAYER2INFO_SUBSYSTEM,
+   .dependencies = _dependencies,
+   .dependencies_count = ARRAYSIZE(_dependencies),
+   .descr = "OLSRv2 layer2 info plugin",
+   .author = "Henning Rogge",
+   .init = _init,
+   .cleanup = _cleanup,
+ };
+ DECLARE_OONF_PLUGIN(_olsrv2_layer2info_subsystem);
+ /**
+  * Initialize plugin
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _init(void) {
+   size_t i;
+   abuf_init(&_key_storage);
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     _tde_if_data[i].key = abuf_getptr(&_key_storage) + abuf_getlen(&_key_storage);
+     _tde_if_data[i].value = _value_if_data[i];
+     _tde_if_data[i].string = true;
+     abuf_puts(&_key_storage, KEY_IF_PREFIX);
+     abuf_puts(&_key_storage, oonf_layer2_net_metadata_get(i)->key);
+     abuf_memcpy(&_key_storage, "\0", 1);
+     _tde_if_origin[i].key = abuf_getptr(&_key_storage) + abuf_getlen(&_key_storage);
+     _tde_if_origin[i].value = _value_if_origin[i];
+     _tde_if_origin[i].string = true;
+     abuf_puts(&_key_storage, KEY_IF_PREFIX);
+     abuf_puts(&_key_storage, oonf_layer2_net_metadata_get(i)->key);
+     abuf_puts(&_key_storage, KEY_ORIGIN_SUFFIX);
+     abuf_memcpy(&_key_storage, "\0", 1);
+   }
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     _tde_neigh_data[i].key = abuf_getptr(&_key_storage) + abuf_getlen(&_key_storage);
+     _tde_neigh_data[i].value = _value_neigh_data[i];
+     _tde_neigh_data[i].string = true;
+     abuf_puts(&_key_storage, KEY_NEIGH_PREFIX);
+     abuf_puts(&_key_storage, oonf_layer2_neigh_metadata_get(i)->key);
+     abuf_memcpy(&_key_storage, "\0", 1);
+     _tde_neigh_origin[i].key = abuf_getptr(&_key_storage) + abuf_getlen(&_key_storage);
+     _tde_neigh_origin[i].value = _value_neigh_origin[i];
+     _tde_neigh_origin[i].string = true;
+     abuf_puts(&_key_storage, KEY_NEIGH_PREFIX);
+     abuf_puts(&_key_storage, oonf_layer2_neigh_metadata_get(i)->key);
+     abuf_puts(&_key_storage, KEY_ORIGIN_SUFFIX);
+     abuf_memcpy(&_key_storage, "\0", 1);
+   }
+   oonf_telnet_add(&_telnet_commands[0]);
+   return abuf_has_failed(&_key_storage) ? -1 : 0;
+ }
+ /**
+  * Cleanup plugin
+  */
+ static void
+ _cleanup(void) {
+   oonf_telnet_remove(&_telnet_commands[0]);
+   abuf_free(&_key_storage);
+ }
+ /**
+  * Callback for the telnet command of this plugin
+  * @param con pointer to telnet session data
+  * @return telnet result value
+  */
+ static enum oonf_telnet_result
+ _cb_layer2info(struct oonf_telnet_data *con) {
+   return oonf_viewer_telnet_handler(
+     con->out, &_template_storage, OONF_LAYER2INFO_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates));
+ }
+ /**
+  * Callback for the help output of this plugin
+  * @param con pointer to telnet session data
+  * @return telnet result value
+  */
+ static enum oonf_telnet_result
+ _cb_layer2info_help(struct oonf_telnet_data *con) {
+   return oonf_viewer_telnet_help(
+     con->out, OONF_LAYER2INFO_SUBSYSTEM, con->parameter, _templates, ARRAYSIZE(_templates));
+ }
+ /**
+  * Initialize the value buffers for a layer2 interface
+  * @param net pointer to layer2 interface
+  */
+ static void
+ _initialize_if_values(struct oonf_layer2_net *net) {
+   struct os_interface *os_if;
+   os_if = net->if_listener.data;
+   strscpy(_value_if, net->name, sizeof(_value_if));
+   snprintf(_value_if_index, sizeof(_value_if_index), "%u", os_if->index);
+   strscpy(_value_if_ident, net->if_ident, sizeof(_value_if_ident));
+   netaddr_to_string(&_value_if_local_addr, &os_if->mac);
+   strscpy(_value_if_type, oonf_layer2_net_get_type_name(net->if_type), IF_NAMESIZE);
+   strscpy(_value_if_dlep, json_getbool(net->if_dlep), TEMPLATE_JSON_BOOL_LENGTH);
+   if (net->last_seen) {
+     oonf_clock_toIntervalString(&_value_if_lastseen, -oonf_clock_get_relative(net->last_seen));
+   }
+   else {
+     _value_if_lastseen.buf[0] = 0;
+   }
+ }
+ /**
+  * Initialize the value buffers for a l2 peer address object
+  * @param peer_ip peer address object
+  */
+ static void
+ _initialize_if_ip_values(struct oonf_layer2_peer_address *peer_ip) {
+   netaddr_to_string(&_value_if_peer_ip, &peer_ip->ip);
+   strscpy(_value_if_peer_ip_origin, peer_ip->origin->name, sizeof(_value_if_peer_ip_origin));
+ }
+ /**
+  * Initialize the value buffers for an array of layer2 data objects
+  * @param template viewer template
+  * @param data array of data objects
+  */
+ static void
+ _initialize_if_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data) {
+   size_t i;
+   memset(_value_if_data, 0, sizeof(_value_if_data));
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     oonf_layer2_net_data_to_string(_value_if_data[i], sizeof(_value_if_data[i]), &data[i], i, template->create_raw);
+   }
+ }
+ /**
+  * Initialize the network origin buffers for an array of layer2 data objects
+  * @param data array of data objects
+  */
+ static void
+ _initialize_if_origin_values(struct oonf_layer2_data *data) {
+   size_t i;
+   memset(_value_if_origin, 0, sizeof(_value_if_origin));
+   for (i = 0; i < OONF_LAYER2_NET_COUNT; i++) {
+     if (oonf_layer2_data_has_value(&data[i])) {
+       strscpy(_value_if_origin[i], oonf_layer2_data_get_origin(&data[i])->name, IF_NAMESIZE);
+     }
+   }
+ }
+ /**
+  * Initialize the value buffers for a layer2 neighbor
+  * @param neigh layer2 neighbor
+  */
+ static void
+ _initialize_neigh_values(struct oonf_layer2_neigh *neigh) {
+   netaddr_to_string(&_value_neigh_addr, &neigh->key.addr);
+   oonf_layer2_neigh_key_to_string(&_value_neigh_key, &neigh->key, false);
+   snprintf(_value_neigh_key_length, sizeof(_value_neigh_key_length), "%u", neigh->key.link_id_length);
++  netaddr_to_string(&_value_neigh_nexthop_v4, oonf_layer2_neigh_get_nexthop(neigh, AF_INET));
++  netaddr_to_string(&_value_neigh_nexthop_v6, oonf_layer2_neigh_get_nexthop(neigh, AF_INET6));
++
++  if (oonf_layer2_neigh_get_lastseen(neigh)) {
++    oonf_clock_toIntervalString(&_value_neigh_lastseen, -oonf_clock_get_relative(oonf_layer2_neigh_get_lastseen(neigh)));
+   }
+   else {
+     _value_neigh_lastseen.buf[0] = 0;
+   }
+ }
+ /**
+  * Initialize the value buffers for a l2 neighbor remote address object
+  * @param neigh_addr neighbor remote address
+  */
+ static void
+ _initialize_neigh_ip_values(struct oonf_layer2_neighbor_address *neigh_addr) {
+   netaddr_to_string(&_value_neigh_remote_ip, &neigh_addr->ip);
++  netaddr_to_string(&_value_neigh_remote_ip_nexthop,
++      oonf_layer2_neigh_get_nexthop(neigh_addr->l2neigh, netaddr_get_address_family(&neigh_addr->ip)));
+   strscpy(_value_neigh_remote_ip_origin, neigh_addr->origin->name, sizeof(_value_neigh_remote_ip_origin));
+ }
+ /**
+  * Initialize the value buffers for an array of layer2 data objects
+  * @param template viewer template
+  * @param data array of data objects
+  */
+ static void
+ _initialize_neigh_data_values(struct oonf_viewer_template *template, struct oonf_layer2_data *data) {
+   size_t i;
+   memset(_value_neigh_data, 0, sizeof(_value_neigh_data));
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     oonf_layer2_neigh_data_to_string(
+       _value_neigh_data[i], sizeof(_value_neigh_data[i]), &data[i], i, template->create_raw);
+   }
+ }
+ /**
+  * Initialize the network origin buffers for an array of layer2 data objects
+  * @param data array of data objects
+  */
+ static void
+ _initialize_neigh_origin_values(struct oonf_layer2_data *data) {
+   size_t i;
+   memset(_value_neigh_origin, 0, sizeof(_value_neigh_origin));
+   for (i = 0; i < OONF_LAYER2_NEIGH_COUNT; i++) {
+     if (oonf_layer2_data_has_value(&data[i])) {
+       strscpy(_value_neigh_origin[i], oonf_layer2_data_get_origin(&data[i])->name, IF_NAMESIZE);
+     }
+   }
+ }
+ /**
+  * Initialize the value buffers for a layer2 destination
+  * @param l2dst layer2 destination
+  */
+ static void
+ _initialize_destination_values(struct oonf_layer2_destination *l2dst) {
+   netaddr_to_string(&_value_dst_addr, &l2dst->destination);
+   strscpy(_value_dst_origin, l2dst->origin->name, IF_NAMESIZE);
+ }
+ /**
+  * Callback to generate text/json description of all layer2 interfaces
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_interface(struct oonf_viewer_template *template) {
+   struct oonf_layer2_net *net;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     _initialize_if_data_values(template, net->data);
+     _initialize_if_origin_values(net->data);
+     /* generate template output */
+     oonf_viewer_output_print_line(template);
+   }
+   return 0;
+ }
+ /**
+  * Callback to generate text/json description of all layer2 interface ips
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_interface_ip(struct oonf_viewer_template *template) {
+   struct oonf_layer2_net *net;
+   struct oonf_layer2_peer_address *peer_ip;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     avl_for_each_element(&net->local_peer_ips, peer_ip, _net_node) {
+       _initialize_if_ip_values(peer_ip);
+       /* generate template output */
+       oonf_viewer_output_print_line(template);
+     }
+   }
+   return 0;
+ }
+ /**
+  * Callback to generate text/json description of all layer2 neighbors
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_neighbor(struct oonf_viewer_template *template) {
+   struct oonf_layer2_neigh *neigh;
+   struct oonf_layer2_net *net;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     avl_for_each_element(&net->neighbors, neigh, _node) {
+       _initialize_neigh_values(neigh);
+       _initialize_neigh_data_values(template, neigh->data);
+       _initialize_neigh_origin_values(neigh->data);
+       /* generate template output */
+       oonf_viewer_output_print_line(template);
+     }
+   }
+   return 0;
+ }
+ /**
+  * Callback to generate text/json description of all layer2 neighbor ips
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_neighbor_ip(struct oonf_viewer_template *template) {
+   struct oonf_layer2_neighbor_address *remote_ip;
+   struct oonf_layer2_neigh *neigh;
+   struct oonf_layer2_net *net;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     avl_for_each_element(&net->neighbors, neigh, _node) {
+       _initialize_neigh_values(neigh);
+       avl_for_each_element(&neigh->remote_neighbor_ips, remote_ip, _neigh_node) {
+         _initialize_neigh_ip_values(remote_ip);
+         /* generate template output */
+         oonf_viewer_output_print_line(template);
+       }
+     }
+   }
+   return 0;
+ }
+ /**
+  * Callback to generate text/json description of the defaults stored
+  * in the layer2 interfaces for their neighbors
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_default(struct oonf_viewer_template *template) {
+   struct oonf_layer2_net *net;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     _initialize_neigh_data_values(template, net->neighdata);
+     _initialize_neigh_origin_values(net->neighdata);
+     /* generate template output */
+     oonf_viewer_output_print_line(template);
+   }
+   return 0;
+ }
+ /**
+  * Callback to generate text/json description of all layer2 destinations
+  * @param template viewer template
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _cb_create_text_dst(struct oonf_viewer_template *template) {
+   struct oonf_layer2_destination *l2dst;
+   struct oonf_layer2_neigh *neigh;
+   struct oonf_layer2_net *net;
+   avl_for_each_element(oonf_layer2_get_net_tree(), net, _node) {
+     _initialize_if_values(net);
+     avl_for_each_element(&net->neighbors, neigh, _node) {
+       _initialize_neigh_values(neigh);
+       avl_for_each_element(&neigh->destinations, l2dst, _node) {
+         _initialize_destination_values(l2dst);
+         /* generate template output */
+         oonf_viewer_output_print_line(template);
+       }
+     }
+   }
+   return 0;
+ }
index 0000000,16b62f4..c0858e1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,282 +1,283 @@@
 -    l2neigh->last_seen = oonf_clock_get_absolute(-((int64_t)(nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]))));
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ /*
+  * Much of the code of this file originally came from the iw userspace
+  * command source code and was adapted for OLSRv2.
+  *
+  * Copyright (c) 2007, 2008 Johannes Berg
+  * Copyright (c) 2007    Andy Lutomirski
+  * Copyright (c) 2007    Mike Kershaw
+  * Copyright (c) 2008-2009   Luis R. Rodriguez
+  *
+  * Permission to use, copy, modify, and/or distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+  * copyright notice and this permission notice appear in all copies.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ #define _GNU_SOURCE
+ /* must be first because of a problem with linux/netlink.h */
+ #include <sys/socket.h>
+ /* and now the rest of the includes */
+ #include <linux/genetlink.h>
+ #include <linux/netlink.h>
+ #include <linux/types.h>
+ #include <netlink/attr.h>
+ #include <netlink/genl/genl.h>
+ #include <netlink/msg.h>
+ #include <oonf/oonf.h>
+ #include <oonf/base/oonf_clock.h>
+ #include <oonf/base/os_system.h>
+ #include <oonf/generic/nl80211_listener/nl80211.h>
+ #include <oonf/generic/nl80211_listener/nl80211_get_station_dump.h>
+ #include <oonf/generic/nl80211_listener/nl80211_internal.h>
+ #include <oonf/generic/nl80211_listener/nl80211_listener.h>
+ static bool _handle_traffic(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, uint32_t new_32bit);
+ static int64_t _get_bitrate(struct nlattr *bitrate_attr);
+ /**
+  * Send a netlink message to get the nl80211 station dump
+  * @param nl pointer to netlink handler
+  * @param nl_msg pointer to netlink message
+  * @param hdr pointer to generic netlink header
+  * @param interf nl80211 listener interface
+  */
+ void
+ nl80211_send_get_station_dump(
+   struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) {
+   int if_index = nl80211_get_if_baseindex(interf);
+   hdr->cmd = NL80211_CMD_GET_STATION;
+   nl_msg->nlmsg_flags |= NLM_F_DUMP;
+   /* add interface index to the request */
+   os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index));
+ }
+ /**
+  * Process NL80211_CMD_NEW_STATION message
+  * @param interf nl80211 listener interface
+  * @param hdr pointer to netlink message header
+  */
+ void
+ nl80211_process_get_station_dump_result(struct nl80211_if *interf, struct nlmsghdr *hdr) {
+   struct oonf_layer2_neigh *l2neigh;
+   struct netaddr l2neigh_mac;
+   struct nlattr *tb[NL80211_ATTR_MAX + 1];
+   struct genlmsghdr *gnlh;
+   struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+   static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+     [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+     [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+     [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
+     [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+     [NL80211_STA_INFO_STA_FLAGS] = { .minlen = sizeof(struct nl80211_sta_flag_update) },
+   };
+ #ifdef OONF_LOG_DEBUG_INFO
+   struct netaddr_str nbuf;
+ #endif
+   gnlh = nlmsg_data(hdr);
+   nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
+   if (!tb[NL80211_ATTR_STA_INFO]) {
+     /* station info missing */
+     return;
+   }
+   if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy)) {
+     /* no nested attributes */
+     return;
+   }
+   if (nl80211_get_if_baseindex(interf) != nla_get_u32(tb[NL80211_ATTR_IFINDEX])) {
+     /* wrong interface */
+     return;
+   }
+   netaddr_from_binary(&l2neigh_mac, nla_data(tb[NL80211_ATTR_MAC]), 6, AF_MAC48);
+   OONF_DEBUG(LOG_NL80211, "Received Station Dump for %s", netaddr_to_string(&nbuf, &l2neigh_mac));
+   l2neigh = oonf_layer2_neigh_add(interf->l2net, &l2neigh_mac);
+   if (!l2neigh) {
+     /* no memory for l2 neighbor */
+     return;
+   }
+   if (sinfo[NL80211_STA_INFO_INACTIVE_TIME]) {
++    oonf_layer2_neigh_set_lastseen(l2neigh,
++        oonf_clock_get_absolute(-((int64_t)(nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME])))));
+   }
+   /* byte data is 64 bit */
+   if (sinfo[NL80211_STA_INFO_RX_BYTES64]) {
+     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_BYTES, nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
+   }
+   if (sinfo[NL80211_STA_INFO_TX_BYTES64]) {
+     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_BYTES, nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
+   }
+   /* packet data is only 32 bit */
+   if (sinfo[NL80211_STA_INFO_RX_PACKETS]) {
+     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_RX_FRAMES, nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
+   }
+   if (sinfo[NL80211_STA_INFO_TX_PACKETS]) {
+     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_FRAMES, nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
+   }
+   if (sinfo[NL80211_STA_INFO_TX_RETRIES]) {
+     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_RETRIES, nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
+   }
+   if (sinfo[NL80211_STA_INFO_TX_FAILED]) {
+     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_FAILED, nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
+   }
+   /* bitrates are special */
+   if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+     int64_t rate = _get_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE]);
+     if (rate) {
+       nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_BITRATE, rate);
+     }
+   }
+   if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
+     int64_t rate = _get_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE]);
+     if (rate) {
+       nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_BITRATE, rate);
+     }
+   }
+   /* expected throughput is special too */
+   if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
+     int64_t rate;
+     rate = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+     /* convert in bps */
+     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_THROUGHPUT, rate * 1024ll);
+   }
+   /* signal strength is special too */
+   if (sinfo[NL80211_STA_INFO_SIGNAL]) {
+     int8_t signal;
+     signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_SIGNAL, signal * 1000ll);
+   }
+   /* remove old data */
+   nl80211_cleanup_l2neigh_data(l2neigh);
+   /* and commit the changes */
+   oonf_layer2_neigh_commit(l2neigh);
+ }
+ static bool
+ _handle_traffic(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, uint32_t new_32bit) {
+   static const uint64_t UPPER_32_MASK = 0xffffffff00000000ull;
+   static const uint64_t LOWER_32_MASK = 0x00000000ffffffffull;
+   struct oonf_layer2_data *data;
+   int64_t old_value, new_value;
+   new_value = 0;
+   old_value = 0;
+   data = &l2neigh->data[idx];
+   oonf_layer2_data_read_int64(&old_value, data);
+   new_value = old_value & UPPER_32_MASK;
+   new_value |= (new_32bit & LOWER_32_MASK);
+   OONF_DEBUG(LOG_NL80211, "new32: 0x%08x old: %016" PRIx64 " new: %016" PRIx64, new_32bit, old_value, new_value);
+   if (new_value + 0x80000000ll < old_value) {
+     /* handle 32bit counter overflow */
+     new_value += 0x100000000ll;
+     OONF_DEBUG(LOG_NL80211, "Overflow, new: %016" PRIx64, new_value);
+   }
+   return nl80211_change_l2neigh_data(l2neigh, idx, new_value);
+ }
+ static int64_t
+ _get_bitrate(struct nlattr *bitrate_attr) {
+   int rate = 0;
+   struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+   static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+     [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+     [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+   };
+   if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, bitrate_attr, rate_policy)) {
+     /* error in parsing bitrate nested arguments */
+     return 0;
+   }
+   if (rinfo[NL80211_RATE_INFO_BITRATE32])
+     rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
+   else if (rinfo[NL80211_RATE_INFO_BITRATE])
+     rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
+   return 1024ll * 1024ll * rate / 10ll;
+ }
index 0000000,553fc69..b5fc72b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1228 +1,1228 @@@
 - * @param src netaddr source
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <ctype.h>
+ #include <net/if.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libcommon/string.h>
+ static char *_mac_to_string(char *dst, size_t dst_size, const void *bin, size_t bin_size, char separator);
+ static char *_uuid_to_string(char *dst, size_t dst_size, const void *bin, size_t bin_size);
+ static int _bin_from_hex(void *bin, size_t bin_size, const char *src, char separator);
+ static int _uuid_from_string(void *bin, size_t bin_size, const char *src);
+ static int _subnetmask_to_prefixlen(const char *src);
+ static int _read_hexdigit(const char c);
+ static bool _binary_is_in_subnet(const struct netaddr *subnet, const void *bin);
+ /* predefined network prefixes */
+ /*! unspecified network address */
+ const struct netaddr NETADDR_UNSPEC = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_UNSPEC, 0 };
+ /*! IPv4 default prefix */
+ const struct netaddr NETADDR_IPV4_ANY = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET, 0 };
+ /*! IPv6 default prefix */
+ const struct netaddr NETADDR_IPV6_ANY = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 0 };
+ /*! IPv4 NULL address */
+ const struct netaddr NETADDR_IPV4_BINDTO_ANY = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET, 32 };
+ /*! IPv6 NULL address */
+ const struct netaddr NETADDR_IPV6_BINDTO_ANY = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 128 };
+ /*! IPv4 multicast prefix */
+ const struct netaddr NETADDR_IPV4_MULTICAST = { { 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET, 4 };
+ /*! IPv6 multicast prefix */
+ const struct netaddr NETADDR_IPV6_MULTICAST = { { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 8 };
+ /*! IPv4 linklocal prefix */
+ const struct netaddr NETADDR_IPV4_LINKLOCAL = { { 169, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET, 16 };
+ /*! IPv6 linklocal prefix */
+ const struct netaddr NETADDR_IPV6_LINKLOCAL = { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6,
+   10 };
+ /*! IPv6 unique local prefix */
+ const struct netaddr NETADDR_IPV6_ULA = { { 0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 7 };
+ /*! IPv6 routable prefix */
+ const struct netaddr NETADDR_IPV6_GLOBAL = { { 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 3 };
+ /*! IPv6 ipv4-compatible prefix */
+ const struct netaddr NETADDR_IPV6_IPV4COMPATIBLE = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET6, 96 };
+ /*! IPv6 ipv4-mapped prefix */
+ const struct netaddr NETADDR_IPV6_IPV4MAPPED = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0 }, AF_INET6,
+   96 };
+ /*! IPv4 loopback prefix */
+ const struct netaddr NETADDR_IPV4_LOOPBACK_NET = { { 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, AF_INET, 8 };
+ /*! IPv6 loopback address */
+ const struct netaddr NETADDR_IPV6_LOOPBACK = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, AF_INET6, 128 };
+ /*! Ethernet broadcast */
+ const struct netaddr NETADDR_MAC48_BROADCAST = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+   AF_MAC48, 48 };
+ /*! Ethernet multicast prefix for IPv4 multicast */
+ const struct netaddr NETADDR_MAC48_IPV4_MULTICAST = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 }, AF_MAC48, 25 };
+ /*! Ethernet multicast prefix for IPv4 multicast */
+ const struct netaddr NETADDR_MAC48_IPV6_MULTICAST = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }, AF_MAC48, 16 };
+ /*! socket for binding to any IPv4 address */
+ const union netaddr_socket NETADDR_SOCKET_IPV4_ANY = { .v4 = {
+                                                          .sin_family = AF_INET,
+                                                          .sin_port = 0,
+                                                          .sin_addr.s_addr = 0,
+                                                        } };
+ /*! socket for binding to any IPv6 address */
+ const union netaddr_socket NETADDR_SOCKET_IPV6_ANY = { .v6 = {
+                                                          .sin6_family = AF_INET6,
+                                                          .sin6_port = 0,
+                                                          .sin6_addr.s6_addr32 = { 0, 0, 0, 0 },
+                                                          .sin6_scope_id = 0,
+                                                        } };
+ /* List of predefined address prefixes */
+ static const struct {
+   const char *name;
+   const struct netaddr *prefix;
+ } _known_prefixes[] = {
+   { NETADDR_STR_ANY4, &NETADDR_IPV4_ANY },
+   { NETADDR_STR_ANY6, &NETADDR_IPV6_ANY },
+   { NETADDR_STR_LINKLOCAL4, &NETADDR_IPV4_LINKLOCAL },
+   { NETADDR_STR_LINKLOCAL6, &NETADDR_IPV6_LINKLOCAL },
+   { NETADDR_STR_ULA, &NETADDR_IPV6_ULA },
+ };
+ /**
+  * Read the binary representation of an address into a netaddr object
+  * @param dst pointer to netaddr object
+  * @param binary source pointer
+  * @param len length of source buffer
+  * @param addr_type address type of source,
+  *     0 to autodetect type from length
+  * @param prefix_len prefix length of source,
+  *     255 for maximum prefix length depending on type
+  * @return 0 if successful recognized the address type
+  *     of the binary data, -1 otherwise
+  */
+ int
+ netaddr_from_binary_prefix(struct netaddr *dst, const void *binary, size_t len, uint8_t addr_type, uint8_t prefix_len) {
+   memset(dst->_addr, 0, sizeof(dst->_addr));
+   if (addr_type != 0) {
+     dst->_type = addr_type;
+   }
+   else {
+     switch (len) {
+       case 4:
+         dst->_type = AF_INET;
+         break;
+       case 6:
+         dst->_type = AF_MAC48;
+         break;
+       case 8:
+         dst->_type = AF_EUI64;
+         break;
+       case 16:
+         dst->_type = AF_INET6;
+         break;
+       default:
+         dst->_type = AF_UNSPEC;
+         if (len > 16) {
+           len = 16;
+         }
+         break;
+     }
+   }
+   if (prefix_len == 255) {
+     prefix_len = len << 3;
+   }
+   dst->_prefix_len = prefix_len;
+   memcpy(dst->_addr, binary, len);
+   return dst->_type == AF_UNSPEC ? -1 : 0;
+ }
+ /**
+  * Writes a netaddr object into a binary buffer
+  * @param dst binary buffer
+  * @param src netaddr source
+  * @param len length of destination buffer
+  * @return 0 if successful read binary data, -1 otherwise
+  */
+ int
+ netaddr_to_binary(void *dst, const struct netaddr *src, size_t len) {
+   uint32_t addr_len;
+   addr_len = netaddr_get_maxprefix(src) >> 3;
+   if (addr_len == 0 || len < addr_len) {
+     /* unknown address type */
+     return -1;
+   }
+   memcpy(dst, src->_addr, addr_len);
+   return 0;
+ }
+ /**
+  * Reads the address and address-type part of an
+  * netaddr_socket into a netaddr object
+  * @param dst netaddr object
+  * @param src netaddr_socket source
+  * @return 0 if successful read binary data, -1 otherwise
+  */
+ int
+ netaddr_from_socket(struct netaddr *dst, const union netaddr_socket *src) {
+   memset(dst->_addr, 0, sizeof(dst->_addr));
+   if (src->std.sa_family == AF_INET) {
+     /* ipv4 */
+     memcpy(dst->_addr, &src->v4.sin_addr, 4);
+     dst->_prefix_len = 32;
+   }
+   else if (src->std.sa_family == AF_INET6) {
+     /* ipv6 */
+     memcpy(dst->_addr, &src->v6.sin6_addr, 16);
+     dst->_prefix_len = 128;
+   }
+   else {
+     /* unknown address type */
+     dst->_type = AF_UNSPEC;
+     return -1;
+   }
+   dst->_type = (uint8_t)src->std.sa_family;
+   return 0;
+ }
+ /**
+  * Writes the address and address-type of a netaddr object
+  * into a netaddr_socket.
+  * @param dst pointer to netaddr_socket
+  * @param src netaddr source
+  * @return 0 if successful read binary data, -1 otherwise
+  */
+ int
+ netaddr_to_socket(union netaddr_socket *dst, const struct netaddr *src) {
+   /* copy address type */
+   dst->std.sa_family = src->_type;
+   switch (src->_type) {
+     case AF_INET:
+       /* ipv4 */
+       memcpy(&dst->v4.sin_addr, src->_addr, 4);
+       break;
+     case AF_INET6:
+       /* ipv6 */
+       memcpy(&dst->v6.sin6_addr, src->_addr, 16);
+       break;
+     default:
+       /* unknown address type */
+       return -1;
+   }
+   /* copy address type */
+   dst->std.sa_family = src->_type;
+   return 0;
+ }
+ /**
+  * Append binary address to autobuf
+  * @param abuf pointer to target autobuf
+  * @param src pointer to source address
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ netaddr_to_autobuf(struct autobuf *abuf, const struct netaddr *src) {
+   uint32_t addr_len;
+   addr_len = netaddr_get_maxprefix(src) >> 3;
+   if (addr_len == 0) {
+     /* unknown address type */
+     return -1;
+   }
+   return abuf_memcpy(abuf, src->_addr, addr_len);
+ }
+ /**
+  * Creates a host address from a netmask and a host number part. This function
+  * will copy the netmask and then overwrite the bits after the prefix length
+  * with the one from the host number.
+  * @param host target buffer
+  * @param netmask prefix of result
+  * @param number postfix of result
+  * @param num_length length of the postfix in bytes
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ netaddr_create_host_bin(struct netaddr *host, const struct netaddr *netmask, const void *number, size_t num_length) {
+   size_t host_index, number_index;
+   uint8_t host_part_length;
+   const uint8_t *number_byte;
+   uint8_t mask;
+   number_byte = number;
+   /* copy netmask with prefixlength max */
+   memcpy(host, netmask, sizeof(*netmask));
+   host->_prefix_len = netaddr_get_maxprefix(host);
+   /* unknown address type */
+   if (host->_prefix_len == 0) {
+     return -1;
+   }
+   /* netmask has no host part */
+   if (host->_prefix_len == netmask->_prefix_len || num_length == 0) {
+     return 0;
+   }
+   /* calculate starting byte in host and number */
+   host_part_length = (host->_prefix_len - netmask->_prefix_len + 7) / 8;
+   if (host_part_length > num_length) {
+     host_index = host->_prefix_len / 8 - num_length;
+     number_index = 0;
+   }
+   else {
+     host_index = netmask->_prefix_len / 8;
+     number_index = num_length - host_part_length;
+     /* copy bit masked part */
+     if ((netmask->_prefix_len & 7) != 0) {
+       mask = (255 >> (netmask->_prefix_len & 7));
+       host->_addr[host_index] &= (~mask);
+       host->_addr[host_index] |= (number_byte[number_index++]) & mask;
+       host_index++;
+     }
+   }
+   /* copy bytes */
+   memcpy(&host->_addr[host_index], &number_byte[number_index], num_length - number_index);
+   return 0;
+ }
+ /**
+  * Creates the network prefix from a host and a netmask. Both host and
+  * netmask must be the same address family.
+  * @param prefix target buffer to write prefix into
+  * @param host host address
+  * @param netmask well formed netmask
+  * @param truncate true if host IP should be truncated
+  * @return -1 if an error happened, 0 otherwise
+  */
+ int
+ netaddr_create_prefix(
+   struct netaddr *prefix, const struct netaddr *host, const struct netaddr *netmask, bool truncate) {
+   int len, maxlen;
+   if (host->_type != netmask->_type || host->_type == AF_UNSPEC) {
+     return -1;
+   }
+   /* get address length */
+   maxlen = netaddr_get_af_maxprefix(host->_type) / 8;
+   /* initialize prefix */
+   prefix->_prefix_len = 0;
+   prefix->_type = host->_type;
+   if (!truncate) {
+     memcpy(&prefix->_addr[0], &host->_addr[0], 16);
+   }
+   for (len = 0; len < maxlen; len++) {
+     if (truncate) {
+       prefix->_addr[len] = host->_addr[len] & netmask->_addr[len];
+     }
+     switch (netmask->_addr[len]) {
+       case 255:
+         len++;
+         prefix->_prefix_len += 8;
+         break;
+       case 254:
+         prefix->_prefix_len += 7;
+         return 0;
+       case 252:
+         prefix->_prefix_len += 6;
+         return 0;
+       case 248:
+         prefix->_prefix_len += 5;
+         return 0;
+       case 240:
+         prefix->_prefix_len += 4;
+         return 0;
+       case 224:
+         prefix->_prefix_len += 3;
+         return 0;
+       case 192:
+         prefix->_prefix_len += 2;
+         return 0;
+       case 128:
+         prefix->_prefix_len += 1;
+         return 0;
+       case 0:
+         return 0;
+       default:
+         return -1;
+     }
+   }
+   return 0;
+ }
+ /**
+  * Clear the bits outside of the prefix length of an address
+  * @param dst trucated destination buffer
+  * @param src source IP
+  */
+ void
+ netaddr_truncate(struct netaddr *dst, const struct netaddr *src) {
+   static uint8_t MASK[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+   int byte_prefix, bit_prefix;
+   /* calulate byte/bit part of prefix */
+   byte_prefix = src->_prefix_len / 8;
+   bit_prefix = src->_prefix_len & 7;
+   if (src != dst) {
+     /* copy type and prefix length */
+     dst->_prefix_len = src->_prefix_len;
+     dst->_type = src->_type;
+     /* copy constant octets */
+     memcpy(dst->_addr, src->_addr, byte_prefix);
+   }
+   if (bit_prefix) {
+     /* modify bitwise */
+     dst->_addr[byte_prefix] = src->_addr[byte_prefix] & MASK[bit_prefix];
+     byte_prefix++;
+   }
+   if (byte_prefix < 16) {
+     /* zero rest of address */
+     memset(&dst->_addr[byte_prefix], 0, 16 - byte_prefix);
+   }
+ }
+ /**
+  * Initialize a netaddr_socket with a netaddr and a port number
+  * @param combined pointer to netaddr_socket to be initialized
+  * @param addr pointer to netaddr source
+  * @param port port number for socket
+  * @param if_index interface index for linklocal ipv6 sockets
+  * @return 0 if successful read binary data, -1 otherwise
+  */
+ int
+ netaddr_socket_init(union netaddr_socket *combined, const struct netaddr *addr, uint16_t port, unsigned if_index) {
+   /* initialize memory block */
+   memset(combined, 0, sizeof(*combined));
+   switch (addr->_type) {
+     case AF_INET:
+       /* ipv4 */
+       memcpy(&combined->v4.sin_addr, addr->_addr, 4);
+       combined->v4.sin_port = htons(port);
+       break;
+     case AF_INET6:
+       /* ipv6 */
+       memcpy(&combined->v6.sin6_addr, addr->_addr, 16);
+       combined->v6.sin6_port = htons(port);
+       combined->v6.sin6_scope_id = if_index;
+       break;
+     default:
+       /* unknown address type */
+       return -1;
+   }
+   /* copy address type */
+   combined->std.sa_family = addr->_type;
+   return 0;
+ }
+ /**
+  * @param sock pointer to netaddr_socket
+  * @return port of socket
+  */
+ uint16_t
+ netaddr_socket_get_port(const union netaddr_socket *sock) {
+   switch (sock->std.sa_family) {
+     case AF_INET:
+       return ntohs(sock->v4.sin_port);
+     case AF_INET6:
+       return ntohs(sock->v6.sin6_port);
+     default:
+       return 0;
+   }
+ }
+ /**
+  * Converts a netaddr into a string
+  * @param dst target string buffer
++ * @param src netaddr source (can be NULL)
+  * @param forceprefix true if a prefix should be appended even with maximum
+  *   prefix length, false if only shorter prefixes should be appended
+  * @return pointer to target buffer
+  */
+ const char *
+ netaddr_to_prefixstring(struct netaddr_str *dst, const struct netaddr *src, bool forceprefix) {
+   static const char *NONE = "-";
+   const char *result = NULL;
+   int maxprefix;
+   if (!src) {
+     return strscpy(dst->buf, NONE, sizeof(*dst));
+   }
+   maxprefix = netaddr_get_maxprefix(src);
+   switch (src->_type) {
+     case AF_INET:
+       result = inet_ntop(AF_INET, src->_addr, dst->buf, sizeof(*dst));
+       break;
+     case AF_INET6:
+       result = inet_ntop(AF_INET6, src->_addr, dst->buf, sizeof(*dst));
+       break;
+     case AF_MAC48:
+       result = _mac_to_string(dst->buf, sizeof(*dst), src->_addr, 6, ':');
+       break;
+     case AF_EUI64:
+       result = _mac_to_string(dst->buf, sizeof(*dst), src->_addr, 8, '-');
+       break;
+     case AF_UUID:
+       result = _uuid_to_string(dst->buf, sizeof(*dst), src->_addr, 16);
+       break;
+     case AF_UNSPEC:
+       /* fall through */
+     default:
+       return strscpy(dst->buf, NONE, sizeof(*dst));
+   }
+   if (forceprefix || src->_prefix_len < maxprefix) {
+     /* append prefix */
+     snprintf(dst->buf + strlen(result), 5, "/%d", src->_prefix_len);
+   }
+   return result;
+ }
+ /**
+  * Generates a netaddr from a string.
+  * @param dst pointer to netaddr object
+  * @param src pointer to input string
+  * @return -1 if an error happened because of an unknown string,
+  *   0 otherwise
+  */
+ int
+ netaddr_from_string(struct netaddr *dst, const char *src) {
+   struct netaddr_str buf;
+   unsigned int colon_count, minus_count;
+   int result;
+   int prefix_len;
+   bool has_coloncolon, has_point;
+   bool last_was_colon;
+   char *ptr1, *ptr2, *ptr3;
+   size_t i;
+   memset(dst, 0, sizeof(*dst));
+   if (strcmp(src, "-") == 0) {
+     /* unspec */
+     netaddr_invalidate(dst);
+     return 0;
+   }
+   /* handle string representations of prefixes */
+   for (i = 0; i < ARRAYSIZE(_known_prefixes); i++) {
+     if (strcasecmp(src, _known_prefixes[i].name) == 0) {
+       memcpy(dst, _known_prefixes[i].prefix, sizeof(*dst));
+       return 0;
+     }
+   }
+   colon_count = 0;
+   minus_count = 0;
+   has_coloncolon = false;
+   has_point = false;
+   last_was_colon = false;
+   result = -1;
+   prefix_len = -1;
+   /* copy input string in temporary buffer */
+   strscpy(buf.buf, src, sizeof(buf));
+   ptr1 = buf.buf;
+   ptr1 = str_trim(ptr1);
+   ptr2 = ptr1;
+   while (*ptr2 != 0 && !isspace(*ptr2) && *ptr2 != '/') {
+     switch (*ptr2) {
+       case ':':
+         if (last_was_colon) {
+           has_coloncolon = true;
+         }
+         colon_count++;
+         break;
+       case '.':
+         has_point = true;
+         break;
+       case '-':
+         minus_count++;
+         break;
+       default:
+         break;
+     }
+     last_was_colon = *ptr2++ == ':';
+   }
+   if (*ptr2) {
+     /* split strings */
+     while (isspace(*ptr2))
+       *ptr2++ = 0;
+     if (*ptr2 == '/') {
+       *ptr2++ = 0;
+     }
+     while (isspace(*ptr2))
+       *ptr2++ = 0;
+     if (*ptr2 == 0) {
+       /* prefixlength is missing */
+       dst->_type = AF_UNSPEC;
+       return -1;
+     }
+     /* try to read numeric prefix length */
+     prefix_len = (int)strtoul(ptr2, &ptr3, 10);
+     if (ptr3 && *ptr3) {
+       /* not a numeric prefix length */
+       prefix_len = -1;
+     }
+   }
+   /* use dst->prefix_len as storage for maximum prefixlen */
+   if ((colon_count == 5 || minus_count == 5) && (colon_count == 0 || minus_count == 0) && !has_point &&
+       !has_coloncolon) {
+     dst->_type = AF_MAC48;
+     dst->_prefix_len = 48;
+     if (colon_count > 0) {
+       result = _bin_from_hex(dst->_addr, 6, ptr1, ':');
+     }
+     else {
+       result = _bin_from_hex(dst->_addr, 6, ptr1, '-');
+     }
+   }
+   else if (colon_count == 0 && !has_point && minus_count == 7) {
+     dst->_type = AF_EUI64;
+     dst->_prefix_len = 64;
+     dst->_addr[7] = 2;
+     result = _bin_from_hex(dst->_addr, 8, ptr1, '-');
+   }
+   else if (colon_count == 0 && has_point && minus_count == 0) {
+     dst->_type = AF_INET;
+     dst->_prefix_len = 32;
+     result = inet_pton(AF_INET, ptr1, dst->_addr) == 1 ? 0 : -1;
+     if (result == 0 && *ptr2 && prefix_len == -1) {
+       /* we need a prefix length, but its not a numerical one */
+       prefix_len = _subnetmask_to_prefixlen(ptr2);
+     }
+   }
+   else if ((has_coloncolon || colon_count == 7) && minus_count == 0) {
+     dst->_type = AF_INET6;
+     dst->_prefix_len = 128;
+     result = inet_pton(AF_INET6, ptr1, dst->_addr) == 1 ? 0 : -1;
+   }
+   else if (minus_count == 4 && colon_count == 0 && !has_point) {
+     dst->_type = AF_UUID;
+     dst->_prefix_len = 128;
+     result = _uuid_from_string(dst->_addr, sizeof(dst->_addr), ptr1);
+   }
+   /* stop if an error happened */
+   if (result) {
+     dst->_type = AF_UNSPEC;
+     return -1;
+   }
+   if (*ptr2) {
+     if (prefix_len < 0 || prefix_len > dst->_prefix_len) {
+       /* prefix is too long */
+       dst->_type = AF_UNSPEC;
+       return -1;
+     }
+     /* store real prefix length */
+     dst->_prefix_len = (uint8_t)prefix_len;
+   }
+   return 0;
+ }
+ /**
+  * Converts a netaddr_socket into a string
+  * @param dst target string buffer
+  * @param src netaddr_socket source
+  * @return pointer to target buffer, NULL if an error happened
+  */
+ const char *
+ netaddr_socket_to_string(struct netaddr_str *dst, const union netaddr_socket *src) {
+   struct netaddr_str buf;
+   if (src->std.sa_family == AF_INET) {
+     snprintf(dst->buf, sizeof(*dst), "%s:%d", inet_ntop(AF_INET, &src->v4.sin_addr, buf.buf, sizeof(buf)),
+       ntohs(src->v4.sin_port));
+   }
+   else if (src->std.sa_family == AF_INET6) {
+     if (src->v6.sin6_scope_id) {
+       char scope_buf[IF_NAMESIZE];
+       snprintf(dst->buf, sizeof(*dst), "[%s]:%d%%%s", inet_ntop(AF_INET6, &src->v6.sin6_addr, buf.buf, sizeof(buf)),
+         ntohs(src->v6.sin6_port), if_indextoname(src->v6.sin6_scope_id, scope_buf));
+     }
+     else {
+       snprintf(dst->buf, sizeof(*dst), "[%s]:%d", inet_ntop(AF_INET6, &src->v6.sin6_addr, buf.buf, sizeof(buf)),
+         ntohs(src->v6.sin6_port));
+     }
+   }
+   else {
+     snprintf(dst->buf, sizeof(*dst), "\"Unknown socket type: %d\"", src->std.sa_family);
+   }
+   return dst->buf;
+ }
+ /**
+  * Compares two addresses in network byte order.
+  * Address type will be compared last.
+  *
+  * This function is compatible with the avl comparator
+  * prototype.
+  * @param k1 address 1
+  * @param k2 address 2
+  * @return >0 if k1>k2, <0 if k1<k2, 0 otherwise
+  */
+ int
+ netaddr_avlcmp(const void *k1, const void *k2) {
+   return netaddr_cmp(k1, k2);
+ }
+ /**
+  * Compares two netaddr sockets.
+  *
+  * This function is compatible with the avl comparator
+  * prototype.
+  * @param k1 address 1
+  * @param k2 address 2
+  * @return >0 if k1>k2, <0 if k1<k2, 0 otherwise
+  */
+ int
+ netaddr_socket_avlcmp(const void *k1, const void *k2) {
+   return netaddr_socket_cmp(k1, k2);
+ }
+ /**
+  * Compares an netaddr object with the address part of
+  * a netaddr_socket.
+  * @param a1 address
+  * @param a2 socket
+  * @return >0 if k1>k2, <0 if k1<k2, 0 otherwise
+  */
+ int
+ netaddr_cmp_to_socket(const struct netaddr *a1, const union netaddr_socket *a2) {
+   int result = 0;
+   result = (int)a1->_type - (int)a2->std.sa_family;
+   if (result) {
+     return result;
+   }
+   if (a1->_type == AF_INET) {
+     result = memcmp(a1->_addr, &a2->v4.sin_addr, 4);
+   }
+   else if (a1->_type == AF_INET6) {
+     /* ipv6 */
+     result = memcmp(a1->_addr, &a2->v6.sin6_addr, 16);
+   }
+   if (result) {
+     return result;
+   }
+   return (int)a1->_prefix_len - (a1->_type == AF_INET ? 32 : 128);
+ }
+ /**
+  * Calculates if a binary address is equals to a netaddr one.
+  * @param addr netaddr pointer
+  * @param bin pointer to binary address
+  * @param len length of binary address
+  * @param af family of binary address
+  * @param prefix_len prefix length of binary address
+  * @return true if matches, false otherwise
+  */
+ bool
+ netaddr_isequal_binary(const struct netaddr *addr, const void *bin, size_t len, uint16_t af, uint8_t prefix_len) {
+   uint32_t addr_len;
+   if (addr->_type != af || addr->_prefix_len != prefix_len) {
+     return false;
+   }
+   addr_len = netaddr_get_maxprefix(addr) >> 3;
+   if (addr_len != len) {
+     return false;
+   }
+   return memcmp(addr->_addr, bin, addr_len) == 0;
+ }
+ /**
+  * Checks if a binary address is part of a netaddr prefix.
+  * @param subnet netaddr prefix
+  * @param bin pointer to binary address
+  * @param len length of binary address
+  * @param af_family address family of binary address
+  * @return true if part of the prefix, false otherwise
+  */
+ bool
+ netaddr_binary_is_in_subnet(const struct netaddr *subnet, const void *bin, size_t len, uint8_t af_family) {
+   if (subnet->_type != af_family || netaddr_get_maxprefix(subnet) != len * 8) {
+     return false;
+   }
+   return _binary_is_in_subnet(subnet, bin);
+ }
+ /**
+  * Checks if a netaddr object is part of another netaddr prefix.
+  * The prefix length of addr is ignored for this comparison.
+  * @param subnet netaddr prefix
+  * @param addr netaddr object that might be inside the prefix
+  * @return true if addr is part of subnet, false otherwise
+  */
+ bool
+ netaddr_is_in_subnet(const struct netaddr *subnet, const struct netaddr *addr) {
+   if (subnet->_type != addr->_type) {
+     return false;
+   }
+   return _binary_is_in_subnet(subnet, addr->_addr);
+ }
+ /**
+  * Calculates the maximum prefix length of an address type
+  * @param af_type address type
+  * @return prefix length, 0 if unknown address family
+  */
+ uint8_t
+ netaddr_get_af_maxprefix(uint32_t af_type) {
+   switch (af_type) {
+     case AF_INET:
+       return 32;
+       break;
+     case AF_INET6:
+       return 128;
+     case AF_MAC48:
+       return 48;
+       break;
+     case AF_EUI64:
+       return 64;
+       break;
+     default:
+       return 0;
+   }
+ }
+ #ifdef WIN32
+ /**
+  * Helper function for windows
+  * @param dst
+  * @param bin
+  * @param dst_size
+  * @param bin_size
+  * @param separator
+  * @return
+  */
+ const char *
+ inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+   if (af == AF_INET) {
+     struct sockaddr_in in;
+     memset(&in, 0, sizeof(in));
+     in.sin_family = AF_INET;
+     memcpy(&in.sin_addr, src, sizeof(struct in_addr));
+     getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
+     return dst;
+   }
+   else if (af == AF_INET6) {
+     struct sockaddr_in6 in;
+     memset(&in, 0, sizeof(in));
+     in.sin6_family = AF_INET6;
+     memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
+     getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
+     return dst;
+   }
+   return NULL;
+ }
+ /**
+  * Helper function for windows
+  * @param dst
+  * @param bin
+  * @param dst_size
+  * @param bin_size
+  * @param separator
+  * @return
+  */
+ int
+ inet_pton(int af, const char *src, void *dst) {
+   struct addrinfo hints, *res;
+   union netaddr_socket *sock;
+   if (af != AF_INET && af != AF_INET6) {
+     return -1;
+   }
+   memset(&hints, 0, sizeof(struct addrinfo));
+   hints.ai_family = af;
+   hints.ai_flags = AI_NUMERICHOST;
+   if (getaddrinfo(src, NULL, &hints, &res) != 0) {
+     return -1;
+   }
+   if (res == NULL) {
+     return 0;
+   }
+   sock = (union netaddr_socket *)res->ai_addr;
+   if (af == AF_INET) {
+     memcpy(dst, &sock->v4.sin_addr, 4);
+   }
+   else {
+     memcpy(dst, &sock->v6.sin6_addr, 16);
+   }
+   freeaddrinfo(res);
+   return 1;
+ }
+ #endif
+ /**
+  * Converts a binary mac address into a string representation
+  * @param dst pointer to target string buffer
+  * @param dst_size size of string buffer
+  * @param bin pointer to binary source buffer
+  * @param bin_size size of binary buffer
+  * @param separator character for separating hexadecimal octets,
+  *     0 for no separator
+  * @return pointer to target buffer, NULL if an error happened
+  */
+ static char *
+ _mac_to_string(char *dst, size_t dst_size, const void *bin, size_t bin_size, char separator) {
+   static const char hex[] = "0123456789abcdef";
+   char *last_separator, *_dst;
+   const uint8_t *_bin;
+   _bin = bin;
+   _dst = dst;
+   last_separator = dst;
+   if (dst_size == 0) {
+     return NULL;
+   }
+   while (bin_size > 0 && dst_size >= 3) {
+     *_dst++ = hex[(*_bin) >> 4];
+     *_dst++ = hex[(*_bin) & 15];
+     /* copy pointer to separator */
+     last_separator = _dst;
+     /* write separator */
+     if (separator) {
+       *_dst++ = separator;
+       dst_size--;
+     }
+     /* advance source pointer and decrease remaining length of buffer*/
+     _bin++;
+     bin_size--;
+     /* calculate remaining destination size */
+     dst_size -= 2;
+   }
+   *last_separator = 0;
+   return dst;
+ }
+ /**
+  * Convert a binary UUID into its string representation
+  * @param dst pointer to target string buffer
+  * @param dst_size size of string buffer
+  * @param src pointer to binary source buffer
+  * @param src_size size of binary buffer
+  * @return pointer to target buffer, NULL if an error happened
+  */
+ static char *
+ _uuid_to_string(char *dst, size_t dst_size, const void *src, size_t src_size) {
+   static const size_t max_group_len[5] = { 8, 4, 4, 4, 12 };
+   const char *_src;
+   char *_dst;
+   size_t i;
+   if (src_size != 16) {
+     return NULL;
+   }
+   if (dst_size < 32 + 4 + 1) {
+     return NULL;
+   }
+   _src = src;
+   _dst = dst;
+   for (i = 0; i < ARRAYSIZE(max_group_len); i++) {
+     if (!_mac_to_string(_dst, dst_size, _src, max_group_len[i] / 2, 0)) {
+       return NULL;
+     }
+     _src += (max_group_len[i] / 2);
+     _dst += (max_group_len[i]);
+     dst_size -= max_group_len[i];
+     if (i < ARRAYSIZE(max_group_len) - 1) {
+       *_dst++ = '-';
+       dst_size--;
+     }
+   }
+   return dst;
+ }
+ /**
+  * Convert a hex string (with optional separators) into a binary representation
+  * @param bin pointer to target binary buffer
+  * @param bin_size pointer to size of target buffer
+  * @param src pointer to source string
+  * @param separator character used to separate octets in source string,
+  *     0 for no separator
+  * @return number of bytes left in target buffer, -1 if an error happened
+  */
+ static int
+ _bin_from_hex(void *bin, size_t bin_size, const char *src, char separator) {
+   uint8_t *_bin;
+   int num, digit_2;
+   _bin = bin;
+   while (bin_size > 0) {
+     num = _read_hexdigit(*src++);
+     if (num == -1) {
+       return -1;
+     }
+     digit_2 = _read_hexdigit(*src);
+     if (digit_2 >= 0) {
+       num = (num << 4) + digit_2;
+       src++;
+     }
+     *_bin++ = (uint8_t)num;
+     bin_size--;
+     if (*src == 0) {
+       return bin_size;
+     }
+     if (separator) {
+       if (*src++ != separator) {
+         return -1;
+       }
+     }
+   }
+   return -1;
+ }
+ /**
+  * Convert a string UUID into the binary (16 byte) representation
+  * @param bin target buffer
+  * @param bin_size size of target buffer
+  * @param src UUID string
+  * @return -1 if an error happened, 0 otherwise
+  */
+ static int
+ _uuid_from_string(void *bin, size_t bin_size, const char *src) {
+   static const size_t max_group_len[5] = { 8, 4, 4, 4, 12 };
+   char buffer[32 + 4 + 1];
+   char *current_group, *next_group, *_bin;
+   size_t i;
+   if (bin_size < 16) {
+     return -1;
+   }
+   if (strlen(src) > 32 + 4) {
+     return -1;
+   }
+   strscpy(buffer, src, sizeof(buffer));
+   _bin = bin;
+   current_group = buffer;
+   for (i = 0; i < ARRAYSIZE(max_group_len); i++) {
+     next_group = strchr(current_group, '-');
+     if (next_group) {
+       /* zero terminate current group */
+       *next_group++ = 0;
+     }
+     else if (i != ARRAYSIZE(max_group_len) - 1) {
+       /* not enough components */
+       return -1;
+     }
+     /* check length */
+     if (strlen(current_group) != max_group_len[i]) {
+       return -1;
+     }
+     /* parse data, we expect a precise number of hex-numbers */
+     if (_bin_from_hex(_bin, max_group_len[i] / 2, current_group, 0)) {
+       return -1;
+     }
+     current_group = next_group;
+     _bin = _bin + max_group_len[i] / 2;
+   }
+   return 0;
+ }
+ /**
+  * Reads a single hexadecimal digit
+  * @param c digit to be read
+  * @return integer value (0-15) of digit,
+  *   -1 if not a hexadecimal digit
+  */
+ static int
+ _read_hexdigit(const char c) {
+   if (c >= '0' && c <= '9') {
+     return c - '0';
+   }
+   if (c >= 'a' && c <= 'f') {
+     return c - 'a' + 10;
+   }
+   if (c >= 'A' && c <= 'F') {
+     return c - 'A' + 10;
+   }
+   return -1;
+ }
+ /**
+  * Converts a ipv4 subnet mask into a prefix length.
+  * @param src string representation of subnet mask
+  * @return prefix length, -1 if source was not a wellformed
+  *   subnet mask
+  */
+ static int
+ _subnetmask_to_prefixlen(const char *src) {
+   uint32_t v4, shift;
+   int len;
+   if (inet_pton(AF_INET, src, &v4) != 1) {
+     return -1;
+   }
+   /* transform into host byte order */
+   v4 = ntohl(v4);
+   shift = 0xffffffff;
+   for (len = 31; len >= 0; len--) {
+     if (v4 == shift) {
+       return len;
+     }
+     shift <<= 1;
+   }
+   /* not wellformed */
+   return -1;
+ }
+ /**
+  * Calculates if a binary address is part of a netaddr prefix.
+  * It will assume that the length of the binary address and its
+  * address family makes sense.
+  * @param subnet netaddr prefix
+  * @param bin pointer to binary address
+  * @return true if part of the prefix, false otherwise
+  */
+ static bool
+ _binary_is_in_subnet(const struct netaddr *subnet, const void *bin) {
+   size_t byte_length, bit_length;
+   const uint8_t *_bin;
+   _bin = bin;
+   /* split prefix length into whole bytes and bit rest */
+   byte_length = subnet->_prefix_len / 8;
+   bit_length = subnet->_prefix_len % 8;
+   /* compare whole bytes */
+   if (memcmp(subnet->_addr, bin, byte_length) != 0) {
+     return false;
+   }
+   /* compare bits if necessary */
+   if (bit_length != 0) {
+     return (subnet->_addr[byte_length] >> (8 - bit_length)) == (_bin[byte_length] >> (8 - bit_length));
+   }
+   return true;
+ }
index 0000000,3cc3e12..7e06d5d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1332 +1,1349 @@@
+ /*
+  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
+  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * * Redistributions of source code must retain the above copyright
+  *   notice, this list of conditions and the following disclaimer.
+  * * Redistributions in binary form must reproduce the above copyright
+  *   notice, this list of conditions and the following disclaimer in
+  *   the documentation and/or other materials provided with the
+  *   distribution.
+  * * Neither the name of olsr.org, olsrd nor the names of its
+  *   contributors may be used to endorse or promote products derived
+  *   from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * Visit http://www.olsr.org for more information.
+  *
+  * If you find this software useful feel free to make a donation
+  * to the project. For more information see the website or contact
+  * the copyright holders.
+  *
+  */
+ /**
+  * @file
+  */
+ #include <stdio.h>
+ #include <oonf/libcommon/avl.h>
+ #include <oonf/libcommon/avl_comp.h>
+ #include <oonf/oonf.h>
+ #include <oonf/libcommon/list.h>
+ #include <oonf/libcommon/netaddr.h>
+ #include <oonf/libcore/oonf_cfg.h>
+ #include <oonf/libcore/oonf_logging.h>
+ #include <oonf/base/oonf_class.h>
+ #include <oonf/base/oonf_rfc5444.h>
+ #include <oonf/nhdp/nhdp/nhdp.h>
+ #include <oonf/nhdp/nhdp/nhdp_db.h>
+ #include <oonf/nhdp/nhdp/nhdp_domain.h>
+ #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
+ #include <oonf/nhdp/nhdp/nhdp_internal.h>
+ static void _apply_metric(struct nhdp_domain *domain, const char *metric_name);
+ static void _remove_metric(struct nhdp_domain *);
+ static void _apply_mpr(struct nhdp_domain *domain, const char *mpr_name, uint8_t willingness);
+ static void _remove_mpr(struct nhdp_domain *);
+ static void _cb_update_everyone_routing_mpr(struct nhdp_domain *domain);
+ static void _cb_update_everyone_flooding_mpr(struct nhdp_domain *domain);
+ static bool _recalculate_neighbor_metric(struct nhdp_domain *domain, struct nhdp_neighbor *neigh);
+ static bool _recalculate_routing_mpr_set(struct nhdp_domain *domain);
+ static bool _recalculate_flooding_mpr_set(void);
+ static const char *_link_to_string(struct nhdp_metric_str *, uint32_t);
+ static const char *_path_to_string(struct nhdp_metric_str *, uint32_t, uint8_t);
+ static const char *_int_to_string(struct nhdp_metric_str *, struct nhdp_link *);
+ /* domain class */
+ static struct oonf_class _domain_class = {
+   .name = NHDP_CLASS_DOMAIN,
+   .size = sizeof(struct nhdp_domain),
+ };
+ /* default metric handler (hopcount) */
+ static struct nhdp_domain_metric _no_metric = {
+   .name = "Hopcount metric",
+   .incoming_link_start = RFC7181_METRIC_MAX,
+   .outgoing_link_start = RFC7181_METRIC_MAX,
+   .incoming_2hop_start = RFC7181_METRIC_MAX,
+   .outgoing_2hop_start = RFC7181_METRIC_MAX,
+   .link_to_string = _link_to_string,
+   .path_to_string = _path_to_string,
+   .internal_link_to_string = _int_to_string,
+   .no_default_handling = true,
+ };
+ /* default MPR handler (no MPR handling) */
+ static struct nhdp_domain_mpr _everyone_mprs = {
+   .name = "Everyone MPR",
+   .update_flooding_mpr = _cb_update_everyone_flooding_mpr,
+   .update_routing_mpr = _cb_update_everyone_routing_mpr,
+ };
+ /* non-default routing domains registered to NHDP */
+ static struct list_entity _domain_list;
+ static struct list_entity _domain_listener_list;
+ static struct list_entity _domain_metric_postprocessor_list;
+ static size_t _domain_counter = 0;
+ /* tree of known routing metrics/mpr-algorithms */
+ static struct avl_tree _domain_metrics;
+ static struct avl_tree _domain_mprs;
+ /* flooding domain */
+ static struct nhdp_domain _flooding_domain;
+ /* NHDP RFC5444 protocol */
+ static struct oonf_rfc5444_protocol *_protocol;
+ /* remember if node is MPR or not */
+ static bool _node_is_selected_as_mpr = false;
+ /**
+  * Initialize nhdp metric core
+  * @param p pointer to rfc5444 protocol
+  */
+ void
+ nhdp_domain_init(struct oonf_rfc5444_protocol *p) {
+   _protocol = p;
+   oonf_class_add(&_domain_class);
+   list_init_head(&_domain_list);
+   list_init_head(&_domain_listener_list);
+   list_init_head(&_domain_metric_postprocessor_list);
+   avl_init(&_domain_metrics, avl_comp_strcasecmp, false);
+   avl_init(&_domain_mprs, avl_comp_strcasecmp, false);
+   /* initialize flooding domain */
+   _flooding_domain.metric = &_no_metric;
+   _flooding_domain.mpr = &_everyone_mprs;
+   _flooding_domain.mpr->_refcount++;
+   _flooding_domain.metric->_refcount++;
+ }
+ /**
+  * cleanup allocated resources for nhdp metric core
+  */
+ void
+ nhdp_domain_cleanup(void) {
+   struct nhdp_domain *domain, *d_it;
+   struct nhdp_domain_listener *listener, *l_it;
+   struct nhdp_domain_metric_postprocessor *processor, *p_it;
+   int i;
+   list_for_each_element_safe(&_domain_list, domain, _node, d_it) {
+     /* free allocated TLVs */
+     for (i = 0; i < 4; i++) {
+       rfc5444_writer_unregister_addrtlvtype(&_protocol->writer, &domain->_metric_addrtlvs[i]);
+     }
+     /* remove domain */
+     list_remove(&domain->_node);
+     oonf_class_free(&_domain_class, domain);
+   }
+   list_for_each_element_safe(&_domain_metric_postprocessor_list, processor, _node, p_it) {
+     nhdp_domain_metric_postprocessor_remove(processor);
+   }
+   list_for_each_element_safe(&_domain_listener_list, listener, _node, l_it) {
+     nhdp_domain_listener_remove(listener);
+   }
+   oonf_class_remove(&_domain_class);
+ }
+ /**
+  * @return number of registered nhdp domains
+  */
+ size_t
+ nhdp_domain_get_count(void) {
+   return _domain_counter;
+ }
+ /**
+  * Add a new metric handler to nhdp
+  * @param metric pointer to NHDP link metric
+  * @return 0 if successful, -1 if metric was already registered
+  */
+ int
+ nhdp_domain_metric_add(struct nhdp_domain_metric *metric) {
+   /* initialize key */
+   metric->_node.key = metric->name;
+   /* insert default values if not set */
+   if (metric->incoming_link_start == 0) {
+     metric->incoming_link_start = RFC7181_METRIC_MAX;
+   }
+   if (metric->outgoing_link_start == 0) {
+     metric->outgoing_link_start = RFC7181_METRIC_INFINITE;
+   }
+   if (metric->incoming_2hop_start == 0) {
+     metric->incoming_2hop_start = RFC7181_METRIC_INFINITE;
+   }
+   if (metric->outgoing_2hop_start == 0) {
+     metric->outgoing_2hop_start = RFC7181_METRIC_INFINITE;
+   }
+   /* initialize to_string method if empty */
+   if (metric->link_to_string == NULL) {
+     metric->link_to_string = _link_to_string;
+   }
+   if (metric->path_to_string == NULL) {
+     metric->path_to_string = _path_to_string;
+   }
+   if (metric->internal_link_to_string == NULL) {
+     metric->internal_link_to_string = _int_to_string;
+   }
+   /* hook into tree */
+   return avl_insert(&_domain_metrics, &metric->_node);
+ }
+ /**
+  * Remove a metric handler from the nhdp metric core
+  * @param metric pointer to metric handler
+  */
+ void
+ nhdp_domain_metric_remove(struct nhdp_domain_metric *metric) {
+   struct nhdp_domain *domain;
+   list_for_each_element(&_domain_list, domain, _node) {
+     if (domain->metric == metric) {
+       _remove_metric(domain);
+       break;
+     }
+   }
+   avl_remove(&_domain_metrics, &metric->_node);
+ }
+ /**
+  * Add a new mpr handler to nhdp
+  * @param mpr pointer to mpr handler
+  * @return 0 if successful, -1 if metric is already registered
+  */
+ int
+ nhdp_domain_mpr_add(struct nhdp_domain_mpr *mpr) {
+   struct nhdp_domain *domain;
+   /* initialize key */
+   mpr->_node.key = mpr->name;
+   if (avl_insert(&_domain_mprs, &mpr->_node)) {
+     return -1;
+   }
+   list_for_each_element(&_domain_list, domain, _node) {
+     if (domain->mpr == &_everyone_mprs) {
+       _apply_mpr(domain, domain->mpr_name, domain->local_willingness);
+     }
+   }
+   if (_flooding_domain.mpr == &_everyone_mprs) {
+     _apply_mpr(&_flooding_domain, _flooding_domain.mpr_name, _flooding_domain.local_willingness);
+   }
+   return 0;
+ }
+ /**
+  * Remove a metric handler from the nhdp metric core
+  * @param mpr pointer to mpr handler
+  */
+ void
+ nhdp_domain_mpr_remove(struct nhdp_domain_mpr *mpr) {
+   struct nhdp_domain *domain;
+   list_for_each_element(&_domain_list, domain, _node) {
+     if (domain->mpr == mpr) {
+       _remove_mpr(domain);
+       break;
+     }
+   }
+   avl_remove(&_domain_mprs, &mpr->_node);
+ }
+ /**
+  * Adds a listener to the NHDP domain system
+  * @param listener pointer to NHDP domain listener
+  */
+ void
+ nhdp_domain_listener_add(struct nhdp_domain_listener *listener) {
+   list_add_tail(&_domain_listener_list, &listener->_node);
+ }
+ /**
+  * Removes a listener from the NHDP domain system
+  * @param listener pointer to NHDP domain listener
+  */
+ void
+ nhdp_domain_listener_remove(struct nhdp_domain_listener *listener) {
+   if (list_is_node_added(&listener->_node)) {
+     list_remove(&listener->_node);
+   }
+ }
+ void
+ nhdp_domain_metric_postprocessor_add(struct nhdp_domain_metric_postprocessor *processor) {
+   list_add_tail(&_domain_metric_postprocessor_list, &processor->_node);
+ }
+ void
+ nhdp_domain_metric_postprocessor_remove(struct nhdp_domain_metric_postprocessor *processor) {
+   if (list_is_node_added(&processor->_node)) {
+     list_remove(&processor->_node);
+   }
+ }
+ /**
+  * @param ext TLV extension value of MPR/Linkmetrics
+  * @return NHDP domain registered to this extension, NULL if not found
+  */
+ struct nhdp_domain *
+ nhdp_domain_get_by_ext(uint8_t ext) {
+   struct nhdp_domain *d;
+   list_for_each_element(&_domain_list, d, _node) {
+     if (d->ext == ext) {
+       return d;
+     }
+   }
+   return NULL;
+ }
+ /**
+  * Initialize the domain data of a new NHDP link
+  * @param lnk NHDP link
+  */
+ void
+ nhdp_domain_init_link(struct nhdp_link *lnk) {
+   struct nhdp_domain *domain;
+   struct nhdp_link_domaindata *data;
+   int i;
+   /* initialize flooding MPR settings */
+   lnk->flooding_willingness = RFC7181_WILLINGNESS_NEVER;
+   lnk->local_is_flooding_mpr = false;
+   lnk->neigh_is_flooding_mpr = false;
+   /* initialize metrics */
+   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
+     lnk->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
+     lnk->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
+     lnk->_domaindata[i].last_metric_change = oonf_clock_getNow();
+   }
+   list_for_each_element(&_domain_list, domain, _node) {
+     data = nhdp_domain_get_linkdata(domain, lnk);
+     if (domain->metric->no_default_handling) {
+       data->metric.in = domain->metric->incoming_link_start;
+       data->metric.out = domain->metric->outgoing_link_start;
+     }
+   }
+ }
+ /**
+  * Initialize the domain data of a new NHDP twohop neighbor
+  * @param l2hop NHDP twohop neighbor
+  */
+ void
+ nhdp_domain_init_l2hop(struct nhdp_l2hop *l2hop) {
+   struct nhdp_domain *domain;
+   struct nhdp_l2hop_domaindata *data;
+   int i;
+   /* initialize metrics */
+   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
+     l2hop->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
+     l2hop->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
+   }
+   list_for_each_element(&_domain_list, domain, _node) {
+     data = nhdp_domain_get_l2hopdata(domain, l2hop);
+     if (domain->metric->no_default_handling) {
+       data->metric.in = domain->metric->incoming_2hop_start;
+       data->metric.out = domain->metric->outgoing_2hop_start;
+     }
+   }
+ }
+ /**
+  * Initialize the domain data of a new NHDP neighbor
+  * @param neigh NHDP neighbor
+  */
+ void
+ nhdp_domain_init_neighbor(struct nhdp_neighbor *neigh) {
+   struct nhdp_domain *domain;
+   struct nhdp_neighbor_domaindata *data;
+   int i;
+   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
+     neigh->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
+     neigh->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
+     neigh->_domaindata[i].best_out_link = NULL;
+     neigh->_domaindata[i].best_out_link_metric = RFC7181_METRIC_INFINITE;
+  &nbs